Activity啟動模式聯想到多程式相關的一些東西

mymdeep發表於2019-02-27

看到這個題目估計又有人說我標題黨了,啟動模式跟多程式有什麼關係,沒啥關係,我只是在寫Activity啟動模式的demo的時候,用到了多程式進行測試,順便一起交代一下。


Task與Process

不知道有沒有人想當然的混淆上述兩個概念,我是見過有這樣想的開發者。

Task

Task就是一堆Activity的集合,你可以這樣想,一個棧中,有多個Activity,當使用者在多個activity之間跳轉時,執行壓棧操作,當使用者按返回鍵時,執行出棧操作。
在做測試之前,我們需要先介紹一下Task相關的程式碼。
在Activity中,直接呼叫getTaskId()可以獲取當前Activity所在的Task_id。
我們也可以呼叫系統方法,獲取Task的相關資訊:

    public static void getTask(Context context){
        ActivityManager  mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<AppTask> list =  mAm.getAppTasks();
        for (AppTask appTask:list){
            Log.e("Utils",context.getClass().getName()+"  "+appTask.getTaskInfo().affiliatedTaskId+"的num = "+appTask.getTaskInfo().numActivities);

        }
    }
複製程式碼

standard

我們設定四個Activity,每個Activity的啟動模式都設定成standard。來看一下Task的相關情況。

Activity啟動模式聯想到多程式相關的一些東西

上圖是用上面介紹的程式碼完成的一個demo,從第一個Actvity切換到第四個Activity,每增加一個ActivityTask都會增加一個Activity,如果按返回鍵,即銷燬一個Activity,根據上圖所知,ActivityTask會減少一個。如果不斷的startActivity則ActivityTask數量不斷增加。

singleTask

我們設定四個Activity,每個Activity的啟動模式都設定成singleTask。來看一下Task的相關情況。

Activity啟動模式聯想到多程式相關的一些東西

由上圖可知,如果一個activity的啟動模式為singleTask,那麼Task棧中將會只有一個該Activity的例項。所以從第一個activity,跳到第四個Activity,再跳回第一個的時候,只是將第一個Activity啟動了,同時呼叫了第一個Activity的onNewIntent
這裡還有一個有趣的事,注意觀察上圖,再回到第一個Activity的時候,Task中Activity的數量變為了1,也就是singleTask除了啟動了第一個Activity,並將第一個Activity順序之上的activity全部銷燬了。

singleTop

我們設定四個Activity,每個Activity的啟動模式都設定成singleTop。來看一下Task的相關情況。

Activity啟動模式聯想到多程式相關的一些東西

根據上圖可知,你可能感覺這個跟standard模式沒什麼區別啊,的確是這樣的,如果被啟動的Activity不處於棧頂,那麼跟standard沒有什麼區別,當要啟動的Activity處於棧頂,不會再次建立這個Activity的例項,而是重用原來的例項,並且呼叫原來例項的onNewIntent()方法。

singleInstance

我們設定四個Activity,每個Activity的啟動模式都設定成singleInstance。來看一下Task的相關情況。

Activity啟動模式聯想到多程式相關的一些東西

由上圖可知,這次的變化是很明顯的。每次啟動Activity都會重新建立一個Task,並且這個Task中只有這一個例項。也就是說被該例項啟動的其他activity會自動執行於另一個任務中。當再次啟動該activity的例項時,會重用已存在的任務和例項。並且會呼叫這個例項的onNewIntent()方法。

Process

現在聊一下與程式相關的東西

taskAffinity屬性

首先先說的是taskAffinity,為什麼在Process標題下聊taskAffinity,因為程式間我們可以看到的taskAffinity屬性,更明顯的特性。
接著需要引出的概念就是taskAffinity,taskAffinity表示當前activity具有親和力的一個Task,可以這樣理解, taskAffinity表示一個Task的名字,這個Task就是當前activity所在的Task。一個Task的taskAffinity取決於跟Activity的taskAffinity。
如果一個Activity沒有顯式的指明該 Activity的taskAffinity,那麼它的這個屬性就等於Application指明的taskAffinity,如果 Application也沒有指明,那麼該taskAffinity的值就等於包名。
現在來示範一個例子(所有的例子,都有demo,在文章的最後),新建兩個應用,app,app2,每個應用都有MainActivity,和SecondActivity,從MainActivity可以跳到SecondActivity,為了方便描述,我們appMain,appSecond,app2Main,app2Second來代替。
我們將appSecond和app2Second設定成同樣的taskAffinity:

<activity android:name=".MainActivity"
                  android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".SecondActivity"
                  android:taskAffinity="com.deep"
                >

        </activity>
