Android外掛化原理分析(基於Neptune框架)

肥寶發表於2019-05-11

前言

Android外掛化不算是一門新技術,發展了有一些年頭了。不同公司的外掛化方案大體原理上很相似。本文通過閱讀愛奇藝的Neptune框架來介紹外掛化的整體思路和流程。

外掛化基礎知識點

外掛應用安裝

所謂的外掛其實本質上也是一個apk。在原生的Android應用中,apk在執行時會被對映成一個LoadedApk物件。外掛在安裝之後也會被對映成類似的PluginLoadedApk物件,統一管理外掛的相關資訊。

public class PluginLoadedApk {
    public static final ConcurrentMap<String, Vector<Method>> sMethods = new ConcurrentHashMap<String, Vector<Method>>(1);
    private static final String TAG = "PluginLoadedApk";
    /* 儲存注入到宿主ClassLoader的外掛 */
    private static Set<String> sInjectedPlugins = Collections.synchronizedSet(new HashSet<String>());
    /* 儲存所有的外掛ClassLoader */
    private static Map<String, DexClassLoader> sAllPluginClassLoader = new ConcurrentHashMap<>();

    /* 宿主的Context */
    private final Context mHostContext;
    /* 宿主的ClassLoader */
    private final ClassLoader mHostClassLoader;
    /* 宿主的Resource物件 */
    private final Resources mHostResource;
    /* 宿主的包名 */
    private final String mHostPackageName;
    /* 外掛的路徑 */
    private final String mPluginPath;
    /* 外掛執行的程式名 */
    private final String mProcessName;
    /* 外掛ClassLoader的parent */
    private ClassLoader mParent;
    /* 外掛的類載入器 */
    private DexClassLoader mPluginClassLoader;
    /* 外掛的Resource物件 */
    private Resources mPluginResource;
    /* 外掛的AssetManager物件 */
    private AssetManager mPluginAssetManager;
    /* 外掛的全域性預設主題 */
    private Resources.Theme mPluginTheme;
    /* 外掛的詳細資訊,主要通過解析AndroidManifest.xml獲得 */
    private PluginPackageInfo mPluginPackageInfo;
    /* 外掛工程的包名 */
    private String mPluginPackageName;
    /* 外掛的Application */
    private Application mPluginApplication;
    /* 自定義外掛Context,主要用來改寫其中的一些方法從而改變外掛行為 */
    private PluginContextWrapper mPluginAppContext;
    /* 自定義Instrumentation,對Activity跳轉進行攔截 */
    private PluginInstrument mPluginInstrument;
    ...
}    
複製程式碼

外掛的安裝分為內建外掛(asset目錄,sdcard)和線上外掛兩部分。

  • 內建外掛:
    • 約定存放在assets/pluginapp/<plugin_pkg_name>.apk形式,安裝時解壓到/data/data/<host_pkg_name>/app_pluginapp目錄
    • sdcard外掛,允許除錯模式下安裝,以<plugin_pkg_name>.apk命名
  • 線上外掛:直接將外掛下載到sdcard目錄上,然後拷貝到/data/data/<host_pkg_name>/app_pluginapp目錄下;為了減少拷貝操作,可以直接下載到/data/data/<hots_pkg_name>/app_pluginapp目錄;

外掛的安裝通過執行在獨立程式的Service完成,主要防止部分機型dexopt hang住主程式。

dexopt

Android根據系統版本不同會採用兩種虛擬機器。Dalvik虛擬機器是JIT方式解釋執行dex位元組碼;ART虛擬機器是AOT方式將dex位元組碼轉化為oat機器碼。

  • Dalvik是執行時解釋dex檔案,安裝比較快,開啟應用比較慢,應用佔用空間小
  • ART是安裝的時候位元組碼預編譯成機器碼儲存在本地,執行的時候直接就可以執行的,安裝慢,開啟應用快,佔用空間大;

如果當前執行在Dalvik虛擬機器下,Dalvik會對classes.dex進行一次“翻譯”,“翻譯”的過程也就是守護程式installd的函式dexopt來對dex位元組碼進行優化,實際上也就是由dex檔案生成odex檔案,最終odex檔案被儲存在手機的VM快取目錄data/dalvik-cache下(注意!這裡所生成的odex檔案依舊是以dex為字尾名,格式如:system@priv-app@Settings@Settings.apk@classes.dex)。如果當前執行於ART模式下, ART同樣會在首次進入系統的時候呼叫/system/bin/dexopt(此處應該是dex2oat工具吧)工具來將dex位元組碼翻譯成本地機器碼,儲存在data/dalvik-cache下。 那麼這裡需要注意的是,無論是對dex位元組碼進行優化,還是將dex位元組碼翻譯成本地機器碼,最終得到的結果都是儲存在相同名稱的一個odex檔案裡面的,但是前者對應的是一個.dex檔案(表示這是一個優化過的dex),後者對應的是一個.oat檔案。通過這種方式,原來任何通過絕對路徑引用了該odex檔案的程式碼就都不需要修改了。 由於在系統首次啟動時會對應用進行安裝,那麼在預置APK比較多的情況下,將會大大增加系統首次啟動的時間。

對於外掛安裝來說,外掛的安裝通過執行在獨立程式的Service完成,主要防止部分機型dexopt hang住主程式。

外掛安裝過程主要執行以下幾步:

  1. 拷貝apk到內建儲存區,重新命名為<plugin_pkg_name>.apk
  2. 解壓apk中的so庫到app_pluginapp/<plugin_pkg_name>/lib目錄
  3. dexopt優化外掛dex,Android 7.0以上第一次會使用解釋模式執行dex,優化載入速度

類載入

Android外掛化原理分析(基於Neptune框架)

Java中的類都是通過ClassLoader載入的,而Android中類的載入也離不開ClassLoadder。在Android系統中,主要的ClassLoader有三個:

  • BootClassLoader:Android系統啟動時用來預載入常用的類
  • PathClassLoader:用來載入系統和應用程式中的類,如果是非系統應用程式類,則會載入/data/app目錄下的dex、apk或jar檔案
  • DexClassLoader:可以載入指定路徑的dex、apk或jar檔案,支援從SD卡進行載入,是外掛化的技術基礎

類載入的雙親委派機制

某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入。

