android的啓動模式是在咱們平常開發中常常用使用到,這個也是在面試用常常問到的一個問題。雖然咱們對他很熟悉,但也會有些地方瞭解的太全面,所以寫篇文章來來總結這方面的知識。文章主要內容來自《android開發藝術探討》這本書,在文章的最後這本書的網頁版本可供查看。 java
standard: 標準啓動模式android
<!--系統默認啓動方式,不須要指定launchMode值-->
<activity
android:name=".StandardActivity"/>
複製代碼
下面內容摘自《android開發藝術探討》第一章16頁底部
git
在standard模式下,誰啓動了這個Activity那麼這個Activity就運行在它的任務棧中。例如:ActivityA啓動了ActivityB(B爲標準模式),那麼ActivityB就會進入ActivityA的任務棧中。github
啓動Activity的時候傳入的Context不要是ApplicationContext。若是必定要傳,那麼必定要設置
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
不然回報下面異常:面試
singleTop: 棧頂複用模式shell
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop"/>
複製代碼
singleTask: 棧內複用模式瀏覽器
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask" />
複製代碼
singleInstance: 單實例模式bash
<activity
android:name=".SingleInstanceActivity"
android:launchMode="singleInstance" >
複製代碼
生命週期執行:微信
onPause-->onNewIntent-->onResume
複製代碼
onNewIntent-->onRestart-->onStart
複製代碼
查看activity在棧中的狀況,可在控制檯輸入:adb shell dumpsys activity activities 經過搜索關鍵字 most recent first 快速定位 留意包名數據結構
任務棧(Task):
Task特色:
Activity指定須要啓動的任務棧能夠用過在配置文件中添加taskAffinity屬性來實現。
TaskAffinity特色:
<activity android:name=".TestActivity"
android:launchMode="singleTask"
android:taskAffinity="com.test.singleTask.affinity"/>
<activity android:name=".Test2ActivityC"
android:exported="true"
android:allowTaskReparenting="true"/>
複製代碼
下面這段內容摘自Activity啓動模式與任務棧(Task)全面深刻記錄(下)這篇文章。
假如如今有這麼一個需求,咱們的客戶端app正處於後臺運行,此時咱們由於某些須要,讓微信調用本身客戶端app的某個頁面,用戶完成相關操做後,咱們不作任何處理,按下回退或者當前Activity.finish(),頁面都會停留在本身的客戶端(此時咱們的app回退棧不爲空),這顯然不符合邏輯的,用戶體驗也是至關出問題的。咱們要求是,回退必須回到微信客戶端,並且要保證不殺死本身的app.這時候咱們的處理方案就是,設置當前被調起Activity的屬性爲: LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"
其中com.tencent.mm是藉助於工具找到的微信包名,就是把本身的Activity放到微信默認的Task棧裏面,這樣回退時就會遵循「Task只要有Activity必定從本Task剩餘Activity回退」的原則,不會回到本身的客戶端;並且也不會影響本身客戶端原本的Activity和Task邏輯。
一個e-mail應用消息包含一個網頁連接,點擊這個連接將出發一個activity來顯示這個頁面,雖然這個activity是瀏覽器應用定義的,可是activity因爲e-mail應用程序加載的,因此在這個時候該activity也屬於e-mail這個task。若是e-mail應用切換到後臺,瀏覽器在下次打開時因爲allowTaskReparenting值爲true,此時瀏覽器就會顯示該activity而不顯示瀏覽器主界面,同時actvity也將從e-mail的任務棧遷移到瀏覽器的任務棧,下次打開e-買了時並不會再顯示該activity。
Taskffinity與singleTask實例:
注: 若是使用我在GitHub上創建的項目測這個功能的時候,請將TestTaskffinityOrAllowTaskRep.zip這個壓縮包解壓,並導入AS中。這個壓縮包是我在測試的時候寫的用於跳轉androidreview應用的testTask應用。
testTask應用(簡稱T應用)MainActivity中有一個按鈕A,點擊按鈕會調用androidreview應用(簡稱A應用)的SingleTaskActivity。下面是兩個應用的主要代碼。
testTask應用代碼:
switch (v.getId()) {
case R.id.mBnt_ForSingleTask:
//跳轉androidreview應用SingleToak頁面的按鈕方法
ComponentName cnForSingleTask = new ComponentName(
"com.hdd.androidreview",
"com.hdd.androidreview.Patterm.SingleTaskActivity");
intent.setComponent(cnForSingleTask);
startActivity(intent);
break;
}
複製代碼
androidreview應用代碼:
<activity
android:name=".Patterm.SingleTaskActivity"
android:exported="true"
android:launchMode="singleTask"
android:taskAffinity="cmom.han.testbt.testTask">
</activity>
複製代碼
從上面的A應用代碼配置信息中能夠看到taskAffinity屬性配置的是T應用的包名。所以SingleTaskActivity會在T的任務中被建立。假如T應用MainActivity的按鈕A點擊事件中啓動了SingleTaskActivity。那麼cmom.han.testbt.testTask任務棧中會存在SingleTaskActivity和MainActivity兩個Activity。下面是Activity在棧中的信息。
TaskAffinity與allowTaskReparenting實例 T應用的B按鈕點擊事件代碼:
case R.id.mBnt_ForAllowTaskRep:
//allowTaskReparenting模式跳轉PattermActivity
intent.setAction("com.hdd.androidreview.PattermActivity");
ComponentName cnForAllowTaskRep = new ComponentName(
"com.hdd.androidreview",
"com.hdd.androidreview.Patterm.PattermActivity");
intent.setComponent(cnForAllowTaskRep);
break;
複製代碼
A應用的PattermActivity(簡稱PActivity)的配置信息:
<activity
android:name=".Patterm.PattermActivity"
android:allowTaskReparenting="true"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="com.hdd.androidreview.PattermActivity"/>
</intent-filter>
</activity>
複製代碼
點擊T用於的B按鈕,會啓動A用的PActivity。該Activity的allowTaskReparenting屬性爲true,那麼Activity會被移動到T應用的任務中站建立。當T應用按home返回桌面,再點擊A應用;PActivity會被移回A的任務棧中。
T應用調用A應用的PActivity棧內信息
T應用按home返回桌面,在啓動A應用棧內信息:
allowTaskReparenting僅限於以standard 和singleTop啓動的activity使用
指定Activity的啓動模式有兩種,一種是在AndroidMenifest.xml中指定
<activity
android:name=".Patterm.SingleInstanceActivity"
android:launchMode="singleInstance">
複製代碼
另一種是經過Intent來指定
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setClass(context, CycleActivity.class);
context.startActivity(intent);
複製代碼
注:intent指定的優先級大於xml中指定的優先級;若是兩個方式都指定了啓動方式,那麼系統會以intent指定的啓動方式爲準。
FLAG_ACTIVITY_NEW_TASK
等同於在xml中配置了singleTask啓動碼模式
FLAG_ACTIVITY_SINGLE_TOP
等同於在xml中配置了singleTop啓動碼模式
FLAG_ACTIVITY_CLEAR_TOP
singTask自帶該標記。這個標記會清除同一個任務棧中目標Activity之上的Activity。 若是目標Activity採用了standard啓動模式,可是任務棧中已經存在了Activity的實例。那麼系統會清除任務中該實例以及它上面的Activity,而且會從新建立一個目標Activity實例放入棧頂。
啓動Activity有兩種,一種爲顯示調用
Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);
複製代碼
另外一種爲隱式調用要配置清單文件
<!--隱式調用-->
<activity
android:name=".Patterm.PattermActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="com.hdd.androidreview.asdf" />
<category android:name="com.hdd.123456" />
<!--比就加上,不然會報錯-->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
Activity跳轉代碼
Intent intent = new Intent();
intent.setAction("com.hdd.androidreview.asdf");
//category非必須指定。若是要指定,一點要和清單文件中填寫的一至
// intent.addCategory("com.hdd.123456");
context.startActivity(intent);
複製代碼
1. action
<activity
android:name=".Patterm.PattermActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="com.hdd.androidreview.asdf" />
<action android:name="com.hdd.androidreview.qwer" />
<action android:name="12345678" />
<!--必須加上,不然會報錯-->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
//java代碼
Intent intent = new Intent();
intent.setAction("12345678");
context.startActivity(intent);
複製代碼
2. category
Intent intent = new Intent();
//必須指定一個並匹配成功
intent.setAction("com.hdd.androidreview.asdf");
//category非必須指定。若是要指定,一點要和清單文件中填寫的一至
// intent.addCategory("com.hdd.123456");
context.startActivity(intent);
複製代碼
3. data
data的匹配規則和action相似,若是過濾規則中定義了data,那麼intent中必需要匹配data。
data有mimeType和URL兩部分組成
mimeType爲媒體類型
: image/jpeg、audio/mpeg4-generic以及video/*等。
URL數據結構爲:
<scheme>://<host>:<prot>/[<path>|<pathPrefix>|<pathPattern>]
複製代碼
例如:
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
複製代碼
Scheme:URL的模式,好比http、file、content等;若是URL沒有指定scheme,這個URL是無效的。
Host:URL主機名,好比www.baidu.com,若是未指定,URL無效。
Port:RUL端口號,好比80,只有scheme和host指定了port纔有意義。
Path:表示完整的路徑信息。 PathPattern:表示完整路徑信息,裏面能夠包含通配符。 PathPrefix:表示路徑的前綴信息
data在定義的時候有兩種狀況須要注意
非完整寫法,及只指定了mimeType或者只指定了URL。
注:若是隻指定了URL,系統會默認設置mimeType的值爲content和file
<!-- 隱式調用,只配置URL -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必須加上,不然會報錯 -->
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
</activity>
<!-- 隱式調用,只配置mimeType· -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必須加上,不然會報錯 -->
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="audio/mpeg" />
</intent-filter>
</activity>
複製代碼
java代碼:
//intent指定只配置了URL的data
intent.setAction("12345678");
intent.setData(Uri.parse("http://www.baidu.com"));
context.startActivity(intent);
//intent指定只配置了mimeType的data
intent.setAction("12345678");
intent.setType("audio/mpeg");
context.startActivity(intent);
複製代碼
完整寫法
注:若是intent指定的爲完整的data,必需要使用setDataAndType(),由於setData()和setType()會彼此清楚對方的值。
<!-- 隱式調用 -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必須加上,不然會報錯 -->
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="audio/mpeg"
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
</activity>
複製代碼
java代碼:
//intent完整的data
intent.setAction("12345678");
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "audio/mpeg");
context.startActivity(intent);
複製代碼
還有一種特殊寫法:
//寫法1
<intent-filter>
<data
android:mimeType="audio/mpeg"
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
//寫法2
<intent-filter>
<data android:mimeType="audio/mpeg" />
<data android:scheme="http" />
<data android:host="www.baidu.com" />
</intent-filter>
複製代碼
上面的兩個寫法上在使用效果上是同樣的。
第一種,使用intent的resolveActivity
ComponentName componentName = intent.resolveActivity(context.getPackageManager());
if (componentName != null)
context.startActivity(intent);
else
Toast.makeText(context, "匹配不成功", Toast.LENGTH_SHORT).show();
複製代碼
第二種,使用PackageManager的resolveActivity
PackageManager packageManager=context.getPackageManager();
ResolveInfo resolveInfo = packageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY);
//判斷是否匹配成功
if (resolveInfo != null)
context.startActivity(intent);
else
Toast.makeText(context, "匹配不成功", Toast.LENGTH_SHORT).show();
複製代碼
上面的代碼中返回ResolveInfo不是最佳的Activity信息,而是全部匹配成功的Activity信息。resolveActivity()填寫的第二個參數必須是MATCH_DEFAULT_ONLY。具體緣由能夠查看《android開發藝術探索》第一章34頁,這裏就不解釋了。
用了3天終於寫完了!其實寫這篇文章是爲了複習android的基礎知識,在複習過程當中增長了對Activity啓動模式以及Activity任務棧中的狀態的瞭解。文章中四個啓動模式中最麻煩的模式我的認爲是singTask,最有意思的爲Taskffinerty這個標籤的使用,本身能夠寫個demo或者使用我在GitHub上的項目測試。