複製程式碼

啟動這兩個Activity,發現沒有什麼特殊效果,appSecond都在各自應用的應用根Activity的Task中。
這時我們改一下程式碼將appSecond設定成singleTask,這樣appSecond所在的Task就是帶有taskAffinity屬性的了。
然後這時我們再啟動app2Second:

Activity啟動模式聯想到多程式相關的一些東西

由圖可知,app2Second仍然在app2Main的Task中,這是由於app2Second的啟動模式是標準,沒有自主選擇task的能力,我們再將app2Second改成singleTask:

Activity啟動模式聯想到多程式相關的一些東西

然後使用adb shell dumpsys activity activities檢視一下activity的堆疊資訊:

Activity啟動模式聯想到多程式相關的一些東西

#733 與#734標識的為Task,與上面app截圖不同是由於,我又執行了一次,所以Task id增加了,但是Activity與Task的歸屬關係是一樣的。
所以我們發現app2Second所在的task跟appSecond所在的task一樣了,這說明app2Second可以根據taskAffinity選擇Task了,而且這個Task還可以不是當前Process。

多程式

關於多程式,可以共用Task的例子,也可以這樣證明:

<activity android:name=".MainActivity"
                  android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".SecondActivity"
                  android:process="com.deep1"
                  android:launchMode="standard"

                     >

        </activity>
        <activity android:name=".ThirdActivity"
                  android:process="com.deep2"
                  android:launchMode="standard">

        </activity>
        <activity android:name=".FourthActivity"
                  android:launchMode="standard">

        </activity>
複製程式碼

在一個app中啟用多程式,但所有的Activity都是標準模式,可以發現,所有的Activity都在同一個Task中。

全域性變數的訪問

Activity都可以設定程式的歸屬關係,但是如果不是Activity,而是一個全域性變數呢?可以試一下,我們建立一個全域性變數:

public class StaticParam {
    public static String a = "default";
    public static Object o = new Object();
}
複製程式碼

首先在Application中初始化:

 @Override
    public void onCreate() {
        super.onCreate();
        StaticParam.a ="init";
        Log.e("xxxxxx","application:"+Utils.getCurProcessName(this)+ "    "+StaticParam.a.getClass().toString());
    }
複製程式碼

然後在上面例子中的四個Activity中的onCreate新增如下程式碼:

  StaticParam.a = StaticParam.a+ " "+getClass().getSimpleName();
Log.e("xxxxxx","a="+StaticParam.a+"      "+StaticParam.o+"    "+Utils.getCurProcessName(this));
複製程式碼

執行一遍所有的activity,看一下效果:

12-27 10:56:32.993 31195-31195/umeng.com.testlauncher E/xxxxxx: application:umeng.com.testlauncher    class java.lang.String
12-27 10:56:33.053 31195-31195/umeng.com.testlauncher E/xxxxxx: a=init MainActivity      java.lang.Object@76794cc    umeng.com.testlauncher
12-27 10:56:40.000 31325-31325/? E/xxxxxx: application:com.deep1    class java.lang.String
12-27 10:56:40.057 31325-31325/? E/xxxxxx: a=init SecondActivity      java.lang.Object@76794cc    com.deep1
12-27 10:56:49.618 31486-31486/com.deep2 E/xxxxxx: application:com.deep2    class java.lang.String
12-27 10:56:49.674 31486-31486/com.deep2 E/xxxxxx: a=init ThirdActivity      java.lang.Object@76794cc    com.deep2
12-27 10:56:51.202 31195-31195/umeng.com.testlauncher E/xxxxxx: a=init MainActivity FourthActivity      java.lang.Object@76794cc    umeng.com.testlauncher
複製程式碼

根據log,我們發現幾個問題:

  • 當從MainActivity跳到SecondActivity的時候,由於SecondActivity屬於程式com.deep1,所以會在com.deep1 程式中再次執行Application的初始化。
  • MainActivity已經將StaticParam.a置成了a=init MainActivity但是SecondActivity新增了自己的名字卻是a=init SecondActivity,等到與MainActivity同程式的FourthActivity的時候,又變回a=init MainActivity FourthActivity,這說明全域性靜態變數在各個程式間是不通用的,每個程式各自維護各自的,互不干擾。

總結

demo地址:https://github.com/mymdeep/activityTest
暫時就想到了這些,寫的有點亂,想到哪寫到哪,如有問題,歡迎進一步討論
也歡迎關注我的公眾號,之後會推薦更多好用的元件庫。

Activity啟動模式聯想到多程式相關的一些東西

相關文章