關於外掛中類的載入機制有兩種處理方式,一種是單類載入機制,另一種是多類載入機制;單類載入器機制,即所有外掛APP的類都通過宿主的ClassLoader(即PathClassLoader)進行載入,與MultiDex、Qzone熱修復技術類似,通過Dex前插後者後插的方式實現。採用單類載入器模型,隨著業務團隊和外掛的增加,很容易出現類重複問題,無法保證所有類都是獨一無二的。多類載入器機制是指每個外掛都由一個新的類載入器例項來載入,元件間的類是完全隔離,不能直接互相訪問。

利用ClassLoader的雙親委派機制,多類載入有兩種思路:

  • 自定義代理的ClassLoader設定為PathClassLoader的父類載入器,那麼自定義的類載入器就能代理所有的類載入行為;在代理ClassLoader內部做類載入的邏輯分發,先嚐試從宿主的ClassLoader載入,再嘗試外掛的ClassLoader載入。(好處:只需要在啟動時hook ClassLoader,新增DelegateClassLoader,後續的類載入由DelegateClassLoader分發;對於未載入的外掛,可以通過包名匹配,先觸發外掛載入,再載入類)
  • 每個PluginLoadedApk維護一個PluginClassLoader例項,其父ClassLoader是PathClassLoader;在類載入時,先嚐試從宿主的ClassLoader載入,再嘗試本外掛的ClassLoader載入。(好處:每個外掛維護自己的PluginLoadedApk,不存在分發,類隔離做的更好)

資源載入

Android APP執行除了類還有資源,執行時需要載入資源;對於Android來說,資源是通過AssetManager和Resources這兩個類管理。App在執行時查詢資源是通過當前Context的Resource例項中查詢,在Resource內部是通過AssetManager管理當前的資源,AssetManager維護了資源包路徑的陣列。外掛化的原理,就是將外掛的資源路徑新增到AssetManager的資源路徑陣列中,通過反射AssetManager的隱藏方法addAssetPath實現外掛資源的載入。

try{
    AssetManager am = AssetManager.class.newInstance();
    Method addAssetPath = AssetManager.class.getDeclaredMethod("addAssetPath", String.class);
    addAssetPath.setAccessible(true);
    addAssetPath.invoke(am, pluginApkPath);
    Resources pluginResources = new Resources(am, hostResource.getDisplayMetrics(), hostResources.getConfiguration());
} catch (Exception e) {
    e.printStackTrace();
}
複製程式碼

各種外掛化方案的資源載入原理都是一樣,區別主要在於不同外掛的資源管理,是公用一套資源還是外掛獨立資源,外掛和宿主的資源訪問ID衝突問題。

  • 公用一套資源需要採用固定資源id及ID分段機制避免衝突
  • 獨立資源方案,不同外掛管理自己的資源

外掛化中資源使用限制

限制:外掛不能使用自己的轉場動畫,只能使用宿主、系統定義的轉場動畫。

轉場動畫最終會呼叫到IActivityManager,發起IPC請求,與AMS互動

    public void overridePendingTransition(IBinder token, String packageName,
            int enterAnim, int exitAnim) throws RemoteException;
複製程式碼

Apk打包流程

先附上兩張Android原生打包流程圖

Android外掛化原理分析(基於Neptune框架)

Android外掛化原理分析(基於Neptune框架)

在外掛編譯打包時,需要完成以下幾件事:

  • 外掛的資源和宿主的資源通過不同的資源分段區分
  • 在外掛化中,如果外掛需要引用宿主的資源,則需要將宿主的資源id進行固定
  • 處理外掛aapt的編譯產物,不將宿主的資源打入apk中
  • 處理Manifest檔案,將佔坑的四大元件寫入Manifest檔案中
  • 在位元組碼層面對程式碼做修改

Hook點

  • Hook MergeResources Task,將public.xml檔案拷貝至資源merge完成的目錄
  • Hook ProcessAndroidResources Task,修改生成的arsc檔案。
  • Hook ManifestProcessorTask, 在Manifest中插入特定資訊。
  • Hook dexTask/Transform,最原始碼的修改

四大元件的外掛化

Activity的外掛化

Activity啟動可以分為兩個階段:往AMS發起啟動Activity的請求、AMS校驗後執行Activity啟動。

往AMS發起請求

Android外掛化原理分析(基於Neptune框架)

在Android 8.0(api 26)以下,應用往AMS發起啟動Activity請求的流程如上。在Android 8.0及以上版本,AMN、AMP已經被棄用,而是使用ActivityManager類;參考文章

Hook點:
  • Hook Instrumentation類,代理execStartActivity方法
  • Hook AMN(<26)/ActivityManager(>=26),動態代理IActivityManager介面的例項物件

AMS校驗後啟動Activity

Android外掛化原理分析(基於Neptune框架)

Android P(api 28)對Activity的啟動過程做了修改;在Android P之前,是在H類的handleMessage方法的switch分支語句中,有專門處理啟動Activity的邏輯

 public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                } break;
            
            //以下省略很多程式碼
            }
        }
複製程式碼

在Android P中,啟動Activity的這部分邏輯,被轉移到了LaunchActivityItem類的execute方法中

public class LaunchActivityItem extends ClientTransactionItem {

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    }
}
複製程式碼

Android P把H類中的100-109這10個訊息都刪除了,取而代之的是159這個訊息,名為EXECUTE_TRANSACTION。收斂了Activity相關的message分發。

Hook點:
  • Hook H類,將佔坑Activity替換成真實的Activity(需要做Android P的適配)
  • Hook Instrumentation類,替換成自定義的Instrument,重寫newActivity、callActivityOnCreate等方法

Service的外掛化

Service啟動可以分為兩個階段:往AMS發起啟動Service的請求、AMS校驗後執行Service啟動。

往AMS發起啟動Service的請求

Android外掛化原理分析(基於Neptune框架)

在Android 8.0(api 26)以下,應用往AMS發起啟動Service請求的流程如上。在Android 8.0及以上版本,AMN、AMP已經被棄用,而是使用ActivityManager類。

Hook點
  • Hook ContextWrapper;替換成自定義的ContextWrapper,將Service替換成佔坑的Service
  • Hook AMN(<26)/ActivityManager(>=26),動態代理IActivityManager介面的例項物件

AMS校驗後執行Service啟動

Android外掛化原理分析(基於Neptune框架)

Hook點:
  • 在佔坑Service的onStartCommand提取真實Service資訊,並分發執行真實Service邏輯
  • Hook handleServiceCreate方法,發射獲取ServiceInfo,修改ServiceInfo的name欄位為真實Service的名字。載入真實的Service類。

