1. Activity lifecycle

1.1 classification

Before explaining the lifecycle approach, here’s the official graph:

This picture illustrates the Activity’s callback methods. The following table categorizes the functions of these methods.

Lifecycle approach role
onCreate Indicates that the Activity is being created
onRestart Indicates that the Activity is being restarted
onStart Indicates that the Activity is being started
onResume Indicates that the Activity is visible
onPause Indicates that the Activity is stopping
onStop Indicates that the Activity is about to stop
onDestroy Indicates that the Activity is about to be destroyed

1.2 Lifecycle callbacks in various cases

Here’s A summary of the life cycle callbacks in each case (the A and B in the table represent two activities) :

situation The callback
First startup onCreate() -> onStart() -> onResume()
Jump from A to opaque B A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume() -> A_onStop()
Jump from A to transparent B A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume()
Opaque B goes back to A again B_onPause() -> A_onRestart() -> A_onStart() -> A_onResume() -> B_onStop()
From transparent B to transparent A again B_onPause() -> A_onResume() -> B_onStop() -> B_onDestroy()
The user presses the home button onPause() -> onStop()
Press the home button to return to the application onRestart() -> onStart() -> onResume()
The user presses the back key to roll back onPause() -> onStop() -> onDestroy()

1.3 onSaveInstanceState () and onRestoreInstanceState ()

These methods are only triggered when the application encounters unexpected conditions. Can be used to store some temporary data.

1.3.1 Triggering Scenario

onSaveInstanceState():

  1. Switching between horizontal and vertical screens
  2. Press the power button
  3. Press the menu button
  4. Switch to another Activity
  5. .

OnRestoreInstanceState () :

  1. Switching between horizontal and vertical screens
  2. To switch between languages
  3. .

2. Jump between activities

2.1 the relevant API

2.1.1 startActivity ()

How to use:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
Copy the code

Use this method to jump to SecondActivity

2.1.2 startActivityforResult ()

How to use:

MainActivity. Java:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
Copy the code

The second argument here is a requestCode, which is called back in onActivityResult.

SecondActivity. Java:

setResult(2);
finish();
Copy the code

The onActivityResult method in MainActivity is called back after SecondActivity Finish:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.e("chan"."==================onActivityResult main= " + requestCode + "" + resultCode);
}
Copy the code

Print result:

E/chan: ==================onActivityResult main= 1 2
Copy the code

Of course, you don’t have to call the setResult() method, so the resultCode that gets called back is 0.

2.2 Explicit Startup

Display startup categories:

  1. Launched directly in the Intent constructor:
Intent intent = new Intent(this, SecondActivity.class);
Copy the code
  1. SetComponent:
ComponentName componentName = new ComponentName(this, SecondActivity.class);
Intent intent = new Intent();
intent.setComponent(componentName);
Copy the code
  1. SetClass/setClassName:
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
intent.setClassName(this, "com.example.administrator.myapplication.SecondActivity");
Copy the code

2.3 Implicit Startup

An implicit launch is the setting of the IntentFilter attribute in the Activity. The Activity can be launched as long as the enabled Intent matches the IntentFilter criteria.

To understand implicit priming you have to understand how IntentFilter is used, right

2.3.1 Usage of IntentFilter

IntentFilter has three tags:

  1. action
  2. category
  3. data

All three tags have matching rules, as described below. Here are some considerations when using IntentFilter

  1. An Activity can have multiple Intent-filters
  2. An intent-filter can have multiple actions, categories, and data
  3. An Intent can start an Activity that matches any set of intent-filters
  4. New activities must include the following sentence:
<category android:name="android.intent.category.DEFAULT"/>
Copy the code

Otherwise, the following error occurs:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.chan1 }
Copy the code

2.3.2 Action Matching rule

The matching rule for an action is that only one action can be successfully started.

Define a SecondActivity in Manifest:

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.chan" />
        <action android:name="com.chan2" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
    <action android:name="com.chan3" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Copy the code

MainActivity:

Intent intent = new Intent();
intent.setAction("com.chan2");
startActivity(intent);
Copy the code

This will start SecondActivity, noting that actions are case sensitive.

2.3.3 Category Matching Rule

The category is set in code as follows:

intent.addCategory("com.zede");
Copy the code

This sentence can be added or not, because the code will match “Android.intent.category.default” for us by DEFAULT.

