1 ApplicationContext啟動(dòng)standard模式的Activity
getApplication().startActivity(new Intent(this, LaunchMode1Activity.class));
報(bào)錯(cuò)如下
Caused by: android.util.AndroidRuntimeException:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.
Is this really what you want?
standard模式的Activity默認(rèn)會(huì)進(jìn)入啟動(dòng)他的Activity所屬的任務(wù)棧中,但是由于非Activity類型的Context,如ApplicationContext并沒有所謂的任務(wù)棧,就會(huì)報(bào)錯(cuò)。解決方法是為待啟動(dòng)的Activity指定FLAG_ACTIVITY_NEW_TASK標(biāo)記位,這樣啟動(dòng)的時(shí)候就會(huì)為他創(chuàng)建一個(gè)新的任務(wù)棧,這時(shí)候待啟動(dòng)的Activity實(shí)際上是以singleTask模式啟動(dòng)的。
Intent intent = new Intent(this, LaunchMode1Activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(intent);
按照上文的說法,如果待啟動(dòng)的Activity是singleTask且使用taskAffinity指定了棧名;或者使用了singleInstance模式,那么getApplication()應(yīng)該就可以正確的啟動(dòng)該Activity,但不行。why?
2. singleTop 棧頂復(fù)用模式
使用此模式的Activity,如果位于任務(wù)棧的棧頂,那么此Activity不會(huì)被重新創(chuàng)建,同時(shí)它的onNewIntent方法會(huì)被回調(diào)。如果不在棧頂,則繼續(xù)創(chuàng)建實(shí)例。
3. singleTask 棧內(nèi)復(fù)用模式
這是一種單例模式。比如一個(gè)Activity A,他是singleTask模式。當(dāng)他被啟動(dòng)時(shí),先看他想要的棧有沒有,沒有則創(chuàng)建并創(chuàng)建A的實(shí)例放入棧中。如果有,則看棧內(nèi)有沒有A的實(shí)例,有則不創(chuàng)建,執(zhí)行A的onNewIntent方法,并執(zhí)行clearTop(singleTask默認(rèn)具有clearTop效果),A之上的Activity都出棧。如果沒有,則創(chuàng)建并放入棧中。
之前學(xué)習(xí)的比較簡(jiǎn)單,如果棧中有則不創(chuàng)建并執(zhí)行onNewIntent方法,沒有則創(chuàng)建,這里默認(rèn)該Activity使用的是當(dāng)前應(yīng)用包名的棧。使用taskAffinity可以指定singleTask模式的Activity所期望的棧名,可以不和包名一個(gè)棧。taskAffinity只能和sigleTask或者singleInstance配合使用,其他情況下使用,沒意義。
<activity
android:name="._1activity.LaunchMode1Activity"
android:launchMode="singleTask"
android:taskAffinity="qingfengmy.ali"></activity>
4. singleInstance 單實(shí)例模式
它是一種加強(qiáng)的singleTask,除了具有singleTask的所有特性外,還加強(qiáng)了一點(diǎn),那就是此種模式的Activity只能單獨(dú)的位于一個(gè)棧中。也就是除非這個(gè)棧被回收,否則不會(huì)創(chuàng)建新的實(shí)例。
5. 多個(gè)任務(wù)棧時(shí)的一種情況
比如AB在一個(gè)棧,在前臺(tái),名為x,順序就是AB;CD在一個(gè)棧,是singleTask的,在后臺(tái),名為y,順序是CD。當(dāng)前臺(tái)棧頂?shù)腂啟動(dòng)D時(shí),會(huì)把y棧變?yōu)榍芭_(tái)棧,x棧為后臺(tái)棧。前臺(tái)棧y順序CD,后臺(tái)棧x順序AB。此時(shí)按回退鍵,前臺(tái)棧y為C,后臺(tái)棧x為AB。此時(shí)再按回退鍵,前臺(tái)棧y因?yàn)闉榭障В笈_(tái)棧x變?yōu)榍芭_(tái)棧,順序AB。再按回退鍵,棧x中為A。再按回退鍵,回退到桌面。
如果當(dāng)時(shí)B啟動(dòng)的是C;那么?C所在的任務(wù)棧y到前臺(tái),由于C沒在棧頂,所以C到棧頂并clearTop,D出棧。此時(shí)棧y中C,棧x為AB。按回退鍵棧y為空消失,棧x為前臺(tái)棧。此后再按回退鍵,棧x中依此出棧,直到回到桌面。
6. TaskAffinity和allowTaskReparenting
taskAffinity,翻譯為任務(wù)相關(guān)性。和singleTask相配合可以指定棧名,棧名同包名格式一樣,必須包含"."連接符。如果沒有指定棧名,則繼承Application的棧名,也就是當(dāng)前應(yīng)用的包名。
allowTaskReparenting,翻譯為允許更換從屬任務(wù)棧,其意義也是允許更換從屬任務(wù)棧。他不局限于singleTask模式,所有模式都可以,他和taskAffinity相配合。
7. allowTaskReparenting的例子
應(yīng)用A中兩個(gè)Activity,LaunchMode1Activity的模式定義為singleTask,allowTaskReparenting設(shè)置為true,為了別的應(yīng)用可以訪問該頁面,exportd屬性設(shè)置為true.
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="._1activity.LaunchMode1Activity"
android:allowTaskReparenting="true"
android:exported="true"></activity>
另一個(gè)應(yīng)用B中啟動(dòng)應(yīng)用A中的LaunchMode1Activity。
Intent intent = new Intent(Intent.ACTION_VIEW);
String packageName = "qingfengmy.developmentofart";
String className = "qingfengmy.developmentofart._1activity.LaunchMode1Activity";
intent.setClassName(packageName, className);
startActivity(intent);
應(yīng)用A的LaunchMode1Activity啟動(dòng)后,點(diǎn)擊home鍵回到桌面,然后點(diǎn)應(yīng)用A的啟動(dòng)圖標(biāo),發(fā)現(xiàn)沒有進(jìn)入應(yīng)用A的MainActivity,而是進(jìn)入應(yīng)用A的LaunchMode1Activity。
這是為什么吶?
正常情況下,如果LaunchMode1Activity沒有設(shè)置allowTaskReparenting為true,則應(yīng)用B啟動(dòng)LaunchMode1Activity時(shí),LaunchMode1Activity會(huì)進(jìn)入應(yīng)用B所在的任務(wù)棧中。此時(shí)點(diǎn)擊home回到桌面,再點(diǎn)應(yīng)用A的圖標(biāo),一定會(huì)到應(yīng)用A的MainActivity.
但是,如果LaunchMode1Activity的allowTaskReparenting屬性設(shè)為true時(shí),LaunchMode1Activity就可以改變從屬任務(wù)棧,也就是當(dāng)應(yīng)用B啟動(dòng)他時(shí),他會(huì)從B的任務(wù)棧自動(dòng)移到他自己的任務(wù)棧,也就是應(yīng)用A的包名的名稱的任務(wù)棧。
當(dāng)從桌面啟動(dòng)應(yīng)用A時(shí),發(fā)現(xiàn)A的任務(wù)棧已經(jīng)存在,就認(rèn)為上次A沒有退出去,最后一個(gè)頁面是棧中的LaunchMode1Activity,所以直接顯示LaunchMode1Activity。
看了下Android源代碼,只有在發(fā)生rese ttask時(shí)才會(huì)去使用這個(gè)標(biāo)志,
而我們從home上啟動(dòng)的程序都帶了FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。
所以從home上啟動(dòng)程序會(huì)進(jìn)行task reset.
也就會(huì)使用到這個(gè)allowTaskReparenting。
上文說的也有道理,桌面本身是一個(gè)應(yīng)用,包名是"com.android.launcher",從該應(yīng)用啟動(dòng)其他應(yīng)用,其他應(yīng)用的Activity肯定不會(huì)進(jìn)入桌面應(yīng)用的任務(wù)棧,肯定會(huì)進(jìn)入自己應(yīng)用的任務(wù)棧。
8. adb shell dumpsys activity命令
檢測(cè)當(dāng)前設(shè)備的任務(wù)棧
Running activities (most recent first):
TaskRecord{ea7283f #8692 A=qingfengmy.behaviordemo.free U=0 sz=2}
Run #1: ActivityRecord{ef55f8d u0 qingfengmy.developmentofart/._1activit
y.LaunchMode1Activity t8692}
Run #0: ActivityRecord{18d62e u0 qingfengmy.behaviordemo.free/qingfengmy
.behaviordemo.activitys.MainActivity t8692}
可見這是LaunchMode1Activity的allowTaskReparenting屬性為false時(shí)的任務(wù)棧。在任務(wù)棧為qingfengmy.behaviordemo.free中有兩個(gè)activity,分別是當(dāng)前應(yīng)用的qingfengmy.behaviordemo.free/qingfengmy
.behaviordemo.activitys.MainActivity和應(yīng)用A中的qingfengmy.developmentofart/._1activit
y.LaunchMode1Activity
9. activity的flags
- FLAG_ACTIVITY_NEW_TASK:指定singleTask啟動(dòng)模式。
- FLAG_ACTIVITY_SINGLE_TOP:指定singleTop啟動(dòng)模式。
- FLAG_ACTIVITY_CLEAR_TOP:當(dāng)他啟動(dòng)時(shí),在同一個(gè)任務(wù)棧中所有位于他上面的Activity都要出棧。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:該標(biāo)記的Activity不會(huì)出現(xiàn)在歷史Activity的列表中。
10. intentfilter概述
隱式啟動(dòng)activity,Activity可以在manifest中配置intentfilter,注意一個(gè)Activity可以配多個(gè)intentfilter,一個(gè)intentfilter中可以有多個(gè)Action,category,data.
11. action的匹配規(guī)則
action是一個(gè)字符串,系統(tǒng)定義了一些,我們也可以自己定義。action的匹配規(guī)則是Intent中的Action必須和過濾規(guī)則中的其中一個(gè)Action相同。比如:
<activity
android:name="._1activity.LaunchMode1Activity">
<intent-filter>
<action android:name="qingfengmy.ali" />
<action android:name="qingfengmy.tx" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
intent-filter中的category必須有一個(gè)默認(rèn)的值,也就是系統(tǒng)提供的DEFAULT。intent中雖然沒設(shè),但startActivity時(shí)會(huì)自動(dòng)加上DEFAULT,所以可以匹配上,如果過濾規(guī)則中沒有DEFAULT,而startActivity默認(rèn)加上了DEFAULT,因?yàn)閏ategory匹配不了,所以找不到Activity。
Intent intent = new Intent();
intent.setAction("qingfengmy.ali");
startActivity(intent);
12. category的匹配規(guī)則
category的匹配規(guī)則是,如果intent中含有category,則每個(gè)category都必須和過濾器中的匹配。intent中有一個(gè),則一個(gè)必須匹配;intent中有三個(gè),則三個(gè)必須匹配。如果過濾器中匹配了系統(tǒng)的DEFAULT,則intnet中沒有category也可以。因?yàn)閟tartActivity會(huì)自動(dòng)加上。
13. data的匹配
data的匹配同Action,intent中必須有data,且必須和過濾器中的某個(gè)data完全匹配。
<activity android:name="._1activity.LaunchMode1Activity">
<intent-filter>
<action android:name="qingfengmy.ali" />
<action android:name="qingfengmy.tx" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="file"
android:host="qingfengmy"
android:mimeType="images/*"/>
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("qingfengmy.ali");
intent.setDataAndType(Uri.parse("file://qingfengmy"), "images/*");
startActivity(intent);
14. 隱式調(diào)用時(shí)的錯(cuò)誤規(guī)避
方式一:
getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
方式二:
getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
二者只要返回為null,則表示沒有匹配的Activity。其中MATCH_DEFAULT_ONLY表示只檢查intent-filter中聲明了<category android:name="android.intent.category.DEFAULT" />
的。
15. 其他重要的action和category
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />