Activity

An activity is simply a standalone page/window, that is responsible for displaying UI and handles interaction events.

AndroidManifest.xml

AndroidManifest.xml is the global registration center for all Activities, otherwise, the app does not know the existence of this activity.

Let’s peek into it:

app/src.main/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myfirstactivity">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyFirstActivity">

<!-- 这里就是 MainActivity 的注册信息 -->
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>

Something to notice here.

  • android:name = ".MainActivity" tells the class name of activity. Preceding . stands for package name. In my example, the package name is com.example.myfirstactivity, thus the full name of this class name is com.example.myfirstactivity.MainActivity
  • android:exported = "true" tells that this activity can be accessed by external apps.
  • <intent-filter>. The action and category tags together tells the system that this activity is the main entrance of the app (ACTION_MAIN) and should appear in phone’s app launcher (CATEGORY_LAUNCHER)

Page Layout

How does a page (activity) looks like is determined by a .xml layout file, which is often located in app/src/main/res/layout/.

In Android Studio, when opening such file, we can directly edit the .xml file to edit the layout.

activity_main.xml

Case study: activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/helloTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, Android!"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me"
app:layout_constraintTop_toBottomOf="@id/helloTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

The defined layout can be loaded into activity with setContentView

MainActivity.kt
1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// !! KEY !!
// Loads the layout to activity
setContentView(R.layout.activity_main);
}

Lifecycle of Activity

Activity is page. Using app of course means changing between pages, which leads to “exiting from one activity” and “entering into another new activity”.

Android informs us the the change of states of activities through Lifecycle callbacks that are triggered at some timings, which we can override to perform some necessary functionalities.

Lifecycle Callbacks

  • onCreate(Bundle savedInstanceState)

    Invoked at the first time when it’s created. Invoked only once in its whole lifecycle.

    We may do some initialization jobs here, e.g.

    1. invoke setContentView() to set page layout
    2. initialize variables, and bind views with findViewById
    3. set event listener
    4. restore previously saved state from savedInstanceState
  • onStart()

    Invoked at the time when activity is ready to be presented and is going to be visible, but yet to be interacted with.

    We can do some UI-related preparation works here.

  • onResume()

    Invoked when activity is completely visible, and receives focus from user and can be interacted with.

    This is the main stage for an activity to interact with the user. e.g., start animation, camera, register event listeners that requires focus.

  • onPause()

    Invoked when activity is about to lose focus but still (partially) visible. Typically scenarios include pop-ups, or pressing home.

    Since this the sign of pausing, we should only do light-weight operations because the starting of next activity will wait for the completion of a previous activity’s onPause().

    Some typically operations that can be done in this stage include

    1. pause video/animation
    2. save unsubmitted data/drafts
    3. release system resources, e.g., camera
  • onStop()

    Invoked when the activity is completely invisible. At this time, the activity goes to backend. We can perform some more aggressive resource release.

  • onRestart()

    Invoked when an already onStop()-ed activity is back to focus, before onStart().

    This usually means this activity is being re-displaying. We can perform some restoring.

    Note that, the invocation order is onRestart() -> onStart() -> onResume()

  • onDestroy()

    Invoked only once during the whole lifecycle, at the time before its destruction. We should release all resources here, e.g., cancel network requests, unregister listeners and clean threads.

Here are some typical scenarios:

  1. Start app for the first time. onCreate() -> onStart() -> onResume()
  2. Press Home to put App at backend. onPause() -> onStop()
  3. Reopen App from desktop or recent apps. onRestart() -> onStart() -> onResume()
  4. Press return and exit App. onPause() -> onStop() -> onDestroy()
  5. Rotate screen. onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> onResume().

We should notice the -> onDestroy() -> onCreate() calling chain here. When rotating, to load (possible) layout specifically for horizontal screens, Android will destroy currect activity and re-create a new one, which may cause the problem that temporary data are lost.

To solve this, Android has provided two extra methods.

Save Data on Rotation

Lifecycle Callbacks for Rotation

  • onSaveInstanceState(Bundle outState)

    Invoked when activity is about to be destroyedm but before onStop(). We can restore temporary data into this object.

  • onRestoreInstanceState(Bundle savedInstanceState)

    Invoked when activity is reconstructed, but after onCreate(). But a more common practice is to restore in onCreate().