14 мая 2014 г.

Задачи и обратный стек (Tasks and back stack). Часть 8 (практика)

За время отпуска, во время долгих перелетов, мои приложения AP0003 и AP0004 изрядно мутировали и перешли на новый виток эволюции. Во первых, для лучшего понимания я добавил вывод ID задачи в скобках в каждом методе: onCreate(), onStart и т.д. Во вторых, третьих, четвертых и пятых сделал кучу изменений. О них по порядку.

В приложении AP0003 так и осталось четыре активности A, B, C и D. Но их функционал и внешний вид несколько поменялся. Активность C имеет launcMode=”singleTask”.  Активность D – launchMode="singleTop". Кроме того в Активности D была добавлена кнопка запуска Акитвности В в приложении AP0004.

В приложении AP0004 добавилась еще одна Активность Е со свойством launchMode="singleInstance".

Кроме того, я дописал код, который корректирует работу счетчика Активностей в задаче при убивании Активности D в приложении AP0003. Ну и еще много чего дописал.

Теперь к практике. Начнем с того момента где остановились в прошлый раз. Запускаем приложение AP0004.

NT0001

Стрелочками отмечено то, на что следует обратить внимание. Теперь смотрим логи.

NT0002

Теперь в скобках, как я и говорил, выводится номер задачи к которой принадлежит Активность.

Нажмем кнопку Info

NT0003

Видим, что в задаче 9 сейчас одна Активность А. Тут все просто.

Жмем кнопку Start D AP0003

NT0004

Теперь видим, что в задаче 9 две Активности: A и D. Активность А принадлежит приложению
AP0004, а Активность D – приложению AP0003. НО ОНИ В ОДНОЙ ЗАДАЧЕ 9.

Посмотрим логи.

NT0005

Активность D была запущена в задаче 9 но в процессе приложения AP0003 с ID=1763. А у Активности А из которой она была запущена процесс ID=1813.

Жмем кнопку Info и смотрим логи.

NT0006

И логов еще раз, подробнее, видно, что сейчас в нашей задаче 9 есть две Активности A и D. Активность D сейчас у нас на вершине стека. В прочем тут пока все как в прошлой практике.

Теперь введем какой-нибудь текст в Активности D, чтобы видеть будет ли он сохранен.

NT0007

И жмем кнопку запустить Активность B AP0004.

NT0008

Смотрим логи

NT0009

Видим что Активность В была запущена в процессе приложения AP0004 с ID=1813. Жмем кнопку Info

NT0010

И так, сейчас в нашей задаче 9 есть три Активности A-D-B. Активность D принадлежит приложению AP0003, а Активности A и B – приложению AP0004. Активность B сейчас на вершине стека. Так же стоит отметить что нажатие кнопки Info отрабатывается в процессе текущей Активности, что собственно логично. И еще запоминаем что процесс Активности D имеет значение 1763. Сейчас мы его будем убивать.

Жмем кнопку HOME и запускаем снова приложение ProcessView. Находим процесс 1763 приложения AP0003.

NT0011

И убиваем его

NT0012

Возвращаемся к нашему приложению AP0004

NT0008

Теперь жмем кнопку ОБРАТНО, должна открыться Активность D, процесс которой мы убили.

NT0013

Счетчик теперь работает правильно, благодаря изменениям в коде, которые я таки сподобился написать. Кроме того текст введенный нами тоже сохранился не смотря на то, что процесс Активности D был убит. Это говорит о том что объект Bundle, в котором сохраняются данные, привязан к задаче, а не процессу.

Теперь посмотрим логи

NT0014

В начале данного скрина первые четыре строки мы видим отработку нажатия кнопки HOME и возврата к приложению. Затем в пятой строке логов нажатие кнопки ОБРАТНО. И происходи СОЗДАНИЕ Активности D. То есть срабатывает метод onCreate() вместо метода onRestart(), так как мы убили процесс Активности D. И создается Активность D уже в новом процессе с номером 1857, а старый был 1763, о чем нам собственно и докладывает наш код.

Но этот код работает только в том случае, если Активность D, запускается из другого приложения. Если же мы просто запустим приложение AP0003 и дойдем до Активности D, а затем убьем процесс приложения AP0003, то вместе с ним убьется и задача, а в месте с ней и объект Bundle.

Привету код Активности D приложения AP0003

package com.example.ap0003;

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;
import android.widget.TextView;

public class ActivityD extends Activity {

