Mehdi Tmimi Mobile

Mobile Development Lab 1 Part 5 of 8

From XML to Java: the activity and its life

You press Home. Did your screen die? Your activity can tell you, in Logcat.

Start of session

  1. Start Android Studio: it reopens Notes by itself. If the Welcome window shows instead: ProjectsNotes.
  2. Start the phone from the project: Device Manager (the phone icon on the window’s right edge, or ViewTool WindowsDevice Manager), ▶ on Pixel 7. It appears in Running Devices.
  3. Wait until indexing ends (the bar at the bottom) before Alt+Enter or Ctrl+O.
The emulator at the end of Part 4, just launched: the gradient header with the notes logo and 0 in the pill, the composer card with Title and Write something…, an empty list.
Where you start

Before you start

By the end you can

  • explain how setContentView() turns activity_main.xml into objects;
  • write a line to Logcat and filter it;
  • predict the methods Android calls for Home, coming back and a rotation.
  • Java MainActivity.java
  • Logcat
  • Emulator
Behind, or a fresh project? Start from here about 4 min
  1. The project: your own from Part 4, or a fresh one: Part 0, steps 0.3 and 0.4 (about 5 minutes).

  2. Your package name, for the code of this page:

    Without JavaScript, the code says com.yourname.notes: replace yourname by the name you chose.

  3. Gradle: check these 3 lines. Add any that is missing, then click Sync Now. Keep your own version numbers.

    Gradle Scripts › libs.versions.tomlAdd the marked lines
    [versions]agp = "9.5.0"junit = "4.13.2"junitVersion = "1.3.0"espressoCore = "3.7.0"appcompat = "1.8.0"material = "1.14.0"constraintlayout = "2.2.2"
    [libraries]junit = { group = "junit", name = "junit", version.ref = "junit" }ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }material = { group = "com.google.android.material", name = "material", version.ref = "material" }constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
    [plugins]android-application = { id = "com.android.application", version.ref = "agp" }
    Gradle Scripts › build.gradle.kts (Module :app)Add the marked lines
    plugins {    alias(libs.plugins.android.application)}
    android { namespace = "com.yourname.notes" compileSdk { version = release(37) }
    defaultConfig { applicationId = "com.yourname.notes" minSdk = 24 targetSdk = 37 versionCode = 1 versionName = "1.0"
    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }
    buildTypes { release { optimization { enable = true packageScope = setOf("androidx.**", "kotlin.**", "kotlinx.**") } } } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }}
    dependencies { implementation(libs.appcompat) implementation(libs.material) implementation(libs.constraintlayout) testImplementation(libs.junit) androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.ext.junit)}
  4. res and manifest: download the pack and open it in Explorer. Drag its app folder onto your project folder (C:\Users\<you>\AndroidStudioProjects\Notes), then choose Replace the files in the destination. Not Extract All: it makes a folder inside and replaces nothing. Then, in res › values › themes, delete themes.xml (night) if it is still there.

    lab1-part5-res.zip zip · 6.3 KB
  5. Java: open MainActivity.java, select all, then paste this file. Missing? First right-click kotlin+java › com.yourname.notes, the one without (test), then NewJava Class: type MainActivity, then press Enter.

    kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
    package com.yourname.notes;
    import android.os.Bundle;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
  6. ▶. You are ready when the phone shows the styled notes screen: the gradient header, the composer, and 0 in the pill.

Step 1 of 5

Read your activity

4 min

Goal Explain in one sentence how activity_main.xml becomes your screen’s objects.

  • Java kotlin+java › com.yourname.notes › MainActivity.java
  1. Your activity, open in Android Studio, and its four notes.

    1. In the Project window, open kotlin+java › com.yourname.notes › MainActivity.java.
    2. Read the four notes under the code below: each number sits on its line.
