Activity,Window 与 View

我们先来看一张 Activity 的调用流程

  1. Activity 调用 startActivity() 后最后会调用 attach() 方法

  2. 然后在 PolicyManager 实现一个 Ipolicy 接口,接着实现一个 Policy 对象

  3. 接着调用 makenewwindow(Context) 方法,返回一个 PhoneWindow 对象,而 PhoneWindow 是 Window 的子类

    PhoneWindow 中有一个 DecorView 的内部类,是所有应用窗口的根 View,即 View 的老大,直接控制 Activity 是否显示

  4. DecorView 里面有一个 LinearLayout,里面又有两个 FrameLayout 他们分别拿来装 ActionBarCustomView

    setContentView() 加载的布局就放到这个 CustomView

Activity,Task 和 Back Stack

我们的 APP 一般都是由多个 Activity 构成的,而在 Android 中给我们提供了一个 Task(任务) 的概念

Task 任务就是将多个相关的 Activity 收集起来,然后进行 Activity 的跳转与返回

当然,这个 Task 只是一个 frameworker 层的概念,而在 Android 中实现了 Task 的数据结构就是 Back Stack(回退堆栈)

这种 Back Stack(回退堆栈)后进先出(LIFO),常用操作入栈(push),出栈(pop),处于最顶部的叫栈顶,最底部叫栈底

Android 中, 当切换到新的 Activity,那么旧的 Activity 会被压入栈中,成为栈顶

而当用户点击 Back 键,栈顶的 Activity 出栈,紧随其后的 Activity 来到栈顶

我们使用官方一张图来看一下这个过程是如何操作的

应用程序中存在 A1,A2,A3 三个 activity,当用户在 Launcher 或 Home Screen 点击应用程序图标时, 启动主A1,接着 A1 开启 A2,A2 开启 A3,这时栈中有三个 Activity,并且这三个 Activity 默认在 同一个任务 ( Task ) 中,当用户按返回时,弹出 A3,栈中只剩 A1 和 A2 ,再按返回键, 弹出 A2,栈中只剩 A1,再继续按返回键,弹出 A1,任务被移除,即程序退出

接着在官方文档中又看到了另外两个图

然后还有这段解释

Task 是 Activity 的集合,是一个概念,实际使用的 Back Stack 来存储 Activity,可以有多个 Task,但是同一时刻只有一个栈在最前面,其它的都在后台!那栈是如何产生的呢?

当我们通过主屏幕,点击图标打开一个新的 App,此时会创建一个新的 Task

举个例子:我们通过点击通信录 APP 的图标打开 APP,这个时候会新建一个栈 1,然后开始把新产生的 Activity 添加进来,可能我们在通讯录的 APP 中打开了短信 APP 的页面,但是此时不会新建一个栈,而是继续添加到栈 1 中,这是 Android 推崇一种用户体验方式,即不同应用程序之间的切换能使用户感觉就像是同一个应用程序, 很连贯的用户体验,官方称其为 seamless (无缝衔接)

这个时候假如我们点击 Home 键,回到主屏幕,此时栈 1 进入后台,我们可能有下述两种操作

  1. 点击菜单键 (正方形那个按钮),点击打开刚刚的程序,然后栈 1 又回到前台了

    又或者我们点击主屏幕上通信录的图标,打开 APP,此时也不会创建新的栈,栈 1 回到前台

  2. 如果此时我们点击另一个图标打开一个新的 APP,那么此时则会创建一个新的栈 2,栈 2 就会到前台, 而栈 1 继续呆在后台

  3. ... 以此类推

Task 的管理

如上面所述,Android 会将新成功启动的 Activity 添加到同一个 Task 中并且按照以 "先进先出" 方式管理多个 Task 和 Back Stack

用户就无需去担心 Activites 如何与 Task 任务进行交互又或者它们是如何存在于 Back Stack 中

或许,你想改变这种正常的管理方式。比如,你希望你的某个 Activity 能够在一个新的 Task 中进行管理; 或者你只想对某个 Activity 进行实例化,又或者你想在用户离开任务时清理 Task 中除了根 Activity 所有 Activities

你可以做这些事或者更多,只需要通过修改 AndroidManifest.xml 中 <activity> 的相关属性值或者在代码中通过传递特殊标识的 Intent 给 startActivity()就可以轻松的实现对 Actvitiy 的管理了

<activity> 中我们可以使用的属性如下

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

能用的主要的 Intent 标志有

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

taskAffinity 和 allowTaskReparenting

