В этом уроке:

- меняем поведение Activity в Task

 

Когда я в Уроке 25 говорил про Task, то решил, что рановато будет рассказывать про возможности изменения поведения Activity. Да и в хелпе можно найти такие слова: "The way Android manages tasks and the back stack (…) works great for most applications and you shouldn't have to worry about how your activities are associated with tasks or how they exist in the back stack". Т.е. дефолтное поведение вполне подходит для большинства случаев и нам не надо о нем задумываться. Но вполне может встретиться ситуация, когда надо будет на это поведение влиять. Поговорим о том, какими средствами можно это делать. За основу возьмем статью "Tasks and Back Stack" из хелпа.

Про механизм заполнения Task я не рассказываю, все есть в Уроке 25. Давайте только определимся с формулировками. Таск – это Task. Корень таска или корневое Activity таска – это первое Activity, помещенное в таск. Топ таска или верхнее Activity в таске – это последнее Activity, добавленное в таск.

 

Для показательных примеров по теме нам понадобятся два приложения. 

Первое состоит из 4 Activity: A,B,C,D. Они последовательно будут вызывать друг друга: A -> B -> C -> D.  И D будет вызывать само себя: D -> D. 
Второе приложение – это только одно Activity, оно будет вызывать C из первого приложения.

 

Создадим проект:

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

 

Добавим строки в strings.xml:

<string name="start">Start</string>
<string name="info">Info</string>

 

Layout-файл main.xml:

<?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:id="@+id/LinearLayout1"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical"
	tools:context=".MainActivity">
	<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClick"
		android:text="@string/start">
	</Button>
	<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onInfoClick"
		android:text="@string/info">
	</Button>
</LinearLayout>

Две кнопки. Вызов Activity и вывод информации в лог.

 

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<ActivityManager.RunningTaskInfo> 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);
}

В onCreate мы в заголовок помещаем название приложения и имя класса Activity, чтобы на экране видеть какое Activity какого приложения сейчас активно.

В onInfoClick используем ActivityManager. Метод getRunningTasks вернет нам список текущих работающих тасков. Последние активные таски будут в начале списка. На всякий случай будем получать первые 10. Среди них точно окажутся два наших. Далее мы получаем информацию о таске: кол-во Activity (numActivities), корневое Activity (baseActivity), верхнее Activity (topActivity). И отсеиваем свои таски по имени пакета корневого Activity.

Этот класс будет предком для 4-х следующих. Он содержит абстрактный метод onClick, который нам надо будет реализовать в наследниках. Этот метод будет вызываться по нажатию на кнопку Start.

 

Создаем 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));
  }
}

 

Все Activity наследуют MainActivity. А, значит, будут выводить в заголовок инфу о себе, а по нажатию на кнопку Info – показывать в логе текущую инфу по таскам.

Ну а метод onClick – это вызов следующего Activity. ActivityD будет вызывать себя же.

 

Прописываем в манифесте новые Activity. ActivityA назначим стартовым вместо MainActivity. ActivityC содержит фильтр с action = mngtasks1_activity_c. MainActivity удаляем из манифеста.

И добавьте права на получение данных о тасках: android.permission.GET_TASKS.

 

Первое приложение готово. Создаем второе. Оно полностью аналогичное, только Activity будет одно.

Создадим проект:

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

 

Добавим строки в strings.xml:

<string name="start">Start</string>
<string name="info">Info</string>

 

Layout-файл main.xml:

<?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:id="@+id/LinearLayout1"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical"
	tools:context=".MainActivity">
	<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onClick"
		android:text="@string/start">
	</Button>
	<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:onClick="onInfoClick"
		android:text="@string/info">
	</Button>
</LinearLayout>

 

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<ActivityManager.RunningTaskInfo> 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());
      }
    }
  }

}

В onClick идет вызов ActivityC из первого приложения.

В манифесте не забудьте про android.permission.GET_TASKS.

 

Все сохраним и запустим первое приложение.

В заголовке видим имя приложения и Activity. Жмем Info и смотрим лог:

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

В таске первого приложения одно Activity, корневое - ActivityA, верхнее - ActivityA.

 

Давайте нажмем Start три раза, чтобы пройти B,C и оказаться в D

Жмем Info:

Count: 4

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

Теперь в таске 4 Activity, корневое - A, верхнее – D. Между ними B и C.

 

Запустим второе приложение.

Жмем 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

В таске второго приложения одно Activity, корневое - MainActivity, верхнее - MainActivity. Таск первого не менялся.

 

