Share
Explore

Lab 2.4: Code Walkthrough - Data Exchange Architecture Between Screens

Executive Summary

This lab completes our multi-screen application by implementing bidirectional data flow between Activities.
We demonstrate how to pass complex state information through Intent extras, enabling SecondActivity to recreate the exact visual state selected in MainActivity. This establishes patterns for data persistence across screen transitions.

Data Flow Architecture Overview

┌─────────────────────────┐ ┌─────────────────────────┐
│ MainActivity │ │ SecondActivity │
│ │ │ │
│ State Variables: │ Intent with │ Receives & Applies: │
│ • currentAnimal │ Extras Bundle │ • Animal selection │
│ • isGrayscale │ ==================> │ • Effect states │
│ • isRotated │ │ • Image resource ID │
│ │ │ │
│ [Navigate Button] ─────┼────────────────────┼──> Recreates exact │
│ │ │ visual state │
└─────────────────────────┘ └─────────────────────────┘

Detailed Data Exchange Flow

1. Preparing Data for Transmission

When the user clicks navigate, we package the current state:
private fun setupNavigationButton() {
navigateButton.setOnClickListener {
// 1. CREATE INTENT: Container for our data
val intent = Intent(this, SecondActivity::class.java)
// 2. PACKAGE STATE: Add current values as extras
intent.putExtra("SELECTED_ANIMAL", currentAnimal)
intent.putExtra("IS_GRAYSCALE", isGrayscale)
intent.putExtra("IS_ROTATED", isRotated)
// 3. RESOLVE RESOURCE: Determine which image to send
val imageResourceId = when (currentAnimal) {
"Cat" -> R.drawable.cat_image
"Dog" -> R.drawable.dog_image
"Bird" -> R.drawable.bird_image
else -> R.drawable.cat_image
}
intent.putExtra("IMAGE_RESOURCE_ID", imageResourceId)
// 4. LAUNCH WITH DATA: Send everything to SecondActivity
startActivity(intent)
}
}

Data packaging visualization:
Intent Bundle {
"SELECTED_ANIMAL": "Dog" (String)
"IS_GRAYSCALE": true (Boolean)
"IS_ROTATED": false (Boolean)
"IMAGE_RESOURCE_ID": 2131165259 (Int - R.drawable.dog_image)
}

2. Intent Extras - The Data Protocol

Let's examine each data element:
// String Extra - Animal name
intent.putExtra("SELECTED_ANIMAL", currentAnimal)
// Key: Unique identifier for retrieval
// Value: Current animal selection from radio buttons

// Boolean Extras - Effect states
intent.putExtra("IS_GRAYSCALE", isGrayscale)
intent.putExtra("IS_ROTATED", isRotated)
// Preserves checkbox states

// Int Extra - Resource identifier
intent.putExtra("IMAGE_RESOURCE_ID", imageResourceId)
// Android resource IDs are integers
// More efficient than passing image names

Why these data types?
Strings: Human-readable, flexible
Booleans: Perfect for binary states
Ints: Efficient for resource references
All are Parcelable: Can cross process boundaries

3. Data Reception in SecondActivity

SecondActivity's onCreate() now includes data extraction:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
// Find views first
findViews()
// Extract and apply received data
receiveAndDisplayData()
// Setup return navigation
setupBackButton()
}

4. Data Extraction and Application

The receiveAndDisplayData() method unpacks and applies the data:
private fun receiveAndDisplayData() {
// 1. GET INTENT: Access the Intent that started this Activity
val intent = intent // 'intent' is an Activity property
// 2. EXTRACT DATA: Retrieve each extra with default values
val selectedAnimal = intent.getStringExtra("SELECTED_ANIMAL") ?: "Unknown"
val isGrayscale = intent.getBooleanExtra("IS_GRAYSCALE", false)
val isRotated = intent.getBooleanExtra("IS_ROTATED", false)
val imageResourceId = intent.getIntExtra("IMAGE_RESOURCE_ID", R.drawable.cat_image)
// 3. APPLY IMAGE: Set the received image resource
receivedImageView.setImageResource(imageResourceId)
// 4. APPLY EFFECTS: Recreate the visual state
if (isGrayscale) {
val matrix = ColorMatrix()
matrix.setSaturation(0f)
val filter = ColorMatrixColorFilter(matrix)
receivedImageView.colorFilter = filter
}
if (isRotated) {
receivedImageView.rotation = 45f
}
// 5. UPDATE UI: Provide feedback about received data
updateInfoDisplay(selectedAnimal, isGrayscale, isRotated)
}