BroadCastReceiver外掛化

Android外掛化原理分析(基於Neptune框架)

廣播分為靜態廣播、動態廣播。動態廣播在執行時向AMS註冊相關資訊。

Hook點:
  • 靜態廣播轉換為動態廣播

ContentProvider外掛化

Android外掛化原理分析(基於Neptune框架)

Hook點:
  • Hook AMN(<26)/ActivityManager(>=26),動態代理IActivityManager介面的例項物件;將目標ContentProvider的資訊放在query引數中

Neptune原始碼分析

Neptune類

Neptune類是整個外掛系統的入口類,主要方法如下

  • init()方法
/**
     * 初始化Neptune外掛環境
     *
     * @param application  宿主的Appliction
     * @param config  配置資訊
     */
    public static void init(Application application, NeptuneConfig config) {

        sHostContext = application;
        sGlobalConfig = config != null ? config
                : new NeptuneConfig.NeptuneConfigBuilder().build();

        PluginDebugLog.setIsDebug(sGlobalConfig.isDebug());

        boolean hookInstr = VersionUtils.hasPie() || sGlobalConfig.getSdkMode() != NeptuneConfig.LEGACY_MODE;
        if (hookInstr) {
            //Hook Instrumentation
            hookInstrumentation();
        }

        // 呼叫getInstance()方法會初始化bindService,管理外掛安裝的Service
        PluginPackageManagerNative.getInstance(sHostContext).setPackageInfoManager(sGlobalConfig.getPluginInfoProvider());
        // 註冊外掛解除安裝監聽廣播
        PluginManager.registerUninstallReceiver(sHostContext);
    }
複製程式碼
  • launchPlugin()方法
 /**
     * 啟動外掛
     *
     * @param mHostContext       主工程的上下文
     * @param mIntent            需要啟動的元件的Intent
     * @param mServiceConnection bindService時需要的ServiceConnection,如果不是bindService的方式啟動元件,傳入Null
     * @param mProcessName       需要啟動的外掛執行的程式名稱,外掛方可以在Application的android:process指定
     *                           如果沒有指定,則有外掛中心分配
     */
    public static void launchPlugin(final Context mHostContext,
                                    final Intent mIntent,
                                    final ServiceConnection mServiceConnection,
                                    final String mProcessName) {
        final String packageName = tryParsePkgName(mHostContext, mIntent);
        if (TextUtils.isEmpty(packageName)) {
            if (null != mHostContext) {
                deliver(mHostContext, false, mHostContext.getPackageName(), ErrorType.ERROR_PLUGIN_LOAD_NO_PKGNAME_INTENT);
            }
            PluginDebugLog.runtimeLog(TAG, "enterProxy packageName is null return! packageName: " + packageName);
            return;
        }
        // 處理不同程式跳轉
        final String targetProcessName = TextUtils.isEmpty(mProcessName) ?
                ProcessManager.chooseDefaultProcess(mHostContext, packageName) : mProcessName;
        String currentProcess = FileUtils.getCurrentProcessName(mHostContext);
        if (!TextUtils.equals(currentProcess, targetProcessName)) {
            // 啟動程式和目標程式不一致,需要先啟動目標程式,初始化PluginLoadedApk
            Intent transIntent = new Intent();
            transIntent.setAction(IntentConstant.ACTION_START_PLUGIN);
            //目標程式的Service中重新通過mIntent啟動外掛
            transIntent.putExtra(IntentConstant.EXTRA_START_INTENT_KEY, mIntent);
            transIntent.putExtra(IntentConstant.EXTRA_TARGET_PROCESS, targetProcessName);
            try {
                String proxyServiceName = ComponentFinder.matchServiceProxyByFeature(targetProcessName);
                transIntent.setClass(mHostContext, Class.forName(proxyServiceName));
                mHostContext.startService(transIntent);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return;
        }

        LinkedBlockingQueue<Intent> cacheIntents = PActivityStackSupervisor.getCachedIntent(packageName);
        //該外掛有其他任務排隊中,mIntent新增到隊尾
        if (cacheIntents != null && cacheIntents.size() > 0) {
            cacheIntents.add(mIntent);
            PluginDebugLog.runtimeLog(TAG, "LoadingMap is not empty, Cache current intent, intent: " + mIntent + ", packageName: " + packageName);
            return;
        }

        boolean isLoadAndInit = isPluginLoadedAndInit(packageName);
        if (!isLoadAndInit) {
            if (null == cacheIntents) {
                cacheIntents = new LinkedBlockingQueue<Intent>();
                PActivityStackSupervisor.addCachedIntent(packageName, cacheIntents);
            }
            // 快取這個intent,等待PluginLoadedApk載入到記憶體之後再啟動這個Intent
            PluginDebugLog.runtimeLog(TAG, "Environment is initializing and loading, cache current intent first, intent: " + mIntent);
            cacheIntents.add(mIntent);
        } else {
            PluginDebugLog.runtimeLog(TAG, "Environment is already ready, launch current intent directly: " + mIntent);
            //可以直接啟動元件
            readyToStartSpecifyPlugin(mHostContext, mServiceConnection, mIntent, true);
            return;
        }

        // 處理外掛的依賴關係
        final PluginLiteInfo info = PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext())
                .getPackageInfo(packageName);
                //獲取外掛的依賴關係
        final List<String> mPluginRefs = PluginPackageManagerNative.getInstance(mHostContext)
                .getPluginRefs(packageName);
        if (info != null && mPluginRefs != null
                && mPluginRefs.size() > 0) {
            PluginDebugLog.runtimeLog(TAG,
                    "start to check dependence installation size: " + mPluginRefs.size());
            //依賴的總數量
            final AtomicInteger count = new AtomicInteger(mPluginRefs.size());
            for (String pkgName : mPluginRefs) {
                PluginDebugLog.runtimeLog(TAG, "start to check installation pkgName: " + pkgName);
                final PluginLiteInfo refInfo = PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext())
                        .getPackageInfo(pkgName);

                PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext()).packageAction(refInfo,
                        new IInstallCallBack.Stub() {
                            @Override
                            public void onPackageInstalled(PluginLiteInfo packageInfo) {
                                //未ready的依賴數量
                                count.getAndDecrement();
                                PluginDebugLog.runtimeLog(TAG, "check installation success pkgName: " + refInfo.packageName);
                                if (count.get() == 0) {
                                    PluginDebugLog.runtimeLog(TAG,
                                            "start Check installation after check dependence packageName: "
                                                    + packageName);
                                    //真正載入外掛
                                    checkPkgInstallationAndLaunch(mHostContext, info, mServiceConnection, mIntent, targetProcessName);
                                }
                            }

                            @Override
                            public void onPackageInstallFail(PluginLiteInfo info, int failReason) throws RemoteException {
                                PluginDebugLog.runtimeLog(TAG,
                                        "check installation failed pkgName: " + info.packageName + " failReason: " + failReason);
                                count.set(-1);
                            }
                        });
            }
        } else if (info != null) {
            PluginDebugLog.runtimeLog(TAG, "start Check installation without dependence packageName: " + packageName);
            //真正載入外掛
            checkPkgInstallationAndLaunch(mHostContext, info, mServiceConnection, mIntent, targetProcessName);
        } else {
            //異常case
            PluginDebugLog.runtimeLog(TAG, "pluginLiteInfo is null packageName: " + packageName);
            PActivityStackSupervisor.clearLoadingIntent(packageName);
            if (PluginDebugLog.isDebug()) {
                throw new IllegalStateException("pluginLiteInfo is null when launchPlugin " + packageName);
            }
        }
    }
複製程式碼
/**
     * 真正啟動一個元件
     *
     * @param mHostContext 主工程Context
     * @param mLoadedApk   需要啟動的外掛的PluginLoadedApk
     * @param mIntent      需要啟動元件的Intent
     * @param mConnection  bindService時需要的ServiceConnection,如果不是bindService的方式啟動元件,傳入Null
     */
    private static void doRealLaunch(Context mHostContext,
                                     PluginLoadedApk mLoadedApk,
                                     Intent mIntent,
                                     ServiceConnection mConnection) {

        String targetClassName = "";
        ComponentName mComponent = mIntent.getComponent();
        if (mComponent != null) {
            //顯式啟動
            targetClassName = mComponent.getClassName();
            PluginDebugLog.runtimeLog(TAG, "launchIntent_targetClassName:" + targetClassName);
            if (TextUtils.isEmpty(targetClassName)) {
                targetClassName = mLoadedApk.getPluginPackageInfo().getDefaultActivityName();
            }
        }

        String pkgName = mLoadedApk.getPluginPackageName();
        Class<?> targetClass = null;
        if (!TextUtils.isEmpty(targetClassName)
                && !TextUtils.equals(targetClassName, IntentConstant.EXTRA_VALUE_LOADTARGET_STUB)) {
            try {
                //外掛ClassLoader載入類
                targetClass = mLoadedApk.getPluginClassLoader().loadClass(targetClassName);
            } catch (Exception e) {
                deliver(mHostContext, false,
                        pkgName, ErrorType.ERROR_PLUGIN_LOAD_COMP_CLASS);
                PluginDebugLog.runtimeLog(TAG, "launchIntent loadClass failed for targetClassName: "
                        + targetClassName);
                executeNext(mLoadedApk, mConnection, mHostContext);
                return;
            }
        }

        String action = mIntent.getAction();

        if (TextUtils.equals(action, IntentConstant.ACTION_PLUGIN_INIT)
                || TextUtils.equals(targetClassName, IntentConstant.EXTRA_VALUE_LOADTARGET_STUB)) {
            PluginDebugLog.runtimeLog(TAG, "launchIntent load target stub!");
            //通知外掛初始化完畢
            if (targetClass != null && BroadcastReceiver.class.isAssignableFrom(targetClass)) {
                Intent newIntent = new Intent(mIntent);
                newIntent.setComponent(null);
                newIntent.putExtra(IntentConstant.EXTRA_TARGET_PACKAGE_KEY, pkgName);
                newIntent.setPackage(mHostContext.getPackageName());
                //通過廣播通知外掛載入完成
                mHostContext.sendBroadcast(newIntent);
            }
            // 表示後臺載入,不需要處理該Intent
            executeNext(mLoadedApk, mConnection, mHostContext);
            return;
        }

        mLoadedApk.changeLaunchingIntentStatus(true);
        PluginDebugLog.runtimeLog(TAG, "launchIntent_targetClass: " + targetClass);
        if (targetClass != null && Service.class.isAssignableFrom(targetClass)) {
            //處理的是Service, 宿主啟動外掛Service只能通過顯式啟動
            ComponentFinder.switchToServiceProxy(mLoadedApk, mIntent, targetClassName);
            if (mConnection == null) {
                mHostContext.startService(mIntent);
            } else {
                mHostContext.bindService(mIntent, mConnection,
                        mIntent.getIntExtra(IntentConstant.BIND_SERVICE_FLAGS, Context.BIND_AUTO_CREATE));
            }
        } else {
            //處理的是Activity
            ComponentFinder.switchToActivityProxy(pkgName,
                    mIntent, -1, mHostContext);
            PActivityStackSupervisor.addLoadingIntent(pkgName, mIntent);
            Context lastActivity = null;
            PActivityStackSupervisor mActivityStackSupervisor =
                    mLoadedApk.getActivityStackSupervisor();
            lastActivity = mActivityStackSupervisor.getAvailableActivity();
            if (mHostContext instanceof Activity) {
                mHostContext.startActivity(mIntent);
            } else if (lastActivity != null) {
                // Clear the Intent.FLAG_ACTIVITY_NEW_TASK
                int flag = mIntent.getFlags();
                flag = flag ^ Intent.FLAG_ACTIVITY_NEW_TASK;
                mIntent.setFlags(flag);
                lastActivity.startActivity(mIntent);
            } else {
                // Add the Intent.FLAG_ACTIVITY_NEW_TASK
                mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mHostContext.startActivity(mIntent);
            }
        }
        // 執行下一個Intent
        executeNext(mLoadedApk, mConnection, mHostContext);
    }
