FrameWork源碼解析(10)-插件化框架VirtualApk之Activity啟動(dòng)

主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
框架地址:VirtualApk
在線源碼查看:AndroidXRef

上面我們已經(jīng)講了兩篇關(guān)于這個(gè)插件化框架了:
1.插件化框架VirtualApk之初始化
2.插件化框架VirtualApk之插件加載
如果看了前面這兩篇應(yīng)該對(duì)插件有點(diǎn)了解,但是要理解這篇文章還需要應(yīng)用程序內(nèi)Activity的啟動(dòng)流程相關(guān)的知識(shí),并且我們知道要繞過(guò)系統(tǒng)檢查必須要Activity在AndroidManifest.xml中顯式聲明,所以我們這里就會(huì)采用稱為"占坑"的方式來(lái)繞過(guò)系統(tǒng)檢查。

一.啟動(dòng)過(guò)程分析

為了大家有個(gè)直觀的感受,我們這里再貼一張粗略圖幫大家回顧回顧:


Activity啟動(dòng)過(guò)程

我們看到啟動(dòng)Activity的時(shí)候,都會(huì)調(diào)用到Instrumentation類的execStartActivity(),newActivity(),callActivityOnCreate()等方法,加上之前我們hook掉系統(tǒng)的Instrumentation類:

  private void hookInstrumentationAndHandler() {
        try {
            Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
            if (baseInstrumentation.getClass().getName().contains("lbe")) {
                // reject executing in paralell space, for example, lbe.
                System.exit(0);
            }

            final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
            Object activityThread = ReflectUtil.getActivityThread(this.mContext);
            ReflectUtil.setInstrumentation(activityThread, instrumentation);
            ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
            this.mInstrumentation = instrumentation;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

所以在啟動(dòng)Activity的時(shí)候會(huì)調(diào)用到VAInstrumentation#execStartActivity()方法:

 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
        // null component is an implicitly intent
        if (intent.getComponent() != null) {
            Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(),
                    intent.getComponent().getClassName()));
            // resolve intent with Stub Activity if needed
            this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
        }

        ActivityResult result = realExecStartActivity(who, contextThread, token, target,
                    intent, requestCode, options);

        return result;
    }

execStartActivity()方法主要做了下面幾件事:
1.將隱式啟動(dòng)轉(zhuǎn)為顯式啟動(dòng)的方式,因?yàn)椴寮嗀ctivity不在宿主工程中,所以隱式啟動(dòng)不能啟動(dòng)插件中的Activity:

  mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);

這個(gè)方法就是將隱式啟動(dòng)轉(zhuǎn)化為顯式啟動(dòng)的方法,我們跟蹤進(jìn)去可以看到:

 /**
     * transform intent from implicit to explicit
     */
    public Intent transformIntentToExplicitAsNeeded(Intent intent) {
        ComponentName component = intent.getComponent();
        if (component == null
            || component.getPackageName().equals(mContext.getPackageName())) {
            ResolveInfo info = mPluginManager.resolveActivity(intent);
            if (info != null && info.activityInfo != null) {
                component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
                intent.setComponent(component);
            }
        }

        return intent;
    }

這個(gè)方法主要是通過(guò)intent來(lái)查找要啟動(dòng)的插件的Activity的ResolveInfo,然后設(shè)置進(jìn)Intent中。我們可以直接跟進(jìn)去看:

 public ResolveInfo resolveActivity(Intent intent) {
        return this.resolveActivity(intent, 0);
    }

 public ResolveInfo resolveActivity(Intent intent, int flags) {
        for (LoadedPlugin plugin : this.mPlugins.values()) {
            ResolveInfo resolveInfo = plugin.resolveActivity(intent, flags);
            if (null != resolveInfo) {
                return resolveInfo;
            }
        }

        return null;
    }