默认情况下,一个应用程序中的 所有 activity 都有一个 Affinity,这让它们属于同一个 Task

可以理解为是否处于同一个 Task 的标志,不过,每个 Activity 可以通过

<activity> 中的 taskAffinity 属性设置单独的 Affinity

不同应用程序中的 Activity 可以共享同一个 Affinity,同一个应用程序中的不同 Activity 也可以设置成不同的 Affinity

Affinity 属性在两种情况下起作用

  1. 当启动 activity 的 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 标记

    当传递给 startActivity() 的 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 标记时,系统会为需要启动的 Activity 寻找与当前 Activity 不同 Task

    如果要启动的 Activity 的 Affinity 属性与当前所有的 Task 的 Affinity 属性都不相同,系统会新建一个带那个 Affinity 属性的 Task,并将要启动的 Activity 压到新建的 Task 栈中;否则将 Activity 压入那个 Affinity 属性相同的栈中

  2. allowTaskReparenting 属性设置为 true

    如果一个 activity 的 allowTaskReparenting 属性为 true, 那么它可以从一个 Task (Task1) 移到另外一个有相同 Affinity 的 Task(Task2) 中 (Task2带到前台时)

    如果一个 .apk 文件从用户角度来看包含了多个 "应用程序",你可能需要对那些 Activity 赋不同的 Affinity 值

launchMode

四个可选值,分别是:

  1. standard (默认)
  2. singleTop
  3. singleTask
  4. singleInstance

清空栈

当用户长时间离开Task (当前 task 被转移到后台) 时,系统会清除 task 中栈底 Activity 外的所有 Activity

当用户返回到 Task 时,只留下那个 task 最初始的 Activity 了

我们可以通过修改下面这些属性来改变这种行为

  1. alwaysRetainTaskState

    如果栈底 Activity 的这个属性被设置为 true,上述的情况就不会发生

    Task 中的所有 activity 将被长时间保存

  2. clearTaskOnLaunch

    如果栈底 activity 的这个属性被设置为 true,一旦用户离开 Task,则 Task 栈中的 Activity 将被清空到只剩下栈底 activity

    这种情况刚好与 alwaysRetainTaskState 相反

    即使用户只是短暂地离开,task 也会返回到初始状态 ( 只剩下栈底 acitivty)

  3. finishOnTaskLaunch

    与 clearTaskOnLaunch 相似,但它只对单独的 activity 操作,而不是整个Task

    它可以结束任何 Activity,包括栈底的 Activity

    当它设置为 true 时,当前的 Activity 只在当前会话期间作为 Task 的一部分存在,

    当用户退出 Activity 再返回时,它将不存在

Activity 的四种加载模式

Android Activity 有四种加载模式

  1. standard (默认)
  2. singleTop
  3. singleTask
  4. singleInstance

四种加载模式的详细介绍,可以访问

  1. Activity启动模式图文详解:standard, singleTop, singleTask 以及 singleInstance

  2. Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance

  3. Android中 Activity 四种启动模式和 taskAffinity 属性详解

standard 模式

标准启动模式,也是 activity 的默认启动模式

在这种模式下启动的 activity 可以被多次实例化,即在同一个任务中可以存在多个 activity 的实例,每个实例都会处理一个 Intent 对象

如果 Activity A 的启动模式为 standard,并且 A 已经启动,在 A 中再次启动 Activity A,即调用 startActivity(new Intent(this,A.class)),会在 A 的上面再次启动一个 A 的实例,即当前的桟中的状态为 A --> A

singleTop 模式

如果一个以 singleTop 模式启动的 Activity 的实例已经存在于任务栈的栈顶, 那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例, 并且会调用该实例的 onNewIntent() 方法将 Intent 对象传递到这个实例中

比如,如果 A 的启动模式为 singleTop,并且 A 的一个实例已经存在于栈顶中, 那么再调用 startActivity(new Intent(this,A.class)) 启动 A 时, 不会再次创建 A 的实例,而是重用原来的实例,并且调用原来实例的 onNewIntent() 方法

这时任务栈中还是这有一个 A 的实例

如果以 singleTop 模式启动的 activity 的一个实例 已经存在与任务栈中,但是不在栈顶,那么它的行为和 standard 模式相同,也会创建多个实例

singleTask 模式

singleTask 模式只允许在系统中有一个 Activity 实例

如果系统中已经有了一个实例,持有这个实例的任务将移动到顶部,同时 intent 将被通过 onNewIntent()发送

如果没有,则会创建一个新的 Activity 并置放在合适的任务中

