Lesson 116. Activity Behavior in Task. Intent-flags, launchMode, affinity

Lesson 116. Activity Behavior in Task. Intent-flags, launchMode, affinity


In this lesson:

– change the behavior of Activity in Task

When I was talking about Task in Lesson 25, I decided that it would be too early to talk about the possibility of changing Activity behavior. And in help you can find the following words: “The way Android manages tasks and the back stack (…) works great for most applications and you should not have to worry about how your activities are associated with tasks or how they exist in the back stack “. That is, default behavior is quite appropriate in most cases and we do not need to think about it. But there may well be times when behavior will need to be affected. Let’s talk about what means you can do. Let’s take the Tasks and Back Stack article from Help.

I do not tell about the mechanism of filling the Task, everything is in Lesson 25. Let’s just decide on the wording. Task is a Task. The root of a saddle or the root Activity of a saddle is the first Activity placed in a saddle. Top Task or Top Activity in Tasko is the latest Activity added to Task.

For example examples, we will need two applications.

The first consists of 4 Activity: A, B, C, D. They will call each other in sequence: A -> B -> C -> D. And D will call itself: D -> D.
The second application is only one Activity, it will call C from the first application.

Let’s create a project:

Project name: P1161_MngTasks1
Build Target: Android 4.1
Application name: MngTasks1
Package name: ru.startandroid.develop.p1161mngtasks1
Create Activity: MainActivity

Add rows to strings.xml:

Start
Info

Layout file main.xml:



	
	

Two buttons. Call Activity and output information to the log.

MainActivity.java:

package ru.startandroid.develop.p1161mngtasks1;

import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public abstract class MainActivity extends Activity {
  
  final String LOG_TAG = "myLogs";
  List list;
  ActivityManager am;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    setTitle(getResources().getString(R.string.app_name) + " : " + getLocalClassName());
    am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
  }
  
  public void onInfoClick(View v) {
    list = am.getRunningTasks(10);
    for (RunningTaskInfo task : list) {
      if (task.baseActivity.flattenToShortString().startsWith("ru.startandroid.develop.p116")){
      Log.d(LOG_TAG, "------------------");
      Log.d(LOG_TAG, "Count: " + task.numActivities);
      Log.d(LOG_TAG, "Root: " + task.baseActivity.flattenToShortString());
      Log.d(LOG_TAG, "Top: " + task.topActivity.flattenToShortString());
      }
    }
  }
  
  abstract public void onClick(View v);
}

In onCreate, we place the name of the program and the name of the Activity class in the header to see on the screen which Activity of which application is currently active.

In onInfoClick we use ActivityManager. The getRunningTasks method will return us a list of currently running TASKs. The last active strings will be at the beginning of the list. Just in case we will get the first 10. Among them, exactly two will be ours. Next we get information about TASK: Activity (numActivities), Root Activity (baseActivity), Upper Activity (topActivity). And we sift our weasels in the name of the package of root Activity.

This class will be the ancestor for the next 4. It contains an abstract onClick method that we will need to implement from heirs. This method will be invoked after clicking the Start button.

We create 4 Activity.

ActivityA.java:

package ru.startandroid.develop.p1161mngtasks1;

import android.content.Intent;
import android.view.View;

public class ActivityA extends MainActivity {
  public void onClick(View v) {
    startActivity(new Intent(this, ActivityB.class));
  }
}

ActivityB.java:

package ru.startandroid.develop.p1161mngtasks1;

import android.content.Intent;
import android.view.View;

public class ActivityB extends MainActivity {
  public void onClick(View v) {
    startActivity(new Intent(this, ActivityC.class));
  }
}

ActivityC.java:

package ru.startandroid.develop.p1161mngtasks1;

import android.content.Intent;
import android.view.View;

public class ActivityC extends MainActivity {
  public void onClick(View v) {
    startActivity(new Intent(this, ActivityD.class));
  }
}

ActivityD.java:

package ru.startandroid.develop.p1161mngtasks1;

import android.content.Intent;
import android.view.View;

public class ActivityD extends MainActivity {
  public void onClick(View v) {
    startActivity(new Intent(this, ActivityD.class));
  }
}

All Activity inherits MainActivity. And, so, they will print the header of the info about themselves, and after clicking on the Info button – show the current info on TASK in the beam.

Well, the onClick method is a call to the next Activity. ActivityD will challenge itself.

New manifestation in the manifest. ActivityA will be set as the start instead of MainActivity. ActivityC contains a filter with action = mngtasks1_activity_c. MainActivity is removed from the manifest.

And add the rights to the TASK data: android.permission.GET_TASKS.

The first application is ready. Let’s create the second one. It is quite similar, only Activity will be one.