複製程式碼
 /**
     * 非同步初始化外掛,宿主靜默載入外掛
     *
     * @deprecated 不建議使用
     */
    @Deprecated
    public static void initPluginAsync(final Context mHostContext,
                                       final String packageName,
                                       final String processName,
                                       final org.qiyi.pluginlibrary.listenter.IPluginStatusListener mListener) {
        // 外掛已經載入
        if (PluginManager.isPluginLoadedAndInit(packageName)) {
            if (mListener != null) {
                mListener.onInitFinished(packageName);
            }
            return;
        }

        BroadcastReceiver recv = new BroadcastReceiver() {
            public void onReceive(Context ctx, Intent intent) {
                String curPkg = IntentUtils.getTargetPackage(intent);
                if (IntentConstant.ACTION_PLUGIN_INIT.equals(intent.getAction()) && TextUtils.equals(packageName, curPkg)) {
                    PluginDebugLog.runtimeLog(TAG, "收到自定義的廣播org.qiyi.pluginapp.action.TARGET_LOADED");
                    //外掛初始化結束
                    if (mListener != null) {
                        mListener.onInitFinished(packageName);
                    }
                    mHostContext.getApplicationContext().unregisterReceiver(this);
                }
            }
        };
        PluginDebugLog.runtimeLog(TAG, "註冊自定義廣播org.qiyi.pluginapp.action.TARGET_LOADED");
        IntentFilter filter = new IntentFilter();
        filter.addAction(IntentConstant.ACTION_PLUGIN_INIT);
        //註冊廣播
        mHostContext.getApplicationContext().registerReceiver(recv, filter);

        Intent intent = new Intent();
        intent.setAction(IntentConstant.ACTION_PLUGIN_INIT);
        intent.setComponent(new ComponentName(packageName, recv.getClass().getName()));
        //傳送一個啟動外掛的intent
        launchPlugin(mHostContext, intent, processName);
    }
複製程式碼

NeptuneInstrument(PluginInstrument)類

Hook系統原生的Instrument,替換成NeptuneInstrument。NeptuneInstrument繼承PluginInstrumentPluginInstrument負責往AMS傳送的請求,NeptuneInstrument負責AMS返回的結果處理。

/**
 * 負責轉移外掛的跳轉目標<br>
 * 用於Hook外掛Activity中Instrumentation
 *
 * @see android.app.Activity#startActivity(android.content.Intent)
 */
public class PluginInstrument extends Instrumentation {
    private static final String TAG = "PluginInstrument";

    private static ConcurrentMap<String, Vector<Method>> sMethods = new ConcurrentHashMap<String, Vector<Method>>(5);
    Instrumentation mHostInstr;
    private String mPkgName;
    private ReflectionUtils mInstrumentRef;

    /**
     * 外掛的Instrumentation
     */
    public PluginInstrument(Instrumentation hostInstr) {
        this(hostInstr, "");
    }

    public PluginInstrument(Instrumentation hostInstr, String pkgName) {
        mHostInstr = hostInstr;
        mInstrumentRef = ReflectionUtils.on(hostInstr);
        mPkgName = pkgName;
    }

    /**
     * 如果是PluginInstrumentation,拆裝出原始的HostInstr
     *
     * @param instrumentation
     * @return
     */
    public static Instrumentation unwrap(Instrumentation instrumentation) {
        if (instrumentation instanceof PluginInstrument) {
            return ((PluginInstrument) instrumentation).mHostInstr;
        }
        return instrumentation;
    }

    /**
     * @Override
     */
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        ...
    }

    /**
     * @Override
     */
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode) {
        ...
    }

    /**
     * @Override For below android 6.0
     */
    public ActivityResult execStartActivityAsCaller(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options, int userId) {
        ...
    }

    /**
     * @Override For android 6.0
     */
    public ActivityResult execStartActivityAsCaller(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options,
            boolean ignoreTargetSecurity, int userId) {
        ...
    }

    /**
     * @Override
     */
    public void execStartActivitiesAsUser(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent[] intents, Bundle options, int userId) {
        ...
    }

    /**
     * @Override For below android 6.0, start activity from Fragment
     */
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Fragment target,
            Intent intent, int requestCode, Bundle options) {
        ...
    }

    /**
     * @Override For android 6.0, start activity from Fragment
     */
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, String target,
            Intent intent, int requestCode, Bundle options) {
       ...
    }
}

複製程式碼
/**
 * 自定義的全域性的Instrumentation
 * 負責轉移外掛的跳轉目標和建立外掛的Activity例項
 * 用於Hook ActivityThread中的全域性Instrumentation
 */
public class NeptuneInstrument extends PluginInstrument {

    private static final String TAG = "NeptuneInstrument";
    private PluginActivityRecoveryHelper mRecoveryHelper = new PluginActivityRecoveryHelper();

    public NeptuneInstrument(Instrumentation hostInstr) {
        super(hostInstr);
    }

    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        ...
    }

    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        ...
    }

    @Override
    public void callActivityOnDestroy(Activity activity) {
        ...
    }

    @Override
    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
        ...
    }

    /**
     * 將Activity反射相關操作分發給外掛Activity的基類
     */
    private boolean dispatchToBaseActivity(Activity activity) {

        //這個模式已棄用
        return Neptune.getConfig().getSdkMode() == NeptuneConfig.INSTRUMENTATION_BASEACT_MODE
                && activity instanceof IPluginBase;
    }
}
複製程式碼

NeptuneInstrument類中的幾個方法有一些特殊的邏輯處理,下面單獨分析:

@Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        if (className.startsWith(ComponentFinder.DEFAULT_ACTIVITY_PROXY_PREFIX)) {
            // 外掛代理Activity,替換回外掛真實的Activity
            String[] result = IntentUtils.parsePkgAndClsFromIntent(intent);
            String packageName = result[0];
            String targetClass = result[1];

            PluginDebugLog.runtimeLog(TAG, "newActivity: " + className + ", targetClass: " + targetClass);
            if (!TextUtils.isEmpty(packageName)) {
                //找到對應外掛
                PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(packageName);
                if (loadedApk != null && targetClass != null) {
                    Activity activity = mHostInstr.newActivity(loadedApk.getPluginClassLoader(), targetClass, intent);
                    activity.setIntent(intent);

                    if (!dispatchToBaseActivity(activity)) {
                        // 這裡需要替換Resources,是因為ContextThemeWrapper會快取一個Resource物件,而在Activity#attach()和
                        // Activity#onCreate()之間,系統會呼叫Activity#setTheme()初始化主題,Android 4.1+
                        //替換成外掛的Resource資源
                        ReflectionUtils.on(activity).setNoException("mResources", loadedApk.getPluginResource());
                    }

                    return activity;
                } else if (loadedApk == null) {
                    // loadedApk 為空,可能是正在恢復程式,跳轉到 RecoveryActivity
                    return mHostInstr.newActivity(cl, mRecoveryHelper.selectRecoveryActivity(className), intent);
                }
            }
        }
        return mHostInstr.newActivity(cl, className, intent);
    }