kotlin+java › com.yourname.notes › MainActivity.javaRead only
package com.yourname.notes;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
  1. extends AppCompatActivity: your class is an activity, a screen that Android can open.
  2. onCreate(): Android calls it when it creates the screen. Your app has no main(): Android starts it (chapter 02). @Override: you redefine a method of the parent class.
  3. super.onCreate(…): the parent class does its own work first. It stays the first line.
  4. R is a class the build writes, with one int per resource. R.layout.activity_main names your layout file: setContentView() reads it and creates its objects.
  1. activity_main.xml your tags: a description of the screen
  2. setContentView() reads the file that R.layout.activity_main names
  3. Objects one Java object per tag: a tree of views, in memory
  4. Pixels Android’s own toolkit draws the tree (chapter 01)
This is inflation: one tag, one object. Changing an object later does not change the file.

Step 2 of 5

Write your first line to Logcat

6 min

Goal Make your activity write “Main onCreate” to Logcat, then show only your lines.

  • Java MainActivity.java
  • Logcat package:mine tag:Lifecycle
  1. Type A tag for your lines.

    1. In MainActivity.java, click at the end of the class line, public class MainActivity …, then press Enter twice.
    2. Type the panel’s line 10: private static final String TAG = "Lifecycle";
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }}
  2. Type Your first line for Logcat.

    1. Click at the end of the setContentView(…) line, then press Enter.
    2. Type the panel’s line 16: Log.d(TAG, "Main onCreate");
    3. If Log is red, click it, then press Alt+Enter.
    4. A list opens: choose Import class. Android Studio writes line 4, import android.util.Log;.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }}

    Log.d writes a debug line, level D, labelled with your tag. Line 4, grey: Whole file shows it.

  3. The app runs, and Logcat opens.

    1. Click ▶ Run 'app' in the toolbar (↻ Rerun if the app already runs).
    2. Open ViewTool WindowsLogcat: Logcat opens at the bottom of the window.
  4. Only your lines, by their tag.

    1. Logcat’s query field already holds package:mine: click right after it.
    2. Type a space, then tag:Lifecycle. One line stays: Main onCreate.
    3. If a tip “Press Ctrl+Space to see suggestions” appears, click Got It.
    Logcat Tool window · bottomFull size
    Logcat with the query package:mine tag:Lifecycle and one line: 7837-7837, Lifecycle, com.yourname.notes, D, Main onCreate.
    1. The query field: package:mine tag:Lifecycle.
    2. The process number (PID): 7837 here; yours differs.
    3. The tag: Lifecycle.
    4. Your message: Main onCreate.

Done I see my line “Main onCreate”, with its process number.

Stuck? 3 symptoms · the file
  • Cannot resolve symbol 'Log' The class is not imported. Click Log, press Alt+Enter, then choose Import class.
  • Alt+Enter offers nothing Android Studio is still indexing: the bar at the bottom of the window. Wait for it to end.
  • No line appears Check the query, package:mine tag:Lifecycle, and the device menu at its left: Pixel 7 (emulator-5554) Android 17, API 37.0.

The files at the end of this step

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }}

Step 3 of 5

Add the six other methods

6 min