Let’s create a project:

Project name: P1162_MngTasks2
Build Target: Android 4.1
Application name: MngTasks2
Package name: ru.startandroid.develop.p1162mngtasks2
Create Activity: MainActivity

Add rows to strings.xml:

Start
Info

Layout file main.xml:



	
	

MainActivity.java:

package ru.startandroid.develop.p1162mngtasks2;

import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";
  List list;
  ActivityManager am;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    setTitle(getResources().getString(R.string.app_name) + " : " + getLocalClassName());
    am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
  }

  public void onClick(View v) {
    startActivity(new Intent("mngtasks1_activity_c"));
  }


  public void onInfoClick(View v) {
    list = am.getRunningTasks(10);
    for (RunningTaskInfo task : list) {
      if (task.baseActivity.flattenToShortString().startsWith("ru.startandroid.develop.p116")) {
        Log.d(LOG_TAG, "------------------");
        Log.d(LOG_TAG, "Count: " + task.numActivities);
        Log.d(LOG_TAG, "Root: " + task.baseActivity.flattenToShortString());
        Log.d(LOG_TAG, "Top: " + task.topActivity.flattenToShortString());
      }
    }
  }

}

IN onClick there is a call to ActivityC from the first application.

Don’t forget about android.permission.GET_TASKS in the manifest.

Let’s save everything and launch the first application.

In the header we see the name of the program and Activity. press Info and look at the log:

Count: 1
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityA

In TASK of the first application one Activity, root – ActivityA, top – ActivityA.

Let’s push Start three times to go through B, C and get into D

press Info:

Count: 4

Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

Now in Tasco 4 Activity, the root is A, the top is D. Between them B and C.

Let’s launch the second application.

press Info:

Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
——————
Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

In TASK of the second application one Activity, root – MainActivity, top – MainActivity. The pressure of the former did not change.

Press twice Start: C opens, then D.

They both fell into the throes of the second addition. press Info:

Count: 3
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD
——————
Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

In TASK of the second activity 3 activity, root – MainActivity, upper – D.

The pitch of the first program has not changed. The ActivityC and AtivityD instances are created in two different TASKs.

This is standard Activity behavior in Tasco. Using these two applications, we will now test non-standard behavior.

For startup convenience, we create application shortcuts on the desktop.

Let’s clear the bowls. To do this, call the program from the list of recent calls or via a shortcut and press Back until all its Activity is closed. Let’s do this for both of our applications. It is advisable to do this before every example.

launchMode

Let’s start with launchMode. This is the Activity attribute. It can take on values: standard, singleTop, singleTask, singleInstance.

standard

standard is a default behavior, it’s clear here we just looked at it.

singleTop

If we call Activity, and it is already in the top of the current Task, then a new instance of this Activity will not be created. We will get to the old one and get a call to the onNewIntent method.

Open the first application, click three times StartTo be in D. At this point, the contents of the tag: A-B-C-D.

Now in D we press again Start. Another copy of D. We open Info and look at the log:

Count: 5
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

Another D was added to the top of the bar. That is, the bar’s contents are now A-B-C-D-D. ActivityD duplicate. And if we continue to press StartYes, new instances of D will be created. We open the manifest and change the launchMode property in D to singleTop.

Save, launch the first application. We press three times Start, Get to D. Click again Start, It is immediately visible that the new Activity has not opened. press Info and look at the log:

Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

The system detects that the top of the drawer is already located ActivityD, which we want to open, and leaves it to us instead of creating new instances. All further clicks Start will give the same result. Because no new ActivityD is created, its onCreate method is not called. In this case, the onNewIntent method will be called.

Let’s break into theory, we need to clarify one point. Activity has an affinity attribute. By default, it is equal to the application package. The string also has an affinity attribute, and when creating a string, this attribute places the affinity value of the root Activity.

That is, when we launch the first application, we have the affinity = = default.startandroid.develop.p1161mngtasks1 created under it. When the second one is started, the newly created claim has affinity = ru.startandroid.develop.p1162mngtasks2.

Thus, for Activity it is possible to adopt the notion of “own” action – when affinity action and Activity coincide. Later in the lesson, we will still work with this attribute. In the meantime, we just need to understand what our “squeeze” is.

singleTask

Instead of creating an instance in the current TASK called Activity, a search for another TASK will be performed, and if “your” prompt is found, then Activity will be created in it. If it already exists in its TASK, then it will be displayed, and all Activity above it in TASCO will be closed. If you do not find your task, it will be created and Activity will be rooted there. Let’s look at the examples.

Let’s see how ActivityC behaves when called from the second application, if it is already open in the first one.