Нажмем два раза Start: откроется C, затем D.

Они оба легли в таск второго приложения. Жмем 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

В таске второго приложения 3 Activity, корневое - MainActivity, верхнее - D.

Таск первого приложения не поменялся. Экземпляры ActivityC и AtivityD созданы в двух разных тасках.

 

Это стандартное поведение Activity в тасках. Используя эти два приложения, мы сейчас будем тестировать нестандартное поведение.

 

Для удобства запуска создадим ярлыки приложений на рабочем столе.

 

Очистим таски. Для этого надо вызвать приложение из списка последних вызываемых или через ярлык и нажимать Назад, пока все его Activity не закроются. Проделаем это для наших обоих приложений. Это желательно проделывать перед каждым примером.

 

launchMode

Начнем с launchMode. Это атрибут Activity. Может принимать значения: standard, singleTop, singleTask, singleInstance.

 

standard

standard – это дефолтное поведение, тут все ясно, его мы только что посмотрели.

 

singleTop

Если мы вызываем Activity, и оно уже находится в топе текущего Task-а, то новый экземпляр этого Activity создан не будет. Мы попадем в старый и получим там вызов метода onNewIntent.

Откроем первое приложение, нажмем три раза Start, чтобы оказаться в D. На этот момент содержимое таска: A-B-C-D.

Теперь в D снова нажмем Start. Открылся еще один экземпляр D. Жмем Info и смотрим лог:

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

В верх таска добавилось еще одно D. Т.е. содержимое таска сейчас таково: A-B-C-D-D. ActivityD продублировалось. И если мы продолжим жать Start, так и будут создаваться новые экземпляры D. Открываем манифест и меняем у D свойство launchMode на singleTop.

Сохраняем, запускаем первое приложение. Жмем три раза Start, попадаем в D. Жмем еще раз Start, сразу визуально видно, что новое Activity не открывалось. Жмем Info и смотрим лог:

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

Система определяет, что наверху таска уже расположено ActivityD, которое мы хотим открыть, и оставляет нам его вместо создания новых экземпляров. Все последующие нажатия Start будут давать тот же результат. Т.к. новое ActivityD не создается, то и его метод onCreate не вызывается. В этом случае будет вызываться метод onNewIntent.

 

 

Прервемся на теорию, надо прояснить один момент.  Activity имеет атрибут affinity. По умолчанию он равен пакету (package) приложения. У таска тоже есть атрибут affinity и при создании таска в этот атрибут помещается значение из affinity корневого Activity.

Т.е. когда мы запускаем первое приложение, то у созданного под него таска affinity = ru.startandroid.develop.p1161mngtasks1. Когда запускаем второе, новый созданный таск имеет affinity = ru.startandroid.develop.p1162mngtasks2.

Таким образом, для Activity можно принять такое понятие, как «свой» таск - когда affinity таска и Activity совпадают. Дальше в уроке мы еще поработаем с этим атрибутом. А пока нам надо просто понимать что такое «свой» таск.

 

singleTask

Вместо создания в текущем таске экземпляра вызываемого Activity будет выполнен поиск по другим таскам, и если будет найден «свой» таск, то Activity будет создано именно в нем. Если оно уже существует в своем таске, то оно и отобразится, а все Activity выше его в таске будут закрыты. Если же свой таск не найдется, то он будет создан и Activity станет там корневым. Посмотрим на примерах.

Проверим, как поведет себя ActivityC при вызове из второго приложения, если оно уже открыто в первом.

Давайте для ActivityC поменяем в манифесте свойство launchMode на singleTask. Сохраняем, запускаем первое приложение.

 Жмем Start, пока не окажемся в D. Жмем Info и смотрим лог:

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

Все как обычно, С было создано в своем таске.

 

Теперь используем второе приложение. Напомню, что там из MainActivity как раз идет вызов ActivityC первого приложения. Как мы видели в начале урока, при стандартном поведении ActivityC просто добавляется в таск второго приложения. Посмотрим, что получится сейчас. Сворачиваем (кнопка Домой) первое приложение, запускаем второе.

Жмем 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

В таске второго приложения только MainActivity. В таске первого A-B-C-D.

 

Жмем Start во втором приложении, открывается ActivityC:

Жмем Info и смотрим лог:

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

Поведение значительно отличается от стандартного. Вместо того, чтобы создать экземпляр ActivityC в таске второго приложения, система нашла его в его родном таске первого приложения, и открыла, при этом закрыв все Activity, находящиеся выше его в таске (ActivityD в нашем случае).