 final String TAG = "States";
 TextView tvTextLife;
 List<ActivityManager.RunningTaskInfo> list;
 ActivityManager am;
 Integer TotalActCount;
 Boolean FirstStart;
 Boolean NextAct;
 static final String SaveMyPID = "MY_OLD_PID";
 static final String SaveMyNextClick = "MY_NEXT_CLICK";
 Integer SavedPID;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.layout_d);
  setTitle(getResources().getString(R.string.app_name) + " | "
    + getLocalClassName() + " | TaskID: " + getTaskId());
  Log.d(TAG, "ActivityD: onCreate("+getTaskId()+")");
  // активность запущена впервые
  FirstStart = true;
  // кнопка запуска следующей Активности не нажималась
  NextAct = false;
  // определяем есть ли сохранненные данные
  if (savedInstanceState != null) {
   // Restore value of members from saved state
   Log.d(TAG, "ActivityD: onCreate("+getTaskId()+") NOT NULL: ");
   // сравниваем сохраненный и текущий PID
   SavedPID = savedInstanceState.getInt(SaveMyPID);
   if (SavedPID != android.os.Process.myPid()) {
    Log.d(TAG, "!!! Pocess AP0003 was killed !!!!");
    Log.d(TAG, "Old PID: " + SavedPID);
    Log.d(TAG, "NEW PID: " + android.os.Process.myPid());
    //коррекция счетчика если процесс приложения был убит
    FirstStart = false;
    //коррекция счетчика на запуск другой Активности
    if (savedInstanceState.getInt(SaveMyNextClick)==1){
     NextAct=true;
     Log.d(TAG, "!!! NextAct=true !!!");
    }

   }
  } else {
   // Probably initialize members with default values for a new
   // instance
   Log.d(TAG, "ActivityD: onCreate("+getTaskId()+") NULL");
  }
 }

 @Override
 protected void onStart() {
  super.onStart();
  Log.d(TAG, "ActivityD: onStart("+getTaskId()+")");
 }

 @Override
 protected void onResume() {
  super.onResume();
  Log.d(TAG, "ActivityD: onResume("+getTaskId()+")");
  // получаем список 10 последних задач
  am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
  list = am.getRunningTasks(10);
  // перебираем список задач и выбираем свою по TaskID
  for (RunningTaskInfo task : list) {
   if (task.id == getTaskId()) {
    // находим поле для вывода информации о количестве запущенных
    // Активностей
    tvTextLife = (TextView) findViewById(R.id.textActCountD);
    TotalActCount = task.numActivities;
    // коррекция счетчика для кнопки ДОМОЙ и ОБРАТНО
    if (NextAct == true & FirstStart == false)
     TotalActCount = TotalActCount - 1;
    // выводим количество Активностей в задаче
    tvTextLife.setText("Activites in task " + TotalActCount);
    // коррекция счетчика для кнопки ДОМОЙ
    NextAct = false;
   }
  }
 }

 @Override
 protected void onPause() {
  super.onPause();
  // флаг что активность уже была запущена
  FirstStart = false;
  // находим текстовое поле по его идентификатору
  tvTextLife = (TextView) findViewById(R.id.textStateActD);
  // присваиваем значение атрибуту Text для выбранного TextView
  tvTextLife.setText("Этот экземпляр ActivityD уже был запущен!");
  Log.d(TAG, "ActivityD: onPause("+getTaskId()+")");
 }

 @Override
 protected void onStop() {
  super.onStop();
  Log.d(TAG, "ActivityD: onStop("+getTaskId()+")");
 }

 @Override
 protected void onRestart() {
  super.onRestart();
  Log.d(TAG, "ActivityD: onRestart("+getTaskId()+")");
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  Log.d(TAG, "ActivityD: onDestroy("+getTaskId()+")");
 }

 @Override
 protected void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // сохраняем значение PID
  savedInstanceState.putInt(SaveMyPID, android.os.Process.myPid());
  Log.d(TAG, "ActivityD: onSave("+getTaskId()+") PID:" + android.os.Process.myPid());
  //сохраняем нажатие запуска другой Активности
  if (NextAct==true){
  savedInstanceState.putInt(SaveMyNextClick, 1);
  Log.d(TAG, "ActivityD: onSave("+getTaskId()+") NextAct=true");
  }
 }

 public void onClickStartA(View v) {
  Intent intent = new Intent(ActivityD.this, ActivityA.class);
  startActivity(intent);
  // кнопка запуска следующей Активности была нажата
  NextAct = true;
 }

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

 public void onClickStartB0004(View v) {
  startActivity(new Intent("AP0004_ActB"));
  NextAct = true;
 }

 public void onInfoClick(View v) {
  final String TAG = "States";
  // получаем список 10 последних задач

  list = am.getRunningTasks(10);
  // перебираем список задач и выбираем свои по имени пакетов
  // com.example.ap000
  for (RunningTaskInfo task : list) {
   if (task.baseActivity.flattenToShortString().startsWith(
     "com.example.ap000")) {
    // находим поле для вывода информации о количестве запущенных
    // Активностей

    Log.d(TAG, "------------------");
    Log.d(TAG, "TaskID: " + task.id);
    Log.d(TAG, "Num: " + task.numActivities);
    Log.d(TAG, "Base: " + task.baseActivity.flattenToShortString());
    Log.d(TAG, "Top: " + task.topActivity.flattenToShortString());
    Log.d(TAG, "Thread ID: " + android.os.Process.myTid());
    Log.d(TAG, "Process ID: " + android.os.Process.myPid());
    Log.d(TAG, "------------------");

   }
  }

 }

}

Комментариев нет:

Отправить комментарий