Let’s change ActivityM’s launchMode property to singleTask in the ActivityC. Save, launch the first application.

press StartUntil we get into D. We press Info and look at the log:

Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

As always, C was created in its TASK.

Now we use the second application. Let me remind you that the MainCctivity call of the first application is just there with MainActivity. As we saw in the beginning of the lesson, with the standard ActivityC activity, it simply adds to the second application’s plugin. Let’s see what happens now. Turn (home button) the first application, launch the second.

press Info and look at the log:

Count: 1

Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
——————
Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

In TASK of the second application only MainActivity. In TASK of the first A-B-C-D.

press Start in the second appendix, ActivityC opens:

press Info and look at the log:

Count: 3

Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
——————
Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity

The behavior is much different from standard behavior. Instead of creating an instance of ActivityC in Tasco of the second application, the system found it in its native TASK of the first application, and opened it, thus closing all Activity above it in Tasco (ActivityD in our case).

Clean the bar with the Back button.

Let’s see what happens if ActivityC is not running in its TASK. We launch the first application, we don’t push anything in it, we stay in ActivityA. We turn first, launch the second application and click Start. press Info, Look at the log:

Count: 2

Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
——————
Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity

ActivityC has come to its senses and is in it.

And what if your claim isn’t created yet? Clean the bar with the Back button. We launch the second application, click Info:

Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity

The first application’s drag is missing. Only the second’s sorrow.

press Start, press Info:

Count: 1
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
——————
Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity

Your ActivityC Task Created.

singleInstance

singleInstance – Activity is always one in your TASK. That is, if you call ActivityC, a new separate action will be created under it. And no matter – a call came from ActivityB (first application task) or MainActivity (second task). If, from ActivityC, further call ActivityD, then D looks for his action (for example, with previously opened A and B) and will be created in it, and if he does not find then he will create a new one.

If you wish, try applying this mode to ActivityC and test it yourself.

After all the tests, be sure to clear the launchMode property for ActivityC and ActivityD in the manifest for further examples to work correctly. And clean the grips.

Flags for Intent

Flags are applied to Intent using the addFlags method. These flags will affect the activity called Activity.

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_NEW_TASK – Help states that this flag is similar to the singleTask value in launchMode. For some reason, I am not quite so. Activity finds its toll, but does not look in this TASK itself, but simply creates a new Activity from above.

FLAG_ACTIVITY_SINGLE_TOP

FLAG_ACTIVITY_SINGLE_TOP is similar to singleTop for launchMode.

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_CLEAR_TOP – looks for Activity-created Taska. If it finds, it opens and everything above closes. We can say that this flag in combination with FLAG_ACTIVITY_NEW_TASK is a singleTask analog in launchMode.

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET – denotes the activity called. The next time you invoke a homepage, this Activity and all of the above will be closed. The mail program is an example. You open the email, click on the attachment, and go to an attachment viewer. Then you turn the application. The next time you start the mail, you will see the top Activity bar, that is, the attachment view. And if you invoke the attachment view with the above flag, the next time you start mail, the attachment view will be closed and you will see an email.

Consider the example. In MainActivity of the second application, we rewrite the method onClick:

  public void onClick(View v) {
    startActivity(new Intent("mngtasks1_activity_c")
        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET));
  }

Let’s call ActivityC with this flag. Save, launch the second application. press Start, Opens ActivityC, click again StartActivityD opens. The main thing now is Main-C-D.

We call the application home by clicking Home. Then we launch it from the desktop. The system detects and opens it. On the MainActivity screen of the second application. Since ActivityC was launched with the above flag, it and all higher ActivityClosed were closed at program startup. In TASK now only MainActivity.

FLAG_ACTIVITY_NO_HISTORY

FLAG_ACTIVITY_NO_HISTORY – Activity will not be saved in Tasco. For example, we call B with such a flag from A. Then from B we call C. Now we press Back and get to A immediately because B is not preserved in Tasco.

FLAG_ACTIVITY_REORDER_TO_FRONT

FLAG_ACTIVITY_REORDER_TO_FRONT – If Activity is already in Tasco it will move to the top. For example, there is A-B-C-D. If D calls B with such a flag, then the pressure will become A-C-D-B.

A couple of flags that appeared with Level 11 API. Shared with FLAG_ACTIVITY_NEW_TASK.

FLAG_ACTIVITY_CLEAR_TASK

FLAG_ACTIVITY_CLEAR_TASK – The call for Activity will be cleared and Activity will be rooted in it. Let’s make an example.

In the MainActivity of the second application, we rewrite the onClick method:

  public void onClick(View v) {
    startActivity(new Intent("mngtasks1_activity_c").addFlags(
        Intent.FLAG_ACTIVITY_CLEAR_TASK).addFlags(
        Intent.FLAG_ACTIVITY_NEW_TASK));
  }