Goal Give your activity six more lifecycle methods, each writing its name to Logcat.

  • Java MainActivity.java
  1. The list of methods to override, opened inside the class.

    1. In MainActivity.java, click right after the } that ends onCreate(), at the end of its line.
    2. Press Ctrl+O. A Select Methods to Override/Implement window opens.
  2. onStart(), written by Android Studio.

    1. Type onstart: the selection jumps to onStart(): void, the only row selected.
    2. Press Enter. onStart() appears under onCreate(), its super.onStart(); line selected in blue: type nothing yet.
    Select Methods to Override/Implement A dialogFull size
    The Select Methods to Override/Implement dialog: onstart typed in the search field; the onStart(): void row is the only one selected; Insert @Override is ticked; OK at the bottom right.
    1. Type onstart: the selection jumps to it.
    2. onStart(): the only method you pick.
    3. OK, or Enter: writes the method.
  3. Type Its line for Logcat.

    1. Click at the end of the super.onStart(); line, then press Enter.
    2. Type line 22: Log.d(TAG, "Main onStart");
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }}

    Grey lines: Ctrl+O writes them.

  4. Paste onResume() and onPause(), after onStart().

    1. On the panel below, find After line 23, then click Copy 12 lines.
    2. In MainActivity.java, click at the end of line 23: the } that ends onStart().
    3. Press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }}
  5. Paste onStop() and onRestart(), after onPause().

    1. On the panel below, find After line 35, then click Copy 12 lines.
    2. In MainActivity.java, click at the end of line 35: the } that ends onPause().
    3. Press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}
  6. Paste onDestroy(), after onRestart().

    1. On the panel below, find After line 47, then click Copy 6 lines.
    2. In MainActivity.java, click at the end of line 47: the } that ends onRestart().
    3. Press Enter, then Ctrl+V.
    kotlin+java › com.yourname.notes › MainActivity.javaAdd the marked lines
    package com.yourname.notes;
    import android.os.Bundle;import android.util.Log;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Lifecycle";
    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
    @Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
    @Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
    @Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
    @Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
    @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
    @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Why Android calls only the parent’s exact names: Ctrl+O writes them, with @Override and super.

Stuck? 4 symptoms · the file
  • Ctrl+O opens nothing The cursor is outside the class: click right after the } of onCreate(), and delete any onstart typed in the code. Or wait for indexing to end.
  • The super.onStart(); line vanished You typed while it was selected (blue). Ctrl+Z brings it back.
  • Your methods came in another order: onResume() right after onCreate() You picked several in the dialog: it writes them in its own order. They work the same. To match the page’s order, replace your whole file with the one below.
  • compact source file should not have package declaration The methods went after the class’s last }. Replace your whole file with the one below.
MainActivity.java Editor · centreFull size
MainActivity after OK: onCreate, then the new methods onResume and onPause, each with @Override and a super call.
  1. onResume(), right after onCreate(): the dialog’s order, not the lifecycle order (onStart() comes first).
  2. onPause(): @Override and super.onPause(); written for you.

The files at the end of this step

