Activity啟動流程詳解

流程簡介

啟動一個Activity可能是我們在Android編程中最長用的功能之一了,方式也很簡單,就是調(diào)用Context類的startActivity方法。可能有些開發(fā)者誤認為startActivity就是通過調(diào)用另一個Activity子類的初始化方法來喚起這個Activity的,其實不然。

當你在使用startActivity時,這個調(diào)用會發(fā)送給屬于系統(tǒng)一部分的ActivityManager一個信息。由ActivityManager來創(chuàng)建該Activity實例并且調(diào)用它的onCreate方法。流程如下圖:


有人可能會問,那系統(tǒng)如何知道啟動哪一個Activity的呢?答案是通過Intent這個組件,以下是摘自Android源碼的兩個最基本的Intent構(gòu)造函數(shù):

public Intent(Context packageContext, Class<?> cls) {
    mComponent = new ComponentName(packageContext, cls);
}

public Intent(String action) {
    setAction(action);
}

第一個構(gòu)造函數(shù)中,ActivityManager可以獲得包名,和具體的類名,進而啟動這個Activity。第二個構(gòu)造函數(shù)中,ActivityManager還可以通過一個叫action的字符串來找到對應(yīng)的Activity來啟動。我們可以感受出來,通過此種架構(gòu),可以讓Activity的啟動具有更大的靈活性,也更加松耦合。既然ActivityManager是系統(tǒng)一部分,那在每個應(yīng)用安裝時,也就需要告訴系統(tǒng)自己哪些部分是可以被這種方式啟動的,此時我們就有了AndroidManifest文件,所以才有了我們需要在AndroidManifest文件中聲明Activity這個組件的做法,否則就會報ActivityNotFoundException這個異常。

顯示和隱式啟動

理解了上面的流程,我們再來看下啟動的兩種方式就很簡單了,一個叫顯示啟動,一個叫隱式啟動。我們來舉兩個顯示啟動的例子

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

或者

ComponentName componentName = new ComponentName(this, SecondActivity.class);  
// 或者 new ComponentName(this, "com.example.SecondActivity");  
// 或者 new ComponentName(this.getPackageName(), "com.example.SecondActivity");  
  
Intent intent = new Intent();  
intent.setComponent(componentName);  
startActivity(intent);  

第一種我想大家再熟悉不過了,第二種在了解啟動流程后耶比較容易理解,如果把第二種方法的第一個參數(shù)換成其他App的包名,還可以直接啟動其它App。

我們再來介紹下隱式啟動,在介紹之前,我們需要了解一個標簽<intent-filter>,從字面上來看,很好理解,就是用來過濾掉一些intent。讓只有具備處理特定intent的Activity才會被啟動。比如在AndroidManifest中有如下聲明:

<activity  
    android:name="com.example.SecondActivity">  
    <intent-filter>  
        <action android:name="12345"/>  
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>

那么啟動該Activity除了以上介紹的顯示方法外,我們還可以用以下方法

Intent intent = new Intent("12345");  
intent.addCategory("android.intent.category.DEFAULT");  //可以不加,默認被加上
startActivity(intent);  

隱式啟動是由action、category和data共同決定的,只有三者都滿足,該Activity才會被列入可處理列表,一般intent-filter包含一個action,可以包含多個category和data,所有category和某一個data滿足才行。如果你手機上多個應(yīng)用同時擁有相同的intent-filter,那么就會出現(xiàn)一個應(yīng)用列表供用戶選擇。

注:Android對待所有隱式Intent,默認它們已經(jīng)具有了"android.intent.category.DEFAULT",所以如果你想你的Activity被隱式調(diào)用,那一定需要在其intent-filter中加上該條category。不過入口Activity被"android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"標記的除外,雖然你也可以加上DEFAULT的category,但不是必須的

應(yīng)用

學(xué)習了以上幾點后,我們來應(yīng)用一下,做一個在不接入微信SDK的情況下的微信分享。你可以用apktool反編譯微信,然后通過看smali代碼和AndroidManifest文件,了解它規(guī)則后就能知道如何調(diào)用它的分享界面了。這里反編譯方法先不介紹了。經(jīng)過分析,我們找到ShareImgUI類極為微信分享的朋友列表頁面,我們來看下它的AndroidManifest.xml中內(nèi)容

<activity android:configChanges="keyboardHidden|orientation|screenSize" android:icon="@drawable/icon" android:name="com.tencent.mm.ui.tools.ShareImgUI">
            <intent-filter android:label="@string/c">
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
                <data android:mimeType="video/*"/>
                <data android:mimeType="text/*"/>
                <data android:mimeType="application/*"/>
            </intent-filter>
            <intent-filter android:label="@string/c">
                <action android:name="android.intent.action.SEND_MULTIPLE"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
            </intent-filter>
 </activity>

根據(jù)以上分析,對應(yīng)分享代碼如下

Intent it = new Intent(Intent.ACTION_SEND);
it.setComponent(new ComponentName("com.tencent.mm","com.tencent.mm.ui.tools.ShareImgUI"));
it.putExtra(Intent.EXTRA_TEXT, "分享成功");
it.setType("text/plain");  //可以不要
mActivity.startActivity(Intent.createChooser(it, "本機未安裝微信"));

相信上面的代碼大家很容易就能看明白,不過有人可能會迷惑,我們已經(jīng)用顯示啟動了,為什么還要用action呢,這是因為微信在代碼里判斷了action,如果你不傳入這個action就無法分享。另外代碼的最后一行我們?yōu)榱吮苊馕⑿盼窗惭b產(chǎn)生ActivityNotFoundException異常,用了Intent.createChooser來處理,如果本機未安裝微信,在執(zhí)行上述代碼時,會出現(xiàn)一個標題為“本機未安裝微信”的彈窗。根據(jù)同理,分享圖片、視頻、應(yīng)用到微信是可以做到的,但你需要具體分析下ShareImgUI的smali代碼,讓自己傳入的數(shù)據(jù)可以被微信接受就行。當然分享到朋友圈也一樣道理。

寫在后面

雖然上面分析了Activity啟動流程,但該流程只是簡化版,實際的啟動流程要復(fù)雜很多,涉及到很多類。雖是簡化版,但對我們理解有關(guān)Activity間跳轉(zhuǎn)的代碼足矣。后續(xù)也為大家能繼續(xù)深入源碼來了解這些流程提供了一點指導(dǎo)。

作者簡介
彭濤(@彭濤me) 致力于讓技術(shù)變得易懂且有趣
個人博客:http://pengtao.me, GitHub地址:https://github.com/CPPAlien

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容