我們知道,插件的相關(guān)的信息都放在LoadedPlugin類中,所以我們?cè)僬{(diào)用到LoadedPlugin #resolveActivity()方法中:

 public ResolveInfo resolveActivity(Intent intent, int flags) {
        List<ResolveInfo> query = this.queryIntentActivities(intent, flags);
        if (null == query || query.isEmpty()) {
            return null;
        }

        ContentResolver resolver = this.mPluginContext.getContentResolver();
        return chooseBestActivity(intent, intent.resolveTypeIfNeeded(resolver), flags, query);
    }

從上面方法我們可以看出會(huì)查找匹配的ResolveInfo集合,然后最后會(huì)調(diào)用chooseBestActivity()方法來(lái)選擇最匹配的Activity的ResolveInfo類:

  public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
        ComponentName component = intent.getComponent();
        List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>();
        ContentResolver resolver = this.mPluginContext.getContentResolver();

        for (PackageParser.Activity activity : this.mPackage.activities) {
            if (match(activity, component)) {
                ResolveInfo resolveInfo = new ResolveInfo();
                resolveInfo.activityInfo = activity.info;
                resolveInfos.add(resolveInfo);
            } else if (component == null) {
                // only match implicit intent
                for (PackageParser.ActivityIntentInfo intentInfo : activity.intents) {
                    if (intentInfo.match(resolver, intent, true, TAG) >= 0) {
                        ResolveInfo resolveInfo = new ResolveInfo();
                        resolveInfo.activityInfo = activity.info;
                        resolveInfos.add(resolveInfo);
                        break;
                    }
                }
            }
        }
        return resolveInfos;
    }

這個(gè)方法主要是通過(guò)intent來(lái)查找插件中匹配的Activity,但是這里分為顯示啟動(dòng)和隱式啟動(dòng)的方式,如果ComponentName為空,那么說(shuō)明是隱式啟動(dòng)的方式,這個(gè)地方就會(huì)通過(guò)隱式啟動(dòng)的一些條件來(lái)匹配到對(duì)應(yīng)的ResolveInfo。如果是顯式啟動(dòng)則只要匹配相應(yīng)的類名,包名對(duì)應(yīng)即可。接著我們看下chooseBestActivity()方法,這個(gè)方法把上面匹配到的ResolveInfo集合選出第一個(gè)元素,接著execStartActivity()方法會(huì)接著調(diào)用如下代碼:
2.將要啟動(dòng)的Activity臨時(shí)替換成占坑的Activity

  // null component is an implicitly intent
        if (intent.getComponent() != null) {
            Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(),
                    intent.getComponent().getClassName()));
            // resolve intent with Stub Activity if needed
            this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
        }

因?yàn)椴寮腁ctivity不在宿主工程的AndroidManifest.xml中,所以要想繞過(guò)檢查,會(huì)提前在宿主工程的AndroidManifest.xml中提前注冊(cè)好:

  <application>
        <!-- Stub Activities -->
        <activity android:name=".A$1" android:launchMode="standard"/>
        <activity android:name=".A$2" android:launchMode="standard"
            android:theme="@android:style/Theme.Translucent" />

        <!-- Stub Activities -->
        <activity android:name=".B$1" android:launchMode="singleTop"/>
        <activity android:name=".B$2" android:launchMode="singleTop"/>
        <activity android:name=".B$3" android:launchMode="singleTop"/>
        <activity android:name=".B$4" android:launchMode="singleTop"/>
        <activity android:name=".B$5" android:launchMode="singleTop"/>
        <activity android:name=".B$6" android:launchMode="singleTop"/>
        <activity android:name=".B$7" android:launchMode="singleTop"/>
        <activity android:name=".B$8" android:launchMode="singleTop"/>

        <!-- Stub Activities -->
        <activity android:name=".C$1" android:launchMode="singleTask"/>
        <activity android:name=".C$2" android:launchMode="singleTask"/>
        <activity android:name=".C$3" android:launchMode="singleTask"/>
        <activity android:name=".C$4" android:launchMode="singleTask"/>
        <activity android:name=".C$5" android:launchMode="singleTask"/>
        <activity android:name=".C$6" android:launchMode="singleTask"/>
        <activity android:name=".C$7" android:launchMode="singleTask"/>
        <activity android:name=".C$8" android:launchMode="singleTask"/>

        <!-- Stub Activities -->
        <activity android:name=".D$1" android:launchMode="singleInstance"/>
        <activity android:name=".D$2" android:launchMode="singleInstance"/>
        <activity android:name=".D$3" android:launchMode="singleInstance"/>
        <activity android:name=".D$4" android:launchMode="singleInstance"/>
        <activity android:name=".D$5" android:launchMode="singleInstance"/>
        <activity android:name=".D$6" android:launchMode="singleInstance"/>
        <activity android:name=".D$7" android:launchMode="singleInstance"/>
        <activity android:name=".D$8" android:launchMode="singleInstance"/>