Очистим таски кнопкой Назад.

 

Посмотрим, что будет, если ActivityC пока не запущено в своем таске. Запускаем первое приложение, ничего не жмем в нем, остаемся в ActivityA. Сворачиваем первое, запускаем второе приложение и жмем Start. Жмем Info, смотрим лог:

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 нашло свой таск и создалось в нем.

 

А что будет, если свой таск пока не создан? Очищаем таски кнопкой Назад. Запускаем второе приложение, жмем Info:

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

Таска первого приложения нет. Только таск второго.

 

Жмем Start, жмем 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

Свой таск для ActivityC cоздался.

 

singleInstance

singleInstance – Activity всегда одно в своем таске. Т.е. если вызывать ActivityC, то будет создан новый отдельный таск под него. И неважно - из ActivityB (таск первого приложения) или из MainActivity (таск второго) пришел вызов. Если же из ActivityC дальше вызвать ActivityD, то D поищет свой таск (например, с ранее открытыми A и B) и создастся в нем, а если не найдет то создаст новый.

Если есть желание, попробуйте применить этот режим для ActivityC и потестить самостоятельно.

 

После всех тестов не забудьте очистить в манифесте свойство launchMode для ActivityС и ActivityD, чтобы корректно работали дальнейшие примеры. И очистите таски.

 

 

 

Флаги для Intent

Флаги применяются к Intent с помощью метода addFlags. Эти флаги повлияют на поведение вызываемого Activity.

 

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_NEW_TASK - в хелпе пишут, что этот флаг аналогичен значению singleTask в launchMode. У меня почему-то не совсем так. Activity находит свой таск, но не ищет в этом таске себя, а просто создает новое Activity сверху.

 

FLAG_ACTIVITY_SINGLE_TOP

FLAG_ACTIVITY_SINGLE_TOP – аналогичен значению singleTop для launchMode.

 

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_CLEAR_TOP – ищет в таске создаваемое Activity. Если находит, то открывает, а все, что выше – закрывает. Можно сказать, что этот флаг в комбинации с FLAG_ACTIVITY_NEW_TASK является аналогом singleTask в launchMode.

 

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET –  помечает вызванное Activity. При следующем вызове таска из Home, это Activity и все вышерасположенные будут закрыты. В качестве примера можно привести почтовую программу. Вы открываете письмо, щелкаете на вложение и переходите в некую программу просмотра вложения. Затем сворачиваете приложение. Когда вы в следующий раз запустите почту, вы увидите верхнее Activity таска, т.е. просмотр вложения. А если вызывать просмотр вложения с вышеуказанным флагом, то при следующем запуске почты просмотр вложения будет закрыт и вы увидите письмо.

 

Рассмотрим на примере.  В MainActivity второго приложения перепишем метод onClick:

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

Будем вызывать ActivityC с этим флагом. Сохраняем, запускаем второе приложение. Жмем Start, открывается ActivityC, жмем еще раз Start, открывается ActivityD. Таск сейчас такой: Main-C-D.

Свернем приложение, нажав Home. Далее запускаем его с рабочего стола. Система находит таск и открывает его. На экране MainActivity второго приложения. Т.к. ActivityC было запущено с вышеуказанным флагом, оно и все вышестоящие Activity были закрыты при запуске приложения. В таске теперь только MainActivity.

 

FLAG_ACTIVITY_NO_HISTORY

FLAG_ACTIVITY_NO_HISTORY – Activity не будет сохранено в таске. Например, мы вызываем B с таким флагом из A. Далее из B вызываем C. Теперь жмем Назад и попадаем сразу в A, т.к. B не сохранилось в таске.

 

FLAG_ACTIVITY_REORDER_TO_FRONT

FLAG_ACTIVITY_REORDER_TO_FRONT – если Activity уже есть в таске оно переместится в топ. Например, есть таск A-B-C-D. Если D вызывает B с таким флагом, то таск станет таким A-C-D-B.

 

Пара флагов, появившихся с API Level 11. Используются совместно с FLAG_ACTIVITY_NEW_TASK.

FLAG_ACTIVITY_CLEAR_TASK

FLAG_ACTIVITY_CLEAR_TASK – таск для вызываемого Activity будет очищен, а вызываемое Activity станет в нем корневым. Сделаем пример.

В MainActivity второго приложения перепишем метод onClick:

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

Очистим таски. Запускаем первое приложение, жмем три раза Start, получаем A-B-C-D. Запускаем второе приложение, жмем Start, открывается ActivityC. Жмем 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