官方文档中提到的一个问题

系统会创建一个新的任务,并将这个Activity实例化为新任务的根部(root) 这个则需要我们对taskAffinity进行设置了,使用taskAffinity后的解雇:

singleInstance 模式

singleInstance 模式保证了系统无论从哪个 Task 启动 Activity 都只会创建一个 Activity 实例,并将它加入新的 Task 栈顶

也就是说被该实例启动的其它 activity 会自动运行于另一个 Task 中

当再次启动该 activity 的实例时,会重用已存在的任务和实例。并且会调用这个实例 的 onNewIntent() 方法,将 Intent 实例传递到该实例中

和 singleTask 相同,同一时刻在系统中只会存在一个这样的 Activity 实例

开源中国客户端 Activity 管理类

开源中国 Android 客户端时开源的,我们发现它的 Activity 管理类非常好用

下面就贴上这个 Activity 管理类的全部代码

package net.oschina.app;

import java.util.Stack;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;


public class AppManager {

    private static Stack<Activity> activityStack;
    private static AppManager instance;

    private AppManager(){}
    /**
     * 单一实例
     */
    public static AppManager getAppManager(){
        if(instance==null){
            instance=new AppManager();
        }
        return instance;
    }
    /**
     * 添加Activity到堆栈
     */
    public void addActivity(Activity activity){
        if(activityStack==null){
            activityStack=new Stack<Activity>();
        }
        activityStack.add(activity);
    }
    /**
     * 获取当前Activity(堆栈中最后一个压入的)
     */
    public Activity currentActivity(){
        Activity activity=activityStack.lastElement();
        return activity;
    }
    /**
     * 结束当前Activity(堆栈中最后一个压入的)
     */
    public void finishActivity(){
        Activity activity=activityStack.lastElement();
        finishActivity(activity);
    }
    /**
     * 结束指定的Activity
     */
    public void finishActivity(Activity activity){
        if(activity!=null){
            activityStack.remove(activity);
            activity.finish();
            activity=null;
        }
    }
    /**
     * 结束指定类名的Activity
     */
    public void finishActivity(Class<?> cls){
        for (Activity activity : activityStack) {
            if(activity.getClass().equals(cls) ){
                finishActivity(activity);
            }
        }
    }
    /**
     * 结束所有Activity
     */
    public void finishAllActivity(){
        for (int i = 0, size = activityStack.size(); i < size; i++){
            if (null != activityStack.get(i)){
                activityStack.get(i).finish();
            }
        }
        activityStack.clear();
    }
    /**
     * 退出应用程序
     */
    public void AppExit(Context context) {
        try {
            finishAllActivity();
            ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            activityMgr.restartPackage(context.getPackageName());
            System.exit(0);
        } catch (Exception e) { }
    }
}

Task进行整体调度的相关操作

  1. 按 Home 键,将之前的 Task 切换到后台
  2. 长按 Home 键,会显示出最近执行过的 Task 列表
  3. 在Launcher或HomeScreen点击app图标,开启一个新Task,或者是将已有的Task调度到前台
  4. 启动 singleTask 模式的 Activity 时,会在系统中搜寻是否已经存在一个合适的Task

    • 若存在,则会将这个 Task 调度到前台以重用这个 Task

      • 如果这个 Task 中已经存在一个要启动的 Activity 的实例,则清除这个实例之上的所有 Activity,将这个实例显示给用户

      • 如果这个已存在的 Task 中不存在一个要启动的 Activity 的实例,则在这个 Task 的顶端启动一个实例

    • 若这个 Task 不存在,则会启动一个新的 Task,在这个新的 Task 中启动这个 singleTask 模式的 Activity 的一个实例

  5. 启动 singleInstance 的 Activity 时,会在系统中搜寻是否已经存在一个这个 Activity 的实例

    • 如果存在,会将这个实例所在的Task调度到前台,重用这个Activity的实例(该Task中只有这一个Activity)
    • 如果不存在,会开启一个新任务,并在这个新Task中启动这个singleInstance模式的Activity的一个实例

参考文档

  1. Tasks and Back Stack
  2. 理解android中Activity和Task的关系
  3. Activity启动模式图文详解:standard, singleTop, singleTask 以及 singleInstance
  4. Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance
  5. Android中Activity四种启动模式和taskAffinity属性详解
  6. Android的Activity和Tasks详解
  7. Activity的四种启动模式和onNewIntent()
  8. 译:Android任务和返回栈完全解析,细数那些你所不知道的细节

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.