Let’s clear the bowls. We launch the first application, click three times Start, We get A-B-C-D. We launch the second application, click Start, ActivityC opens. press Info:

Count: 1
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
——————
Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity

Task of the first application has completely cleared, in it now only ActivityC.

FLAG_ACTIVITY_TASK_ON_HOME

FLAG_ACTIVITY_TASK_ON_HOME – Activity Call will be located immediately after Home. If you leave the new button with the Back button, you will not go to the previous button, but to Home. Let’s make an example.

In the MainActivity of the second application, we rewrite the onClick method:

  public void onClick(View v) {
    startActivity(new Intent("mngtasks1_activity_c").addFlags(
        Intent.FLAG_ACTIVITY_TASK_ON_HOME).addFlags(
        Intent.FLAG_ACTIVITY_NEW_TASK));
  }

Let’s clear the bowls. We are launching the second application. Click Start. The first application, ActivityC, opens. In normal mode, if you click Back now, we would be back in the throttle of the second application, in MainActivity. But we used the flag, so we press Back and we get to Home.

Affinities

taskAffinity

Let’s return to the affinity attribute. Its exact name is taskAffinity. As I said above, this attribute is used to define its activity drag. By default for each Activity, this attribute is equal to the application package. Tusk also takes the affinity value from the root Activity.

To better understand the principle, let’s take an example. We have the first application that uses affinity = start.startandroid.develop.p1161mngtasks1 when running. The second application uses affinity = en.startandroid.develop.p1162mngtasks2.

If we call ActivityC with the flag FLAG_ACTIVITY_NEW_TASK from the second application, ActivityC will look for its affinity = en.startandroid.develop.p1161mngtasks1. Will it find the first application and launch it, or if it does not yet, then it will create that application.

Let’s try for ActivityC to spell out some left affinity, for example: en.startandroid.develop.p1163testaffinity. In this case, the pressure of the first application will not suit him, and he will exactly create his own pressure. Let’s check it out. Prescribe affinity for ActivityC

Save and launch the first application, form the Task A-B-C-D. Let’s call the first application, launch the second one. press Info:

Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity |
——————
Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

press Start in the second appendix, ActivityC opened. press Info:

Count: 1
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC
——————
Count: 1
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
——————
Count: 4
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

Unfortunately, I do not know how to extract the affinity value for a saddle or its root Activity. But it is clear that ActivityC did not approach the first application (through various affinity), and it was separated into a separate application.

Clear the affinity attribute for ActivityC and clear the strings.

allowTaskReparenting

Activity has the allowTaskReparenting attribute. It lets you move Activity from backgrounds to active strings.

Let’s make an example.

Set this to ActivityD

In the second application, we rewrite onClick:

  public void onClick(View v) {
    startActivity(new Intent("mngtasks1_activity_c"));
  }

We save everything, launch the second application. We press Start, we get ActivityC, we press Start we get ActivityD.

We press Info:

Count: 3
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD

Main-C-D. We turn this claim. We launch the first application (from Eclipse since there were changes) and see ActivityD immediately. We press Info:

Count: 2
Root: ru.startandroid.develop.p1161mngtasks1 / .ActivityA
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityD
——————
Count: 2
Root: ru.startandroid.develop.p1162mngtasks2 / .MainActivity
Top: ru.startandroid.develop.p1161mngtasks1 / .ActivityC

ActivityD, for which we specified the attribute allowTaskReparenting, has moved from the background of the second application to its own application (the first application).

clearing the squeeze

If the user does not use the coiled squeeze for a long time, then the system clears it, except for the root Activity. The system behaves on the assumption that if the user does not return to the task for a long time, then this task is no longer interesting to him.

We can influence this behavior of the system. There are attributes for Activity:

alwaysRetainTaskState – if set to true for Activity, which is root in Tasko, then this action will retain its Activity even after a long time

clearTaskOnLaunch – if set to true for Activity, which is root in Tasko, then every time the task is cleaned. And, it is at startup, and not at the choice of a tank from the list of the last.

finishOnTaskLaunch – similar to clearTaskOnLaunch but valid for a specific Activity. For example, let’s include this attribute for ActivityC. Let’s launch the first application and form a pressure A-B-C-D. Let’s turn it down and run the first application again, get A-B-D. ActivityC closed on new startup.

Great material turned out. At once all this in the head, of course, will not fit. But, I think, it will be quite useful as a help or memo when you need to set non-standard behavior for your application.

In the next lesson:

– create a simple widget
– Understand his Lifecycle




Discuss in the forum [31 replies]

Leave a Comment