複製程式碼
    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        boolean isRecovery = activity instanceof TransRecoveryActivity0;
        if (isRecovery) {
        //外掛載入中的Activity,使用宿主的Instrument
            mRecoveryHelper.saveIcicle(activity, icicle);
            mHostInstr.callActivityOnCreate(activity, null);
            return;
        }
        final Intent intent = activity.getIntent();
        String[] result = IntentUtils.parsePkgAndClsFromIntent(intent);
        boolean isLaunchPlugin = false;
        if (IntentUtils.isIntentForPlugin(intent)) {
            String packageName = result[0];
            String targetClass = result[1];
            if (!TextUtils.isEmpty(packageName)) {
                PluginDebugLog.runtimeLog(TAG, "callActivityOnCreate: " + packageName);
                PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(packageName);
                if (loadedApk != null) {
                    icicle = mRecoveryHelper.recoveryIcicle(activity, icicle);
                    // 設定 extra 的 ClassLoader,不然可能會出現 BadParcelException, ClassNotFound
                    if (icicle != null) {
                        icicle.setClassLoader(loadedApk.getPluginClassLoader());
                    }
                    if (!dispatchToBaseActivity(activity)) {
                        // 如果分發給外掛Activity的基類了,就不需要在這裡反射hook替換相關成員變數了
                        try {
                            ReflectionUtils activityRef = ReflectionUtils.on(activity);
                            //設定為外掛資源
                            activityRef.setNoException("mResources", loadedApk.getPluginResource());
                            //設定外掛的Application
                            activityRef.setNoException("mApplication", loadedApk.getPluginApplication());
                            Context pluginContext = new PluginContextWrapper(activity.getBaseContext(), packageName);
                            //替換為PluginContextWrapper,處理inflate相關
                            ReflectionUtils.on(activity, ContextWrapper.class).set("mBase", pluginContext);
                            // 5.0以下ContextThemeWrapper內會儲存一個mBase,也需要反射替換掉
                            ReflectionUtils.on(activity, ContextThemeWrapper.class).setNoException("mBase", pluginContext);
                            //替換為外掛的Instrumentation
                            ReflectionUtils.on(activity).setNoException("mInstrumentation", loadedApk.getPluginInstrument());

                            // 修改外掛Activity的ActivityInfo, theme, window等資訊
                            PluginActivityControl.changeActivityInfo(activity, targetClass, loadedApk);
                        } catch (Exception e) {
                        PluginDebugLog.runtimeLog(TAG, "callActivityOnCreate with exception: " + e.getMessage());
                        }

                    }

                    if (activity.getParent() == null) {
                    //Activity棧的邏輯是怎麼處理的?
                        loadedApk.getActivityStackSupervisor().pushActivityToStack(activity);
                    }
                    isLaunchPlugin = true;
                }
            }
            IntentUtils.resetAction(intent);  //恢復Action
        }

        try {
            mHostInstr.callActivityOnCreate(activity, icicle);

            if (isLaunchPlugin) {
                NotifyCenter.notifyPluginStarted(activity, intent);
                NotifyCenter.notifyPluginActivityLoaded(activity);
            }
            //check是否需要hook callActivityOnRestoreInstanceState方法
            mRecoveryHelper.mockActivityOnRestoreInstanceStateIfNeed(this, activity);
        } catch (Exception e) {
            ErrorUtil.throwErrorIfNeed(e);
            if (isLaunchPlugin) {
                NotifyCenter.notifyStartPluginError(activity);
            }
            activity.finish();
        }
    }
複製程式碼
 @Override
    public void callActivityOnDestroy(Activity activity) {
        mHostInstr.callActivityOnDestroy(activity);
        if (activity.getParent() != null) {
            return;
        }

        final Intent intent = activity.getIntent();
        String pkgName = IntentUtils.parsePkgNameFromActivity(activity);
        if (IntentUtils.isIntentForPlugin(intent)
                || intent == null) {
            // intent為null時,如果能夠從Activity中解析出pkgName,也應該是外掛的頁面
            if (!TextUtils.isEmpty(pkgName)) {
                PluginDebugLog.runtimeLog(TAG, "callActivityOnDestroy: " + pkgName);
                PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(pkgName);
                if (loadedApk != null) {
                //退出外掛的Activity棧
                    loadedApk.getActivityStackSupervisor().popActivityFromStack(activity);
                }
            }
        }
    }
複製程式碼
  @Override
    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
        if (activity instanceof TransRecoveryActivity0) {
            mRecoveryHelper.saveSavedInstanceState(activity, savedInstanceState);
            return;
        }
        if (IntentUtils.isIntentForPlugin(activity.getIntent())) {
            String pkgName = IntentUtils.parsePkgAndClsFromIntent(activity.getIntent())[0];
            PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(pkgName);
            if (loadedApk != null && savedInstanceState != null) {
            //用外掛的ClassLoader恢復資料
                savedInstanceState.setClassLoader(loadedApk.getPluginClassLoader());
            }
        }
        mHostInstr.callActivityOnRestoreInstanceState(activity, savedInstanceState);
    }
複製程式碼

外掛Activity任務棧

/**
 * 外掛的Activity棧抽象, 和系統的{@link com.android.server.am.ActivityStack}類似
 */
public class PActivityStack {
    private final LinkedList<Activity> mActivities;
    // taskAffinity
    private String taskName;

    PActivityStack(String taskName) {
        this.taskName = taskName;
        mActivities = new LinkedList<>();
    }

    /**
     * 獲取當前任務棧的名稱
     */
    public String getTaskName() {
        return taskName;
    }

    public LinkedList<Activity> getActivities() {
        return mActivities;
    }

    public int size() {
        return mActivities.size();
    }

    public synchronized boolean isEmpty() {
        return mActivities.isEmpty();
    }

    // 放入連結串列的前面
    public synchronized void push(Activity activity) {
        mActivities.addFirst(activity);
    }

    public synchronized void insertFirst(Activity activity) {
        mActivities.addLast(activity);
    }

    public synchronized boolean pop(Activity activity) {
        return mActivities.remove(activity);
    }

    public synchronized Activity getTop() {
        return mActivities.getFirst();
    }