Таск первого приложения полностью очистился, в нем теперь только ActivityC.

 

FLAG_ACTIVITY_TASK_ON_HOME

FLAG_ACTIVITY_TASK_ON_HOME – таск для вызываемого Activity будет располагаться сразу после Home. Если из этого нового таска выходить кнопкой Назад, то попадешь не в предыдущий таск, а в Home. Сделаем пример.

В MainActivity второго приложения перепишем метод onClick:

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

Очистим таски. Запускаем второе приложение. Жмем Start. Открывается таск первого приложения, ActivityC. В нормальном режиме, если нажать сейчас Назад, то мы бы вернулись в таск второго приложения, в MainActivity. Но мы использовали флаг, поэтому нажимаем Назад и попадаем в Home.

 

Affinities

taskAffinity

Вернемся к атрибуту affinity. Его точное название  – taskAffinity. Как я уже рассказал выше, этот атрибут используется для определения своего таска для Activity. По умолчанию для каждого Activity этот атрибут равен пакету приложения. Таск же принимает значение affinity от корневого Activity.

Чтобы лучше понять принцип, давайте реализуем пример. У нас есть первое приложение, которое при запуске использует таск с affinity = ru.startandroid.develop.p1161mngtasks1. Второе приложение использует таск с affinity = ru.startandroid.develop.p1162mngtasks2.

Если мы из второго приложения вызовем ActivityC с флагом FLAG_ACTIVITY_NEW_TASK, то ActivityC будет искать свой таск с affinity = ru.startandroid.develop.p1161mngtasks1. Найдет таск первого приложения и запустится там, либо, если такого таска пока нет, то само создаст такой таск.

Попробуем для ActivityC прописать какой-нибудь левый affinity, например: ru.startandroid.develop.p1163testaffinity. В этом случае таск первого приложения ему не подойдет, и он точно создаст свой таск. Давайте проверим. Пропишите affinity для ActivityC

Сохраняем и запускаем первое приложение, формируем таск A-B-C-D. Свернем первое приложение, запустим второе. Жмем 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

 

Жмем Start во втором приложении, открылось ActivityC. Жмем 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

К сожалению, я не знаю, как вытащить значение affinity для таска или его корневого Activity. Но четко видно, что для ActivityC не подошел таск первого приложения (из-за разных affinity), и оно выделилось в отдельный таск.

Очистите атрибут affinity для ActivityC и очистите таски.

 

allowTaskReparenting

У Activity есть атрибут allowTaskReparenting.Он позволяет перекидывать Activity из фоновых в активные таски.

 

Сделаем пример.

Установим этот параметр для ActivityD

 

Во втором приложении перепишем onClick:

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

Все сохраняем, запускаем второе приложение. Жмем Start, получаем ActivityC, жмем Start получаем ActivityD.

Жмем Info:

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

 

Таск Main-C-D. Сворачиваем этот таск. Запускаем первое приложение (из Eclipse ,т.к. были изменения) и видим сразу ActivityD. Жмем 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, для которого мы указали атрибут allowTaskReparenting, перешло из фонового таска второго приложения в запущенный свой таск (первого приложения).

 

Очистка таска

Если пользователь долгое время не использует свернутый таск, то система его чистит, за исключением корневого Activity. Система ведет себя так исходя из предположения, что, если пользователь долго не возвращался к выполняемой задаче, то эта задача ему более не интересна.

Мы можем повлиять на это поведение системы. Существуют атрибуты для Activity:

alwaysRetainTaskState – если установлено в true для Activity, которое является корневым в таске, то этот таск сохранит свои Activity даже спустя долгое время

clearTaskOnLaunch – если установлено в true для Activity, которое является корневым в таске, то при каждом запуске таск очищается. Причем, именно при запуске, а не при выборе таска из списка последних.

finishOnTaskLaunch – аналогичен clearTaskOnLaunch, но действует для конкретного Activity. Например, включим этот атрибут для ActivityC. Запустим первое приложение и сформируем таск A-B-C-D. Свернем его и запустим снова первое приложение, получим таск A-B-D. ActivityC при новом запуске закрылось.

 

Большой получился материал. Сразу все это в голове, конечно, не уложится. Но, думаю, вполне пригодится как справка или памятка, когда понадобится задать нестандартное поведение для своего приложения.

 

На следующем уроке:

- создаем простой виджет
- разбираемся в его Lifecycle


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование



Похожие статьи


Последние статьи



Language

Система Orphus

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Страница в Facebook

Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Яндекс.Метрика