...........
    </application>

我們看到這里根據(jù)四種啟動(dòng)模式standard,singleTop,singleTask,singleInstance,分別注冊(cè)了幾個(gè)Activity,因?yàn)槲覀冎?,一般不?huì)一時(shí)間連續(xù)在棧中壓入這么多Activity,所以一個(gè)啟動(dòng)模式只要提前注冊(cè)數(shù)個(gè)即可,這里每個(gè)啟動(dòng)提前注冊(cè)了8個(gè)Activity。我們來(lái)看調(diào)用的markIntentIfNeeded()方法:

 public void markIntentIfNeeded(Intent intent) {
        if (intent.getComponent() == null) {
            return;
        }

        String targetPackageName = intent.getComponent().getPackageName();
        String targetClassName = intent.getComponent().getClassName();
        // search map and return specific launchmode stub activity
        if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
            intent.putExtra(Constants.KEY_IS_PLUGIN, true);
            intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
            intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
            dispatchStubActivity(intent);
        }
    }

這個(gè)方法會(huì)判斷啟動(dòng)的Activity在不在插件中,如果是的話,則會(huì)將要啟動(dòng)的Activity的包名和名稱放進(jìn)intent中,最后調(diào)用dispatchStubActivity()方法來(lái)根據(jù)相應(yīng)的啟動(dòng)模式來(lái)啟動(dòng)目標(biāo)Activity:

 private void dispatchStubActivity(Intent intent) {
        ComponentName component = intent.getComponent();
        String targetClassName = intent.getComponent().getClassName();
        LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
        ActivityInfo info = loadedPlugin.getActivityInfo(component);
        if (info == null) {
            throw new RuntimeException("can not find " + component);
        }
        int launchMode = info.launchMode;
        Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
        themeObj.applyStyle(info.theme, true);
        String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
        Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
        intent.setClassName(mContext, stubActivity);
    }

這個(gè)方法主要是根據(jù)intent獲取到啟動(dòng)模式,然后匹配到的Activity放進(jìn)intent的className中,這樣intent的信息已經(jīng)齊全,execStartActivity()方法繼續(xù)調(diào)用realExecStartActivity()方法來(lái)真正啟動(dòng)Activity:

   private ActivityResult realExecStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ActivityResult result = null;
        try {
            Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,
            int.class, Bundle.class};
            result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,
                    "execStartActivity", parameterTypes,
                    who, contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            if (e.getCause() instanceof ActivityNotFoundException) {
                throw (ActivityNotFoundException) e.getCause();
            }
            e.printStackTrace();
        }

        return result;
    }

這個(gè)方法主要是反射調(diào)用系統(tǒng)的execStartActivity()方法,這樣的話流程就到AMS去了。

二.將StubActivity替換回TargetActivity