2.3.4 Data Matching rules

Data is mainly composed of URIs and mimeTypes. The URI structure is as follows:

<scheme> :// <host> : <port> [<path>|<pathPrefix>|<pathPattern>]
Copy the code

These values can be defined in the Manifest file with the following syntax:

<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string" />
Copy the code

Here’s an example:

The Manifest:

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data
            android:host="zede"
            android:port="1010"
            android:scheme="chan" />
    </intent-filter>
</activity>
Copy the code

MainActivity:

Intent intent = new Intent();
intent.setData(Uri.parse("chan://zede:1010"));
startActivity(intent);
Copy the code

You can jump to SecondActivity with this method.

We can also create an HTML file to jump to SecondActivity.

Test. HTML:

<! DOCTYPE html> <html> <head> <title></title> </head> <body> <a href="chan://zede:1010"> Jump to SecondActivity</ A > </body> </ HTML >Copy the code

Open the HTML file in your mobile browser and click the hyperlink to jump to SecondActivity as well.

You can also transfer data to SecondActivity using this link as follows:

<! DOCTYPE html> <html> <head> <title></title> </head> <body> <a href="chan://zede:1010/mypath? user=admin&psd=123456"> Jump to SecondActivity</ A > </body> </ HTML >Copy the code

Receiving data in SecondActivity:

Intent intent = getIntent();
Uri uri = intent.getData();

Log.e("chan"."==================getScheme= " + intent.getScheme());
Log.e("chan"."==================getHost= " + uri.getHost());
Log.e("chan"."==================getPort= " + uri.getPort());
Log.e("chan"."==================getPath= " + uri.getPath());

Log.e("chan"."==================getQuery= " + uri.getQuery());

Set < String > names = uri.getQueryParameterNames();

Iterator < String > iterator = names.iterator();
while (iterator.hasNext()) {
    String key = iterator.next();
    uri.getQueryParameter(key);
    Log.e("chan"."==================getQueryParameter= " + uri.getQueryParameter(key));
}
Copy the code

Print result:

07-19 10:47:54. 969, 19201-19201 / com. Example. Administrator. Myapplication E/chan: = = = = = = = = = = = = = = = = = = = getScheme chan 07-19 10:47:54. 970, 19201-19201 / com. Example. Administrator. Myapplication E/chan: ==================getHost= zede ==================getPort= 1010 ==================getPath= /mypath ==================getQuery= user=admin&psd=123456 ==================getQueryParameter= admin ==================getQueryParameter= 123456Copy the code

There’s another attribute to be aware of: Android :mimeType, which tells you what type of data to pass, usually text/plain or image/ JPEG.

You can start the Activity with the following code:

intent.setType("text/plain");
Copy the code

Note that if both URI and mimeType are set, you must use the following code to jump:

intent.setDataAndType(Uri.parse("chan://zede:1010"), "text/plain");
Copy the code

SetData () or setType() sets type and data to null, respectively.

3. The startup mode of the Activity

3.1 classification:

Boot mode role
standard An Activity is recreated each time it is started
singleTop If there is an Activity to start on the top of the stack, the Activity is not recreated and onNewIntent() is called back
singleTask If an Activity is already in the stack, it will not be created and onNewIntent() will be called.
singleInstance Creating the Activity creates a new task stack

Here we focus on singleTask.

3.2 singleTask analysis

SingleTask is called in-stack reuse mode. The startup logic of this startup mode is shown below:

I believe that the logic of singleTask is clear from the above figure, but there are a few points to note about this mode.

3.2.1 taskAffinity

So what is the task stack that A wants? This brings up a property called taskAffinity, which is described in more detail below.

3.2.1.1 role

Identifies the name of the task stack required by an Activity. If this property is not set, the default value is the application package name.

3.2.1.2 taskAffinity is paired with singleTask

If you start an Activity with these properties set, the Activity will be in the task stack set by taskAffinity.

Create SecondActvitiy and set the SecondActvitiy in the Mnifest file as follows:

<activity android:name=".SecondActivity"
    android:taskAffinity="com.chan"
    android:launchMode="singleTask" />
Copy the code

Now launch SecondActvitiy with MainActivity. This code is not shown here, so let’s go straight to the result and type the following command on the terminal:

adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
Copy the code

This command displays the Activity that is running as follows:

Running activities (most recent first):
    TaskRecord{762a040 #63 A=com.chan U=0 StackId=1 sz=1}
    Run #1: ActivityRecord{3881f68 u0 com.example.activitydemo/.SecondActivity t63}
    TaskRecord{351eb79 #62 A=com.example.activitydemo U=0 StackId=1 sz=1}
Copy the code

As you can see from the printed results, MainActivity and SecondActivity run on different task stacks.

3.2.1.3 taskAffinity and allowTaskReparenting are used together

AllowTaskReparenting is a trait that many people might find difficult to explain, but it can be explained in an example:

Now create two applications, one package named com.example.activitydemo, which will be called application A. The package name of the other application is com.example.hellodemo, hereafter called application B.

Add the following code to MainActivtiy for application A:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        Intent intent=new Intent();
        intent.setClassName("com.example.hellodemo"."com.example.hellodemo.HelloActivity"); startActivity(intent); }}); }Copy the code

Create HelloActivity in application B and set the HelloActivity’s allowTaskReparenting to true in the Manifest file:

<activity android:name=".HelloActivity"
    android:exported="true"
    android:allowTaskReparenting="true" />
Copy the code

Then follow these steps:

  1. Now run application B and exit
  2. Run app A again and press the button to start
  3. Press the home button to return to the home screen
  4. Start application B

At the end of step 2, take a look at the task stack:

Running activities (most recent first):
    TaskRecord{5d54c1c #85 A=com.example.activitydemo U=0 StackId=1 sz=2}
    Run #1: ActivityRecord{ff0b8e u0 com.example.hellodemo/.HelloActivity t85}
    Run #0: ActivityRecord{95ee35c u0 com.example.activitydemo/.MainActivity t85}
Copy the code

You can see that the HelloActivity is running in application A’s task stack.

After completing step 4, look at the task stack:

Running activities (most recent first):
    TaskRecord{74c894d #86 A=com.example.hellodemo U=0 StackId=1 sz=2}
        Run #1: ActivityRecord{ff0b8e u0 com.example.hellodemo/.HelloActivity t86}
    TaskRecord{5d54c1c #85 A=com.example.activitydemo U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{95ee35c u0 com.example.activitydemo/.MainActivity t85}
Copy the code

As you can see from the results, the HelloActivity moves from application A’s task stack to Application B’s task stack.

Now modify the taskAffinity property of the HelloActivity as follows:

<activity android:name=".HelloActivity"
    android:exported="true"
    android:allowTaskReparenting="true"
    android:taskAffinity="com.chan"/>
Copy the code

Repeat the above steps and look at the task stack information after the operation is complete:

Running activities (most recent first):
    TaskRecord{50264fe #90 A=com.example.hellodemo U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{bc77713 u0 com.example.hellodemo/.MainActivity t90}
    TaskRecord{41abf9e #89 A=com.example.activitydemo U=0 StackId=1 sz=2}
    Run #1: ActivityRecord{2d0b7bb u0 com.example.hellodemo/.HelloActivity t89}
    Run #0: ActivityRecord{8b57551 u0 com.example.activitydemo/.MainActivity t89}
Copy the code

You can see that The HelloActivity is not moved to the main task stack of application B, because this is not the desired task stack for The HelloActivity.

Continue modifying the HelloActivity configuration properties by adding the singleTask property:

<activity android:name=".HelloActivity"
    android:exported="true"
    android:allowTaskReparenting="true"
    android:taskAffinity="com.chan"
    android:launchMode="singleTask"/>
Copy the code

Continue operation, the result of the task stack is as follows:

Running activities (most recent first):
    TaskRecord{775e709 #95 A=com.example.hellodemo U=0 StackId=1 sz=1}
        Run #2: ActivityRecord{757bb47 u0 com.example.hellodemo/.MainActivity t95}
    TaskRecord{aa75b2 #94 A=com.chan U=0 StackId=1 sz=1}
        Run #1: ActivityRecord{76e2133 u0 com.example.hellodemo/.HelloActivity t94}
    TaskRecord{21c8903 #93 A=com.example.activitydemo U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{be84df4 u0 com.example.activitydemo/.MainActivity t93}
Copy the code

As you can see, the allowTaskReparenting attribute is the same as not adding singleTask. In fact, the main function of the allowTaskReparenting attribute is to move the Activity to its task stack, for example, when a messaging app receives a text message with a network link. Clicking on the link takes you to the browser, and if allowTaskReparenting is set to true, opening the browser app will show you the page you just opened, while the browser interface disappears when you open the messaging app.

3.3 Setting the boot mode

You can specify the launchMode by setting the launchMode property in the AndroidMenifest file, or by setting the flag bit in the Intent. The priority of the second method is higher than that of the first method. If both methods are set, the second method will prevail. Let’s verify:

Set the following code in MainActivity:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(MainActivity.this, SecondActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); }}); }Copy the code

SecondActivity on AndroidMenifest is set as follows:

        <activity android:name=".SecondActivity"
            android:taskAffinity="com.chan" />
Copy the code

I didn’t set SecondActivity to sigleTask to verify that starting SecondActivity opens a new task stack.

After running, the result of the task stack is:

    Running activities (most recent first):
      TaskRecord{148d7c5 #143 A=com.chan U=0 StackId=1 sz=1}
        Run #2: ActivityRecord{de59b2d u0 com.example.activitydemo/.SecondActivity t143}
      TaskRecord{520151a #142 A=com.example.activitydemo U=0 StackId=1 sz=1}
        Run #1: ActivityRecord{d80bfc1 u0 com.example.activitydemo/.MainActivity t142}
Copy the code

It can be seen from the result that a new task stack is opened, which also proves that the second method has a higher priority

3.4 onNewIntent Callback timing

The onNewIntent() method is called when the Activity of singleTask is started, but this is not the case in all cases, as summarized below:

The following uses code to verify these four cases:

3.4.1 There is no task stack required by A

The code is as follows:

MainActivity. Java:

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); }}); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.e("chan"."MainActivity=======================onNewIntent"); }}Copy the code