Data extraction pattern:
// Safe extraction with defaults
val value = intent.getTypeExtra("KEY", defaultValue)
↑ ↑ ↑
Type-specific Unique Fallback
getter method key if missing

5. Null Safety and Default Values

The extraction demonstrates defensive programming:
// String extraction with Elvis operator
val selectedAnimal = intent.getStringExtra("SELECTED_ANIMAL") ?: "Unknown"
// ↑
// If null, use "Unknown"

// Boolean extraction with default
val isGrayscale = intent.getBooleanExtra("IS_GRAYSCALE", false)
// ↑
// If not found, assume false

// Int extraction with fallback resource
val imageResourceId = intent.getIntExtra("IMAGE_RESOURCE_ID", R.drawable.cat_image)
// ↑
// Safe fallback image

6. State Recreation Logic

The effect application mirrors MainActivity's logic:
// Grayscale Application
if (isGrayscale) {
val matrix = ColorMatrix()
matrix.setSaturation(0f) // Remove all color
val filter = ColorMatrixColorFilter(matrix)
receivedImageView.colorFilter = filter
}

// Rotation Application
if (isRotated) {
receivedImageView.rotation = 45f // Apply 45-degree rotation
}

Important: Effects are conditionally applied based on received state, not toggled.

7. User Feedback Implementation

Informing the user about received data:
private fun updateInfoDisplay(animal: String, grayscale: Boolean, rotated: Boolean) {
// Build effect description
val effects = mutableListOf<String>()
if (grayscale) effects.add("Grayscale")
if (rotated) effects.add("Rotated")
val effectsText = if (effects.isEmpty()) {
"no effects"
} else {
"with ${effects.joinToString(" + ")}"
}
// Create comprehensive message
infoTextView.text = """
Received from first screen:
$animal image $effectsText
This demonstrates data passing between Activities!
""".trimIndent()
}

Data Flow Sequence Diagram

MainActivity Intent SecondActivity
│ │ │
├─[User clicks navigate] │ │
│ │ │
├─Create Intent──────────────>│ │
├─putExtra("ANIMAL", "Dog")──>│ │
├─putExtra("GRAYSCALE", true)>│ │
├─putExtra("ROTATED", false)─>│ │
├─putExtra("IMAGE_ID", 123)──>│ │
│ │ │
├─startActivity(intent)───────┼─────────────────────────────>│
│ │ │
│ │ onCreate()│
│ │ ├─getIntent()
│ │<─────────────────────────────┤
│ │ ├─getStringExtra("ANIMAL")
│ │ ├─getBooleanExtra("GRAYSCALE")
│ │ ├─getBooleanExtra("ROTATED")
│ │ ├─getIntExtra("IMAGE_ID")
│ │ │
│ │ ├─Apply image & effects
│ │ │

Intent Extras Bundle - Under the Hood

Bundle Structure:

// Conceptual representation of Intent's extras Bundle
Bundle {
entries: HashMap<String, Any> {
"SELECTED_ANIMAL" -> "Dog"
"IS_GRAYSCALE" -> true
"IS_ROTATED" -> false
"IMAGE_RESOURCE_ID" -> 2131165259
}
}

Serialization Process:

MainActivity Memory → Parcel (serialized) → Binder IPC → SecondActivity Memory
Binary format for
process boundaries

Key Architectural Patterns

1. Data Contract Pattern

We've established an implicit contract between Activities:
// Data Contract (should be documented/formalized)
object NavigationContract {
const val EXTRA_ANIMAL = "SELECTED_ANIMAL"
const val EXTRA_GRAYSCALE = "IS_GRAYSCALE"
const val EXTRA_ROTATED = "IS_ROTATED"
const val EXTRA_IMAGE_ID = "IMAGE_RESOURCE_ID"
}

2. State Transfer Pattern

// Sender (MainActivity)
private fun packageCurrentState(): Bundle {
return Bundle().apply {
putString(EXTRA_ANIMAL, currentAnimal)
putBoolean(EXTRA_GRAYSCALE, isGrayscale)
putBoolean(EXTRA_ROTATED, isRotated)
putInt(EXTRA_IMAGE_ID, resolveImageResource())
}
}

// Receiver (SecondActivity)
private fun unpackReceivedState(extras: Bundle?) {
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.