由从服务中启动activity——谈谈安卓的任务与栈

引子

在安卓的服务中这样启动活动:

Intent intent = new Intent(this,XXXActivity.class);
startActivity(intent);

你会得到这样的错误:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?


你知道我对安卓的什么地方最为痴迷?是它应用之间的协作。怎么协作?依靠activity之间的协作。

task

这种协作是可以跨越应用的。我们从task谈起。
task最直观的是安卓第三个虚拟键所列出的那些就是任务。

image.jpeg

An activity can even start activities that exist in other applications on the device. …… Eventhough the activities may be from different applications, Android maintains this seamless userexperience by keeping both activities in the same task.

以上这种功能的实现,要从task谈起,developer上,这样定义task:

A task is a collection of activities that users interact withwhen performing a certain job. The activities are arranged in a stack (the back stack), inthe order in which each activity is opened. When apps are running simultaneously in amulti-windowed environment, supported inAndroid 7.0 (API level 24) and higher, the system manages tasks separately for each window;each window may have multiple tasks. The same holds true for Android apps running on Chromebooks: the system manages tasks, or groups of tasks, on aper-window basis.

翻译过来,大体意识就是task是一个具有栈结构的容器,用以执行一定特定的工作,它可以放置多个Activity实例。

启动一个应用,系统就会为之创建一个task,来放置根Activity。默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个task中的,后者被压入前者所在的task栈,当用户按下后退键,后者从task被弹出,前者又显示在幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用。

task可以分为系统task和task。这两者之间是互相独立的。

当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。

更近一步,Activity的归属

一个Activity当然要表面自己身在哪个task,所以每个Activity都有taskAffinity属性。这个属性指出了它希望进入的Task。

如果一个Activity指明自己的taskaffinity,那么它的这个属性就等于Application指明的taskAffinity,如果Application也没有指明,那么该taskAffinity的值就等于包名。

而Task也有自己的affinity属性,它的值等于它的根Activity的taskAffinity的值。

显示的声明activiy的属性
这很简单,在AndroidManifest.xml中声明

<application android:icon="@drawable/icon" android:label="@string/app_name">  
    <activity android:name=".TaskTestActivity"  
              android:taskAffinity="com.tasktest.task"  
              android:label="@string/app_name">  
    </activity>  
</application>  

有了上面的基础,请记住这两种文档说明的情况:

1. 如果该Activity的allowTaskReparenting设置为true,它进入后台,当一个和它有相同affinity的Task进入前台时,它会重新宿主,进入到该前台的task中。
**2. 如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。 **

再来看看Activity四种启动模式详解

activity有四种启动模式,分别为standard,singleTop,singleTask,singleInstance。

这四种模式我不细说,我们只从名字上分析分析。

第一种,标准模式,想想就知道是平常的模式,这里的标准意思是每生成一个activity的实例,就当一个实例的放在栈里。

第二种,singleTop,在于那个top。要是activity不在栈顶,它和standard模式没什么区别,要是在top,就不创建一个新的,用栈顶原来那个。

第三种,不那么容易,尤其是官网的说法好像有问题。

singleTask模式的Activity只允许在系统中有一个实例。如果系统中已经有了一个实例,持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。如果没有,则会创建一个新的Activity并置放在合适的任务中。
这话的意思,我们分两种情况讨论,一是在同一个应用,二是不同。
在同一个应用中,如果系统中还没有singleTask的Activity,会新创建一个,并放在同一任务的栈顶。但是如果已经存在,singleTask Activity上面的所有Activity将以合适的方式自动销毁,让我们想要显示的Activity处于栈顶。
在非同一个应用中,intent是从另外的应用发送过来。系统中没有任何Activity的实例的化,会创建一个新的任务,并且新的Activity被作为根Activity创建;如果系统中拥有这个singleTask的应用存在,新建的Activity会置于这个任务的上面。

第四种,和第三种很像,关键在于singleInstance,就是只能有这一个单例存在在栈中。

Intent Flags

回到开头,我们还没解决开始的错误。最简单粗暴的方法是:

Intent intent = new Intent(this,XXXActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
startActivity(intent);

这可以解决问题,不过也还存在一个小问题,留待读者发现:)
Flag的字面意思很好理解,如果你读懂了上述的任务与活动启动模式的化,再提供几个intent Flags:

**FLAG_ACTIVITY_CLEAR_TOP**   
**FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET**   
**FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS**    
**FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY**    
**FLAG_ACTIVITY_MULTIPLE_TASK **   
**FLAG_ACTIVITY_NEW_TASK**   
**FLAG_ACTIVITY_NO_ANIMATION**  
**FLAG_ACTIVITY_NO_HISTORY**   
**FLAG_ACTIVITY_NO_USER_ACTION**  
**FLAG_ACTIVITY_PREVIOUS_IS_TOP **   
**FLAG_ACTIVITY_REORDER_TO_FRONT**    
**FLAG_ACTIVITY_RESET_TASK_IF_NEEDED **
**FLAG_ACTIVITY_SINGLE_TOP**    

至于意思,还是字面理解就好了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容