SecondActivity. Java:

public class SecondActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        findViewById(R.id.start2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SecondActivity.this, ThirdActivity.class)); }}); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.e("chan"."SecondActivity=======================onNewIntent"); }}Copy the code

The manifest file sets SecondActivity to singleTask and the taskAffinity property to a value other than the package name as follows:

        <activity android:name=".SecondActivity"
            android:taskAffinity="com.onnewintent"
            android:launchMode="singleTask" />
Copy the code

The result of the above code does not print anything, proving that this does not call onNewIntent().

3.4.2 Task stack required by A exists

There are two things that happen, one is A is not on the stack, and the other is A is not on the stack.

3.4.2.1 A is not in the stack

Going back to the code in the example above, add a ThridActivity, described in the manifest:

        <activity android:name=".ThirdActivity"
            android:taskAffinity="com.onnewintent"
            android:launchMode="singleTask" />

Copy the code

Clicking the SecondActivity jump button also does not print anything, proving that onNewIntent() is not called back.

3.4.2.2 A is in the stack

There are also two kinds of cases where A is at the top of the stack and A is not at the top of the stack.

3.4.2.2.1 A is at the top of the stack

Using the same example above, modify ThirdActivity with the following code:

public class ThirdActivity extends AppCompatActivity {



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);
        findViewById(R.id.start3).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(ThirdActivity.this, ThirdActivity.class)); }}); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.e("chan"."ThirdActivity=======================onNewIntent"); }}Copy the code

When ThirdActivity is at the top of the stack, click the button to start ThirdActivity again.

chan    : ThirdActivity=======================onNewIntent
Copy the code

You can see that this case calls back to ThirdActivity’s onNewIntent.

3.4.2.2.2 A is not at the top of the stack

Also change ThirdActivity, this time starting SecondActivity. The specific code will not be written, and the printed result is as follows:

chan    : SecondActivity=======================onNewIntent
Copy the code

You can see that this situation calls back to SecondActivity’s onNewIntent.

3.5 Flags in The Activity

3.5.1 track of FLAG_ACTIVITY_NEW_TASK

As with the launch mode singleTask, taskAffinity must be set

3.5.2 FLAG_ACTIVITY_SINGLE_TOP

Same as the boot mode singleTop

3.5.3 FLAG_ACTIVITY_CLEAR_TOP

As you can see from the name, this flag is used to remove all activities at the top of the Activity if there are other activities at the top of the Activity. If the Activity starts in Standard mode, The previous Activity is pushed off the stack and a new Activity is created. If it is not, the onNewIntent method is called.

3.5.4 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

You cannot view this Activity in View History Activity