我們知道在AMS中的過(guò)程是無(wú)法進(jìn)行hook操作的,所以只要等流程重新到達(dá)應(yīng)用程序進(jìn)程我們才能做手腳,我們重新回顧一下hook掉Instrumentation類的過(guò)程:

  private void hookInstrumentationAndHandler() {
        try {
            Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
            if (baseInstrumentation.getClass().getName().contains("lbe")) {
                // reject executing in paralell space, for example, lbe.
                System.exit(0);
            }

            final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
            Object activityThread = ReflectUtil.getActivityThread(this.mContext);
            ReflectUtil.setInstrumentation(activityThread, instrumentation);
            ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
            this.mInstrumentation = instrumentation;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

我們看到這里調(diào)用setHandlerCallback()方法將VAInstrumentation設(shè)置替換成ActivityThread中Handler的mCallback,所以當(dāng)ActivityThread中H進(jìn)行dispathMessage()的時(shí)候,會(huì)走到VAInstrumentation類中的handleMessage()方法:

 @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            // ActivityClientRecord r
            Object r = msg.obj;
            try {
                Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent");
                intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());
                ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");

                if (PluginUtil.isIntentFromPlugin(intent)) {
                    int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                    if (theme != 0) {
                        Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                        activityInfo.theme = theme;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return false;
    }

我們可以看到這個(gè)方法只攔截了LAUNCH_ACTIVITY的處理,在里面將intent中的activityInfo.theme替換為插件的theme,并給intent設(shè)置了ClassLoader,這是因?yàn)楹竺?code>ActivityThread類中的performLaunchActivity()方法會(huì)將類加載器重新設(shè)置給mInstrumentation#newActivity()方法:

   @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        try {
            cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
            String targetClassName = PluginUtil.getTargetActivity(intent);

            Log.i(TAG, String.format("newActivity[%s : %s]", className, targetClassName));

            if (targetClassName != null) {
                Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
                activity.setIntent(intent);

                try {
                    // for 4.1+
                    ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
                } catch (Exception ignored) {
                    // ignored.
                }

                return activity;
            }
        }

        return mBase.newActivity(cl, className, intent);
    }

這里會(huì)往類加載器中設(shè)置className,這里的className就是之前占坑的那一些類,但是這些類其實(shí)現(xiàn)實(shí)中并不存在,如果直接調(diào)用肯定是會(huì)報(bào)ClassNotFoundException異常,所以會(huì)走到異常的地方,首先會(huì)根據(jù)intent獲取到插件,然后根據(jù)intent獲取到targetActivity,這樣就又替換回來(lái)插件中的Activity,最后我們注意這個(gè)地方有個(gè)調(diào)用:

   ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());

這主要是performLaunchActivity()方法中調(diào)用createBaseContextForActivity()方法創(chuàng)建出的appContext用的是宿主Resources,所以如果不進(jìn)行處理,在調(diào)用callActivityOnCreate()方法的時(shí)候會(huì)獲取不到資源,因?yàn)榇藭r(shí)插件加載資源的時(shí)候還是使用的宿主的資源,而不是我們特意為插件所創(chuàng)建出來(lái)的Resources對(duì)象,則會(huì)發(fā)生找不到資源的問(wèn)題,所以在4.1以上的版本就會(huì)另外做處理。接下來(lái)會(huì)調(diào)用到VAInstrumentationcallActivityOnCreate()方法中:

  @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        final Intent intent = activity.getIntent();
        if (PluginUtil.isIntentFromPlugin(intent)) {
            Context base = activity.getBaseContext();
            try {
                LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
                ReflectUtil.setField(base.getClass(), base, "mResources", plugin.getResources());
                ReflectUtil.setField(ContextWrapper.class, activity, "mBase", plugin.getPluginContext());
                ReflectUtil.setField(Activity.class, activity, "mApplication", plugin.getApplication());
                ReflectUtil.setFieldNoException(ContextThemeWrapper.class, activity, "mBase", plugin.getPluginContext());

                // set screenOrientation
                ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
                if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
                    activity.setRequestedOrientation(activityInfo.screenOrientation);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        mBase.callActivityOnCreate(activity, icicle);
    }

這個(gè)方法主要是判斷要啟動(dòng)的Activity是插件中的,這樣就會(huì)替換目標(biāo)Activity中的ResourcesContext,mApplication為相應(yīng)的加載插件資源用的ResourcesContext,然后調(diào)用系統(tǒng)InstrumentationcallActivityOnCreate()方法來(lái)啟動(dòng)插件TargetActivity。到這里插件中的Activity啟動(dòng)就已經(jīng)完成了。

總結(jié):到這里我們插件中的Activity啟動(dòng)已經(jīng)講完了,接下來(lái)我們會(huì)來(lái)講講Service的啟動(dòng)情況,希望有什么不理解或者不正確的地方可以留言,一起交流。

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

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

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