一般情况下,程序员很少会关心Task的运行,最近也是业务需求才多看了一些。主要内容来自这个PPT. 最近接手了分享的 Android SDK,里面存在太多的内存泄露,泄露点非常多,而且代码比较冗杂,改起来比较麻烦所以想了一个取巧的办法,在后台隐藏一个透明的Activity做载体,每次需要用到Activity的时候都把这个Activity供出来,同时把这个Activity做成 “单例” 这样即使泄露也只会泄露一个Activity。
基于上面的需求我的代理Activity需要有如下功能,
我是这样解决的,把Activity的启动模式设置为launchMode="singleInstance"
这样这个Activity就会唯一的存在于一个单独的Task中,然后利用 moveTaskToBack
方法把这个Task移到后台(注意如果这个Activity不是独立的Task而是和App的Task在一起,那么这个方法会把整个App移到后台)。在需要使用Activity的时候再把Activity挪到前台,但是要覆盖 finish
方法
public void finish(){
moveTaskToBack(true);
}
这样Activity就不会被回收,需要的时候再挪到前台。看似“聪明“的方案,实际上存在很多问题,所以有了下面的探索。
进入模拟器的shell(>adb shell),Android SDK 提供了 dumpsys activity
命令来打印Activity的状态。敲入这个命令会打印很多信息,也可以通过下面的命令打印需要的信息。
eg:
//正常 App 的所有Activity生存在一个Task中
Running activities (most recent first):
TaskRecord{2c048081 #4 A=com.example.managetask U=0 sz=3}
Run #2: ActivityRecord{3a7134b0 u0 com.example.managetask/.ActivityWorld t4}
Run #1: ActivityRecord{257fac0a u0 com.example.managetask/.ActivityHello t4}
Run #0: ActivityRecord{6f882dc u0 com.example.managetask/.MainActivity t4}
经常使用的方法是在Intent中添加 FLAG_ACTIVITY_NEW_TASK
标签。但是真实的情况是这样是无法启动新的Task的,还需要给对应的Activity添加taskAffinity属性才能把这个Activity启动到新的额Task中。
还有两种方法也能启动新的Task,给Activity配置launchMode
属性,可选值为
signleInstance
新的Task中只会有这一个ActivitysingleTask
新的Task中此Activity是最底层,而且整个Task只会有一个实例上面两种模式只会产生一个Task实例,如果再次启动这个Activity的话,会触发onNewIntent
方法。
Recents 就是在Android手机上按下最右的虚拟按键时,显示的最近打开过的App
如果一个App产生了两个(或多个)Task,那么默认情况下Recents中只会出现一个App的快照。但是如果其中一个Task有不同的 taskAffinity(Task 的 taskAffinity 来自于它的根Activity的taskAffinity属性), 否则还是会和原App绑定一起。可以使用 excludeFromRecents="true"
属性把Task从Recent中隐藏起来。
使用 launchMode = “singleInstance” 标记的Activity会启动一个新的Task,而这个Task中只有一个Activity,就是自己。 如果有三个Activity,A, B( singleInstance ),C 启动顺序 : A -> B -> C
启动B的时候查看Recents会发现Recent为空 (既找不到A也找不到B)。。。然后启动C,查看Task列表会发现
C(task#0),A(task#0),B(task#1), 队列顺序发生变化,B 进入新的Task,C 回到原来的Task,并且把这个Task的其他Activity拍到了B的前面。这时候Back键后退,会发现顺序是: C -> A -> B .
避免上面奇怪现象的发生,可以同时给B设置 ( singleInstance,excludeFromRecents= true, taskAffinity ) 这样,会先分离出来B为独立Task,然后通过 excludeFromRecents 可以隐藏掉。
之前所有的启动Activity的方式默认都是用的startActivity方法。但是如果使用 startActivityForResult 方法又会出现不一样的结果。
使用 FLAG_ACTIVITY_NEW_TASK 和 taskAffinity 标记的Activity还会成为新的Task。但是使用SingleInstance标记的Activity 和父Activity在同一个Task中(使用 SingleTask 标记的话,会发现所有Activity都在一个Task中)。
同时,如果被启动的sub-Activity真的运行在新的Task中了,那么这也会产生问题,onActivityResult 方法会立即执行(在sub-Activity运行之前)。
下面记录几种情况