Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Android Activity Core Concepts: Layout, Lifecycle, Navigation, and Data Passing

Tech Aug 15 16

An Android project typically involves three key files working together: a Java/Kotlin controller, an XML layout definition, and the app manifest. Understanding their roles forms the foundation of Activity development.

Structuring the User Interface

The XML layout file dictates the visual arrangement of UI components. While Android Studio defaults to ConstraintLayout, switching to simpler containers like LinearLayout during initial learning clarifies the basic structure.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HomeScreen">

    <TextView
        android:id="@+id/display_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Initial Content" />

</LinearLayout>

Connecting Logic to Views

The activity class bridges the static layout and dynamic behavior. Inside onCreate(), the setContentView method inflates the XML. Components defined in the layout become accessible via their unique identifiers.

@Override
protected void onCreate(Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.activity_home);
    
    TextView output = (TextView) findViewById(R.id.display_text);
    output.setText("Updated programmatically");
}

The manifest declares every Activity to the operating system and defines launch properties. An entry with MAIN action and LAUNCHER category marks the app's primary screen.

<activity android:name=".HomeScreen"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Navigating Between Screens

Switching between activities relies on Intent objects. A basic navigation call targets the destination class directly.

Intent move = new Intent(this, DetailView.class);
startActivity(move);

Forwarding Primitive and String Values

Data can be attached to the intent using key-value pairs. The receiving activity retrieves them using typed accessor methods.

// Sender
Intent sendData = new Intent(getApplicationContext(), DataReceiver.class);
sendData.putExtra(DataReceiver.KEY_COUNT, 42);
sendData.putExtra(DataReceiver.KEY_FLAG, true);
sendData.putExtra(DataReceiver.KEY_LABEL, "Sample");
startActivity(sendData);
public class DataReceiver extends BaseActivity {
    public static final String KEY_COUNT = "count_val";
    public static final String KEY_FLAG = "flag_val";
    public static final String KEY_LABEL = "label_val";

    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            int num = extras.getInt(KEY_COUNT, 0);
            boolean status = extras.getBoolean(KEY_FLAG, false);
            String text = extras.getString(KEY_LABEL);
        }
    }
}

Transferring Complex Objects with Parcelable

Android's Parcelable offers efficient in-memory serialization. Unlike Serializable, it requires explicitly defining how data is packed and unpacked.

public class SongRecord implements Parcelable {
    private int id;
    private String title;
    private String artist;

    public SongRecord(int id, String title, String artist) {
        this.id = id;
        this.title = title;
        this.artist = artist;
    }

    protected SongRecord(Parcel source) {
        id = source.readInt();
        title = source.readString();
        artist = source.readString();
    }

    @Override
    public void writeToParcel(Parcel output, int flags) {
        output.writeInt(id);
        output.writeString(title);
        output.writeString(artist);
    }

    public static final Creator<SongRecord> CREATOR = new Creator<SongRecord>() {
        @Override
        public SongRecord createFromParcel(Parcel in) {
            return new SongRecord(in);
        }

        @Override
        public SongRecord[] newArray(int size) {
            return new SongRecord[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
}

Receiving Results from a Target Activity

To get data back after a sub-activity completes, use startActivityForResult and override onActivityResult.

private static final int EDIT_REQUEST = 100;

// Launching
Intent edit = new Intent(this, EditorScreen.class);
startActivityForResult(edit, EDIT_REQUEST);

// Handling return
@Override
protected void onActivityResult(int request, int result, Intent payload) {
    super.onActivityResult(request, result, payload);
    if (request == EDIT_REQUEST && result == RESULT_OK && payload != null) {
        String updatedText = payload.getStringExtra(EditorScreen.RETURN_VALUE);
        refreshDisplay(updatedText);
    }
}

The target activity sets the result payload before calling finish().

Intent output = new Intent();
output.putExtra(RETURN_VALUE, editField.getText().toString());
setResult(RESULT_OK, output);
finish();

Understanding the Activity Lifecycle

Activity transitions through distinct states. The visible lifetime spans onStart() through onStop(), while the foreground lifetime is bounded by onResume() and onPause().

Key distinctions:

  • onCreate() performs one-time initialization only when the system instantiates the activity.
  • onStart() makes the UI visible, but the activity may not yet have input focus.
  • onPause() should be brief; avoid performing heavy data base writes or network calls here because the method must complete before the next activity resumes. Use onStop() for CPU-intensive teardown tasks and releasing resources that are not needed while the activity is completely hidden.

Retrieving View Dimensions Correctly

Requesting a view's width or height inside onCreate() or onResume() typically returns 0 because layout measurement hasn't finalized. Reliable alternatives include:

Using a Window Focus Listener

@Override
public void onWindowFocusChanged(boolean focused) {
    super.onWindowFocusChanged(focused);
    if (focused) {
        int w = targetView.getWidth();
        int h = targetView.getHeight();
    }
}

Registering a Global Layout Observer

ViewTreeObserver observer = targetView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int measuredWidth = targetView.getWidth();
        int measuredHeight = targetView.getHeight();
        targetView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
});

Posting a Runnable to the View's Message Queue

targetView.post(() -> {
    int w = targetView.getWidth();
    int h = targetView.getHeight();
});

Once dimensions are known, you can dynamically adjust layouts, such as scaling an ImageView proportionally.

Launch Modes Overview

Activities are organized into tasks (back stacks). The launch mode dictates how instances are created:

  • standard: Default. A new instance is always created in the target task.
  • singleTop: If an instance already sits at the top of the target stack, it receives the intent via onNewIntent() instead of creating a duplicate.
  • singleTask: The system retains a single instance. If an existing instance is elsewhere in the stack, all activities above it are destroyed, and the intent is routed to onNewIntent().
  • singleInstance: The activity operates in its own isolated task and its task cannot contain other activities.

Defining Custom Transition Animations

Smooth activity transitions can be crafted using XML animation resources placed in the res/anim directory.

<!-- slide_in_right.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:fromXDelta="100%p"
    android:toXDelta="0%p" />

<!-- slide_out_left.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:fromXDelta="0%p"
    android:toXDelta="-100%p" />

Apply these through a style definition.

<style name="BaseAppTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowAnimationStyle">@style/CustomTransition</item>
</style>

<style name="CustomTransition">
    <item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
    <item name="android:activityOpenExitAnimation">@anim/slide_out_left</item>
    <item name="android:activityCloseEnterAnimation">@anim/slide_in_left</item>
    <item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
</style>

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.