以前面試一些校招同窗,聊到微信小程序是什麼launchMode,其任務棧是如何實現的?不少同窗只提到singleInstance,這可能沒那麼簡單。 今天咱們就猜想並解析一下微信主程序與小程序的關係與大體實現,最後給出源碼,能夠給你們做一個簡單參考。php
既然要研究微信,那麼咱們就先打開幾個小程序,再用adb命令看看任務棧信息。 在終端使用 adb shell dumpsys activity activities
命令後,能夠找到最近任務列表的Activity信息:java
Running activities (most recent first):
TaskRecord{caccd90 #3239 A=.AppBrandUI3 U=0 StackId=1 sz=1}
Run #4: ActivityRecord{bb162b8 u0 com.tencent.mm/.plugin.appbrand.ui.AppBrandUI3 t3239}
TaskRecord{d6c62d6 #3190 A=com.tencent.mm U=0 StackId=1 sz=1}
Run #3: ActivityRecord{7f2d805 u0 com.tencent.mm/.ui.LauncherUI t3190}
TaskRecord{34a386a #3238 A=.AppBrandUI2 U=0 StackId=1 sz=1}
Run #2: ActivityRecord{16cfede u0 com.tencent.mm/.plugin.appbrand.ui.AppBrandUI2 t3238}
TaskRecord{7ade2d1 #3237 A=.AppBrandUI U=0 StackId=1 sz=1}
Run #1: ActivityRecord{ccfd8ae u0 com.tencent.mm/.plugin.appbrand.ui.AppBrandUI t3237}
...
複製代碼
能夠發現這裏的#3是微信主Activity,四、二、1都是我開的小程序,且位於不一樣的任務棧中,Activity名稱都是AppBrandUI+數字的形式。 而後再看看其餘關鍵信息(這裏我單獨篩出來):android
packageName=com.tencent.mm processName=com.tencent.mm
taskAffinity=com.tencent.mm
packageName=com.tencent.mm processName=com.tencent.mm:appbrand3
taskAffinity=.AppBrandUI3
packageName=com.tencent.mm processName=com.tencent.mm:appbrand2
taskAffinity=.AppBrandUI2
packageName=com.tencent.mm processName=com.tencent.mm:appbrand
taskAffinity=.AppBrandUI
複製代碼
很簡單,和咱們平時實現多進程差很少,說明是給Activity設置了process屬性。git
轉念一想,小程序那麼多,難道這些不一樣後綴的Activity都寫死在代碼裏嗎? 顯然不能這麼幹,只能兩種途徑能夠達成目的:github
對於第一種,我查閱了一些資料,理論上講是能夠作到的,涉及到NDK開發,須要咱們對AMS的源碼很熟悉,不走常規流程啓動Activity,且小程序是多進程的,可能還須要手動fork進程。 這種方式顯然具備較大的風險,屬於黑科技範疇,並且谷歌官方是不推薦的,微信做爲十幾億用戶的常駐App,幾乎不太可能使用這一方案。面試
那麼只剩第二種了,預先在本地寫死n個同樣的Activity(固然也能夠經過繼承形式),同時在Manifest中註冊好。 而後打開一個小程序就佔用一個Activity,當打開第n+1個小程序時,覆蓋第1個小程序所在的Activity,這樣就至關於第1個小程序被頂掉了。shell
分析到此,就很明顯了,若是真的是第二種方案,那麼小程序就不能無限數量地打開咯?果斷打開微信試了一下,果真,最多隻能開5個!當你啓動第6個小程序時,第1個就被銷燬了。 其實這也是符合咱們上述預期的,每一個小程序的進程不同,taskAffinity也不同,類名也不同。原生API是不支持動態設置taskAffinity和進程名的。小程序
小程序所在的Activity:微信小程序
public class SmallActivity extends AppCompatActivity {
public static class Small0 extends SmallActivity {}
public static class Small1 extends SmallActivity {}
public static class Small2 extends SmallActivity {}
public static class Small3 extends SmallActivity {}
public static class Small4 extends SmallActivity {}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_small);
// 動態地給小程序Activity設置名稱和圖標,下面代碼只是舉例,實際信息確定是動態獲取的
// 因爲iconRes這個構造參數的API 28才加入的,因此建議區分版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
int iconRes = 0; // 這裏應該是小程序圖標的資源索引
setTaskDescription(new ActivityManager.TaskDescription("小程序名", iconRes));
} else {
Bitmap iconBmp = null; // 這裏應該是小程序圖標的bitmap
setTaskDescription(new ActivityManager.TaskDescription("小程序名", iconBmp));
}
}
}
複製代碼
Manifest:bash
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.ysy.smallapp">
<application ...>
<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=".SmallActivity$Small0" android:label="Small0" android:launchMode="singleTask" android:process=":Small0" android:taskAffinity=".Small0" />
<activity android:name=".SmallActivity$Small1" android:label="Small1" android:launchMode="singleTask" android:process=":Small1" android:taskAffinity=".Small1" />
<activity android:name=".SmallActivity$Small2" android:label="Small2" android:launchMode="singleTask" android:process=":Small2" android:taskAffinity=".Small2" />
<activity android:name=".SmallActivity$Small3" android:label="Small3" android:launchMode="singleTask" android:process=":Small3" android:taskAffinity=".Small3" />
<activity android:name=".SmallActivity$Small4" android:label="Small4" android:launchMode="singleTask" android:process=":Small4" android:taskAffinity=".Small4" />
</application>
</manifest>
複製代碼
具體的複用邏輯這裏暫時就這樣簡單地實現了,實際狀況確定比此複雜:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val edtText = findViewById<EditText>(R.id.edt_main)
findViewById<View>(R.id.btn_main).setOnClickListener {
startActivity(Intent().apply {
val id = edtText.text.toString().toInt() % 5
setClassName(this@MainActivity, "com.ysy.smallapp.SmallActivity\$Small$id")
})
}
}
}
複製代碼
完整源碼: github.com/ysy950803/S…