    /**
     * 清空當前任務棧裡的Activity
     */
    public void clear(boolean needFinish) {
        Iterator<Activity> iterator = mActivities.iterator();
        while (iterator.hasNext()) {
            Activity activity = iterator.next();
            if (activity != null && needFinish
                    && !FileUtils.isFinished(activity)) {
                activity.finish();
            }
            iterator.remove();
        }
    }
}
複製程式碼

PActivityStackSupervisor管理Activity任務棧

    /**
     * 處理Activity的launchMode,給Intent新增相關的Flags
     */
    public void dealLaunchMode(Intent intent) {
        if (null == intent) {
            return;
        }

        String targetActivity = IntentUtils.getTargetClass(intent);
        if (TextUtils.isEmpty(targetActivity)) {
            return;
        }

        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode target activity: " + intent + " source: "
                + targetActivity);
        // 不支援LAUNCH_SINGLE_INSTANCE
        ActivityInfo info = mLoadedApk.getPluginPackageInfo().getActivityInfo(targetActivity);
        if (info == null || info.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
            return;
        }
        boolean isSingleTop = info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
                || (intent.getFlags() & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0;
        boolean isSingleTask = info.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
        boolean isClearTop = (intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0;
        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode isSingleTop " + isSingleTop + " isSingleTask "
                + isSingleTask + " isClearTop " + isClearTop);
        int flag = intent.getFlags();
        PluginDebugLog.runtimeLog(TAG, "before flag: " + Integer.toHexString(intent.getFlags()));
        if ((isSingleTop || isSingleTask) && (flag & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) {
            flag = flag ^ Intent.FLAG_ACTIVITY_SINGLE_TOP;
        }
        if ((isSingleTask || isClearTop) && (flag & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            flag = flag ^ Intent.FLAG_ACTIVITY_CLEAR_TOP;
        }
        intent.setFlags(flag);
        PluginDebugLog.runtimeLog(TAG, "after flag: " + Integer.toHexString(intent.getFlags()));

        if (isSingleTop && !isClearTop) {
            // 判斷棧頂是否為需要啟動的Activity, 只需要處理前臺棧
            Activity activity = null;
            if (!mFocusedStack.isEmpty()) {
                activity = mFocusedStack.getTop();
            }
            boolean hasSameActivity = false;
            String proxyClsName = ComponentFinder.findActivityProxy(mLoadedApk, info);
            if (activity != null) {
                // 棧內有例項, 可能是ProxyActivity,也可能是外掛真實的Activity
                //Fix: 新的實現中只有外掛真實的Activity
                if (TextUtils.equals(proxyClsName, activity.getClass().getName())
                        || TextUtils.equals(targetActivity, activity.getClass().getName())) {
                    String key = getActivityStackKey(activity);

                    if (!TextUtils.isEmpty(key) && TextUtils.equals(targetActivity, key)) {
                        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                        hasSameActivity = true;
                    }
                }
            }
            if (hasSameActivity) {
                handleOtherPluginActivityStack(activity, mFocusedStack);
            }
        } else if (isSingleTask || isClearTop) {

            PActivityStack targetStack; // 需要搜尋的任務棧
            boolean fromBackStack = false;
            if (isClearTop) {
                targetStack = mFocusedStack;
            } else {
                // singleTask
                if (mLastFocusedStack != null
                        && TextUtils.equals(mLastFocusedStack.getTaskName(), matchTaskName(info.taskAffinity))) {
                    // 後臺棧和Activity的taskAffinity匹配
                    targetStack = mLastFocusedStack;
                    fromBackStack = true;
                    PluginDebugLog.runtimeLog(TAG, "dealLaunchMode search in background stack: " + info.taskAffinity);
                } else {
                    // 前臺棧中搜尋
                    targetStack = mFocusedStack;
                }
            }
            // 查詢棧中是否存在已有例項
            Activity found = null;
            // 遍歷已經起過的activity
            for (Activity activity : targetStack.getActivities()) {
                String proxyClsName = ComponentFinder.findActivityProxy(mLoadedApk, info);
                if (activity != null) {
                    if (TextUtils.equals(proxyClsName, activity.getClass().getName())
                            || TextUtils.equals(targetActivity, activity.getClass().getName())) {
                        String key = getActivityStackKey(activity);
                        if (!TextUtils.isEmpty(key) && TextUtils.equals(targetActivity, key)) {
                            PluginDebugLog.runtimeLog(TAG, "dealLaunchMode found:" + IntentUtils.dump(activity));
                            found = activity;
                            break;
                        }
                    }
                }
            }

            // 棧中已經有當前activity
            if (found != null) {
                // 處理其他外掛的邏輯
                // 在以這兩種SingleTask, ClearTop flag啟動情況下,在同一個棧的情況下
                handleOtherPluginActivityStack(found, targetStack);

                // 處理當前外掛的Activity
                List<Activity> popActivities = new ArrayList<Activity>(5);
                for (Activity activity : targetStack.getActivities()) {
                    if (activity == found) {
                        if (isSingleTask || isSingleTop) {
                            PluginDebugLog.runtimeLog(TAG, "dealLaunchMode add single top flag!");
                            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                        }
                        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode add clear top flag!");
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        break;
                    }
                    popActivities.add(activity);
                }

                for (Activity act : popActivities) {
                    PluginDebugLog.runtimeLog(TAG, "dealLaunchMode popActivities finish " + IntentUtils.dump(act));
                    popActivityFromStack(act);
                    if (!FileUtils.isFinished(act)) {
                        act.finish();
                    }
                }

                // 如果Activity是在後臺堆疊中找到的,需要合併前後臺棧
                if (fromBackStack) {
                    // https://developer.android.com/guide/components/activities/tasks-and-back-stack
                    // 把返回棧中的Activity全部推到前臺
                    PActivityStack sysForeStack = findAssociatedStack(mFocusedStack);
                    PActivityStack sysBackStack = findAssociatedStack(mLastFocusedStack);
                    mergeActivityStack(sysBackStack, sysForeStack);
                    // 處理外掛自身的棧
                    mergeActivityStack(mLastFocusedStack, mFocusedStack);
                    // 切換前後臺堆疊
                    switchToBackStack(mFocusedStack, mLastFocusedStack);
                }

                mLoadedApk.quitApp(false);
            } else {
                // 堆疊裡沒有找到,遍歷還未啟動cache中的activity記錄
                LinkedBlockingQueue<Intent> records = sIntentCacheMap
                        .get(mLoadedApk.getPluginPackageName());
                if (null != records) {
                    Iterator<Intent> recordIterator = records.iterator();
                    String notLaunchTargetClassName = null;
                    while (recordIterator.hasNext()) {
                        Intent record = recordIterator.next();
                        if (null != record) {
                            if (null != record.getComponent()) {
                                notLaunchTargetClassName = record.getComponent().getClassName();
                            }
                            if (TextUtils.equals(notLaunchTargetClassName, targetActivity)) {
                                PluginDebugLog.runtimeLog(TAG, "sIntentCacheMap found: " + targetActivity);
                                if (isSingleTask || isSingleTop) {
                                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                }
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                                break;
                            }
                        }
                    }
                }
                // 遍歷啟動過程中的activity記錄
                List<Intent> loadingIntents = sIntentLoadingMap.get(mLoadedApk.getPluginPackageName());
                if (null != loadingIntents) {
                    Iterator<Intent> loadingRecordIterator = loadingIntents.iterator();
                    String notLaunchTargetClassName = null;
                    while (loadingRecordIterator.hasNext()) {
                        Intent record = loadingRecordIterator.next();
                        if (null != record) {
                            notLaunchTargetClassName = IntentUtils.getTargetClass(record);
                            if (TextUtils.equals(notLaunchTargetClassName, targetActivity)) {
                                PluginDebugLog.runtimeLog(TAG,
                                        "sIntentLoadingMap found: " + targetActivity);
                                if (isSingleTask || isSingleTop) {
                                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                }
                                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                                break;
                            }
                        }
                    }
                }

                if (isSingleTask) {
                    // 是否需要放到單獨的任務棧
                    String taskName = matchTaskName(info.taskAffinity);
                    if (!TextUtils.equals(mFocusedStack.getTaskName(), taskName)) {
                        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode push activity into separated stack: " + taskName);
                        PActivityStack stack = mActivityStacks.get(taskName);
                        if (stack == null) {
                            // 建立一個新的任務棧
                            stack = new PActivityStack(taskName);
                            mActivityStacks.put(taskName, stack);
                        }
                        // 切換前後臺棧
                        switchToBackStack(mFocusedStack, stack);
                    } else {
                        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode push activity into current stack: " + taskName);
                    }
                }
            }
        }
        PluginDebugLog.runtimeLog(TAG, "dealLaunchMode end: " + intent + " "
                + targetActivity);
    }
複製程式碼

處理外掛中的廣播

  /**
     * 動態註冊外掛中的靜態Receiver
     */
    private void installStaticReceiver() {
        if (mPluginPackageInfo == null || mHostContext == null) {
            return;
        }
        Map<String, PluginPackageInfo.ReceiverIntentInfo> mReceiverIntentInfos =
                mPluginPackageInfo.getReceiverIntentInfos();
        if (mReceiverIntentInfos != null) {
            Set<Map.Entry<String, PluginPackageInfo.ReceiverIntentInfo>> mEntrys =
                    mReceiverIntentInfos.entrySet();
            Context mGlobalContext = mHostContext.getApplicationContext();
            for (Map.Entry<String, PluginPackageInfo.ReceiverIntentInfo> mEntry : mEntrys) {
                PluginPackageInfo.ReceiverIntentInfo mReceiverInfo = mEntry.getValue();
                if (mReceiverInfo != null) {
                    try {
                        BroadcastReceiver mReceiver =
                                BroadcastReceiver.class.cast(mPluginClassLoader.
                                        loadClass(mReceiverInfo.mInfo.name).newInstance());
                        List<IntentFilter> mFilters = mReceiverInfo.mFilter;
                        if (mFilters != null) {
                            for (IntentFilter mItem : mFilters) {
                                mGlobalContext.registerReceiver(mReceiver, mItem);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
複製程式碼

處理外掛中的Service

PluginContextWrapper類中完成了startService等方法的代理

    @Override
    public ComponentName startService(Intent service) {
        PluginDebugLog.log(TAG, "startService: " + service);
        PluginLoadedApk mLoadedApk = getPluginLoadedApk();
        if (mLoadedApk != null) {
            ComponentFinder.switchToServiceProxy(mLoadedApk, service);
        }
        return super.startService(service);
    }

    @Override
    public boolean stopService(Intent name) {
        PluginDebugLog.log(TAG, "stopService: " + name);
        PluginLoadedApk mLoadedApk = getPluginLoadedApk();
        if (mLoadedApk != null) {
            String actServiceClsName = "";
            if (name.getComponent() != null) {
                actServiceClsName = name.getComponent().getClassName();
            } else {
                ServiceInfo mServiceInfo = getPluginPackageInfo().resolveService(name);
                if (mServiceInfo != null) {
                    actServiceClsName = mServiceInfo.name;
                }
            }

            PluginServiceWrapper plugin = PServiceSupervisor
                    .getServiceByIdentifer(PluginServiceWrapper.getIdentify(getPluginPackageName(), actServiceClsName));
            if (plugin != null) {
                plugin.updateServiceState(PluginServiceWrapper.PLUGIN_SERVICE_STOPED);
                plugin.tryToDestroyService();
                return true;
            }
        }
        return super.stopService(name);
    }

    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        PluginDebugLog.log(TAG, "bindService: " + service);
        PluginLoadedApk mLoadedApk = getPluginLoadedApk();
        if (mLoadedApk != null) {
            ComponentFinder.switchToServiceProxy(mLoadedApk, service);
        }
        if (conn != null) {
            if (mLoadedApk != null && service != null) {
                String serviceClass = IntentUtils.getTargetClass(service);
                String packageName = mLoadedApk.getPluginPackageName();
                if (!TextUtils.isEmpty(serviceClass) && !TextUtils.isEmpty(packageName)) {
                    PServiceSupervisor.addServiceConnectionByIdentifer(packageName + "." + serviceClass, conn);
                }
            }
        }
        return super.bindService(service, conn, flags);
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        PServiceSupervisor.removeServiceConnection(conn);
        PluginDebugLog.log(TAG, "unbindService: " + conn);
    }
複製程式碼

總結

Neptune框架註釋比較清晰。但是由於Neptune框架程式碼存在兩套對Activity外掛化的方案實現(版本迭代,一套老的,一套新的),】,導致程式碼邏輯不是很統一。在閱讀程式碼的時候,把握住Hook思路,只看相關的程式碼,還是比較容易理解的。

相關文章