kotlin+java › com.yourname.notes › MainActivity.javaReplace the whole file
package com.yourname.notes;
import android.os.Bundle;import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Lifecycle";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "Main onCreate"); }
@Override protected void onStart() { super.onStart(); Log.d(TAG, "Main onStart"); }
@Override protected void onResume() { super.onResume(); Log.d(TAG, "Main onResume"); }
@Override protected void onPause() { super.onPause(); Log.d(TAG, "Main onPause"); }
@Override protected void onStop() { super.onStop(); Log.d(TAG, "Main onStop"); }
@Override protected void onRestart() { super.onRestart(); Log.d(TAG, "Main onRestart"); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Main onDestroy"); }}

Step 4 of 5

Predict, then check in Logcat

8 min

Goal Predict what Android calls when you leave, come back and turn the phone, then check in Logcat.

  • Emulator Running Devices
  • Logcat package:mine tag:Lifecycle
  1. onCreate() the object is created
  2. onStart() the screen becomes visible
  3. onResume() in front: your taps reach it
The screen opens: Android calls these three, in this order.
  1. onPause() no longer in front
  2. onStop() no longer visible
  3. onDestroy() the object is destroyed
The screen goes away: these three, in this order, but not always all three.

Predict first

Write down what each action will show. First: after Home, which line comes last?

See the answer

Main onStop: Home stops your activity, without destroying it. The rest of the table waits after the actions.

  1. Launch, leave, come back, turn: one table row at a time.

    1. In Logcat, click the trash icon at the top of its left column: Clear Logcat.
    2. Click the row’s button (Rerun in the main toolbar, the others in Running Devices), then do what Then says.
    3. Read your lines now: compare them with your prediction.
    The four actions, in order
    ButtonThen
    1↻ Rerun, or ▶ Run 'app' if the app is not runningNothing.
    2HomeNothing.
    3OverviewThe recent apps appear as cards: tap the one that shows your notes screen.
    4Rotate LeftNothing. In landscape the composer runs off the screen: Lab 1 keeps the phone upright.
  2. Upright again.

    1. In the Running Devices toolbar, click Rotate Right: the phone stands upright again.
  3. Your predictions, checked.

    1. Open The answer table, just below: check each row against the lines you read.
The answer table

From a real run. onRestart() comes before onStart() when a stopped screen comes back.

ActionLines in Logcat
▶ Run 'app'Main onCreate · Main onStart · Main onResume
HomeMain onPause · Main onStop
Overview, then your appMain onRestart · Main onStart · Main onResume
Rotate LeftMain onPause · Main onStop · Main onDestroy · Main onCreate · Main onStart · Main onResume

Done I see my lines match the table.

Stuck? 2 symptoms
  • Old lines mix with the new ones Clear Logcat before each action, with its trash icon.
  • The screen does not turn Auto-rotate is off. Drag down twice from the top of the phone’s screen, then tap the rotation tile (two arrows around a phone) so it turns blue.

No file changes in this step: step 5.3 holds the file.

Step 5 of 5

Read what your lines prove

4 min

Goal Say what Home and a rotation do to your activity, with your lines as proof.

  • Logcat package:mine tag:Lifecycle

This proves Home stops your activity without destroying it: leaving is not closing (chapter 03).

You should see10-31 19:36:49.584  5947  5947 D Lifecycle: Main onPause
10-31 19:36:50.722  5947  5947 D Lifecycle: Main onStop

This shows A rotation destroys your activity and creates a new object, in the same process: same process number.

You should see10-31 19:37:27.802  5947  5947 D Lifecycle: Main onPause
10-31 19:37:27.805  5947  5947 D Lifecycle: Main onStop
10-31 19:37:27.806  5947  5947 D Lifecycle: Main onDestroy
10-31 19:37:27.844  5947  5947 D Lifecycle: Main onCreate
10-31 19:37:27.844  5947  5947 D Lifecycle: Main onStart
10-31 19:37:27.850  5947  5947 D Lifecycle: Main onResume

Your times and process numbers will differ. These lines come from adb logcat: the Logcat window shows the same fields in columns.

Go deeper Back, and edge-to-edge 1 min

The Back button, on this first screen. Right after a start with ▶, Back calls onDestroy(). After a start from the app’s icon, or once you came back through Overview, it does not: Android keeps the activity, as for Home. Part 8 shows onDestroy() on Back, on its second screen.

Edge-to-edge. The Empty Views Activity template writes Android’s documented way: WindowCompat.enableEdgeToEdge(getWindow()) and an insets listener in onCreate(). This lab needs neither: android:fitsSystemWindows, a standard attribute, does the job.

Now you

Count the new objects

Clear Logcat, then ▶. Click Rotate Left, Rotate Right, Rotate Left: how many Main onCreate lines?

See the answer

Four: one at launch, then one per rotation. Each rotation creates a new object of your activity. Three Rotate Left in a row give only two: the phone never turns upside down, and from one landscape to the other it keeps the activity.

If it doesn’t work

  • “Waiting for all target devices to come online” The phone is still booting: wait.
  • error: package R does not exist The first line of MainActivity is not your package. Copy the file again: the Copy the file button writes your package into it.
  • The phone opened in its own window, not in Running Devices You started it from the Welcome window. Close that window, then start it from the project’s Device Manager.

End of the part

Recap

No: Home only stopped your activity. A rotation destroyed it and created a new one. And setContentView() turns your XML into objects.

  • explain how setContentView() turns activity_main.xml into objects;
  • write a line to Logcat and filter it;
  • predict the methods Android calls for Home, coming back and a rotation.
Your app at the end of Part 5: the same screen as at the end of Part 4. What changed is in Logcat.
Your app now
← Lab 1, all parts