专栏首页流媒体android插件化在9.0上插件activity的theme失效问题(VirtualAPK)

android插件化在9.0上插件activity的theme失效问题(VirtualAPK)

android插件化在9.0上插件activity的theme失效问题(VirtualApk) 在使用VirtualApk的时候,发现在android 9.0上,插件中的Activity配置的theme失效

这个问题和Android系统代码修改有关,我们看下9.0前后设置theme的变化在哪里。

看到ActivityThread中

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
//...省略代码
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

//...省略代码
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
//...省略代码
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

}

performLaunchActivity中进行了Activity的创建和theme的设置。而这个theme是从参数ActivityClientRecord r获取,看到performLaunchActivity被调用的地方

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...省略代码
    Activity a = performLaunchActivity(r, customIntent);
}

继续找到

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;

在ActivityThread的Handler中调用的handleLaunchActivity。正是如此,给了我们hook修改的机会,看到VirtualApk的处理VAInstrumentation

@Override
public boolean handleMessage(Message msg) {
    if (msg.what == LAUNCH_ACTIVITY) {
        // ActivityClientRecord r
        Object r = msg.obj;
        try {
            Reflector reflector = Reflector.with(r);
            Intent intent = reflector.field("intent").get();
            intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
            ActivityInfo activityInfo = reflector.field("activityInfo").get();

            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) {
            Log.w(TAG, e);
        }

Virtual 给ActivityThread中的Handler增加了自己的callback,也就是在系统处理LAUNCH_ACTIVITY消息时,virtualApk会先处理,获取到对应的ActivityClientRecord,然后修改activityInfo中的theme为插件的theme。

那为什么在9.0后就不行了呢,我们看下9.0这部分的源码 呵,好家伙,根本就没有LAUNCH_ACTIVITY这个定义了,所以hook失效,根本就没有设置插件的theme 那系统是怎么调用的handleLaunchActivity呢? 我们找到LaunchActivityItem

public class LaunchActivityItem extends ClientTransactionItem {

    private Intent mIntent;
    private int mIdent;
    private ActivityInfo mInfo;
    private Configuration mCurConfig;
    private Configuration mOverrideConfig;
    private CompatibilityInfo mCompatInfo;
    private String mReferrer;
    private IVoiceInteractor mVoiceInteractor;
    private int mProcState;
    private Bundle mState;
    private PersistableBundle mPersistentState;
    private List<ResultInfo> mPendingResults;
    private List<ReferrerIntent> mPendingNewIntents;
    private boolean mIsForward;
    private ProfilerInfo mProfilerInfo;

    @Override
    public void preExecute(ClientTransactionHandler client, IBinder token) {
        client.updateProcessState(mProcState, false);
        client.updatePendingConfiguration(mCurConfig);
    }

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        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 */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

这个execute又是在哪执行的呢?找到ActivityStackSupervisor

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            // TODO: Have this take the merged configuration instead of separate global
            // and override configs.
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
            r.persistentState, results, newIntents, mService.isNextTransitionForward(),
            profilerInfo));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);

我们找到LaunchActivityItem创建的地方,而mService.getLifecycleManager().scheduleTransaction(clientTransaction);是如何执行的呢

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }
    }

其实就是执行了 transaction的client的schedule()方法。继续看到

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

这里个client是什么,会到创建的地方看到

    final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
            r.appToken);

咱们的IApplicationThread嘛。看到他的scheduleTransaction

        @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }

继续看到 ActivityThread

    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

这下是不是很清晰了,就是把 ClientTransaction 通过Handler发送给ActivityThread来处理

    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        if (isSystem()) {
            // Client transactions inside system process are recycled on the client side
            // instead of ClientLifecycleManager to avoid being cleared before this
            // message is handled.
            transaction.recycle();
        }
        // TODO(lifecycler): Recycle locally scheduled transactions.
        break;

看到如何execute

    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
        log("End resolving transaction");
    }

看到executeCallbacks

    @VisibleForTesting
    public void executeCallbacks(ClientTransaction transaction) {
    //...省略代码
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            item.execute(mTransactionHandler, token, mPendingActions);
    //...省略代码
        }
    }

这下清楚了把,把添加的callbacks execute下。我们在回顾下设置的地方

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            // TODO: Have this take the merged configuration instead of separate global
            // and override configs.
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
            r.persistentState, results, newIntents, mService.isNextTransitionForward(),
            profilerInfo));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);

设置了LaunchActivityItem。而execute执行的就是handleLaunchActivity

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        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 */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

到这里整个流程就穿起来了。 所以知道为什么VirtualApk在9.0上为什么设置的theme没有效果,因为系统启动的调用方式已经发生了改变。

那现在我们如何去修改呢。同理看到Handler处理的地方

    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        if (isSystem()) {
            // Client transactions inside system process are recycled on the client side
            // instead of ClientLifecycleManager to avoid being cleared before this
            // message is handled.
            transaction.recycle();
        }
        // TODO(lifecycler): Recycle locally scheduled transactions.
        break;

我们可以获取到 ClientTransaction。 然后再反射获取到 mActivityCallbacks。判断如果是LaunchActivityItem。 继续反射获取到ActivityInfo。这里附上完整的修改代码

    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            // ActivityClientRecord r
            Object r = msg.obj;
            try {
                Reflector reflector = Reflector.with(r);
                Intent intent = reflector.field("intent").get();
                intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
                ActivityInfo activityInfo = reflector.field("activityInfo").get();

                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) {
                Log.w(TAG, e);
            }
        } else if (msg.what == 159) {
            //r实际为clienttransaction
            Object r = msg.obj;
            try {
                Class clientClazz = r.getClass();
                Field fCallbacks = clientClazz.getDeclaredField("mActivityCallbacks");
                fCallbacks.setAccessible(true);
                //得到transactionz中的callbacks,为一个list,获取其中元素为LaunchActivityItem
                List<?> lists = (List) fCallbacks.get(r);
                for (int i = 0; i < lists.size(); i++) {
                    Object item = lists.get(i);
                    Class itemClazz = item.getClass();
                    Log.w(TAG, "class--->" + itemClazz.getName());
                    if (!(itemClazz.getSimpleName().equals("LaunchActivityItem"))) {
                        return false;
                    }
                    //获取成员 mIntent
                    Log.w(TAG, "=======get " + itemClazz.getName());
                    Field mIntent = itemClazz.getDeclaredField("mIntent");
                    mIntent.setAccessible(true);
                    Intent intent = (Intent) mIntent.get(item);
                    Log.w(TAG, "=======get intent " + intent);

                    //ActivityInfo mInfo
                    Field activityInfoField = itemClazz.getDeclaredField("mInfo");
                    activityInfoField.setAccessible(true);
                    ActivityInfo activityInfo = (ActivityInfo) activityInfoField.get(item);
                    Log.w(TAG, "=======get ActivityInfo " + activityInfoField);
                    intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
                    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 (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 美团App插件化实践

    背景 在Android开发行业里,插件化已经不是一门新鲜的技术了,在稍大的平台型App上早已是标配。进入2017年,Atlas、Replugin、Virtual...

    美团技术团队
  • 有关Android插件化的一些总结思考

    最近几年移动开发业界兴起了「 插件化技术 」的旋风,各个大厂都推出了自己的插件化框架,各种开源框架都评价自身功能优越性,令人目不暇接。随着公司业务快速发展,项目...

    Android技术干货分享
  • 有关Android插件化思考

    最近几年移动开发业界兴起了「 插件化技术 」的旋风,各个大厂都推出了自己的插件化框架,各种开源框架都评价自身功能优越性,令人目不暇接。随着公司业务快速发展,项目...

    用户1269200
  • Android插件化——Activity的启动

    Oceanlong
  • 自己动手写Android插件化框架

    本文旨在通过两个实例直观的说明插件的实现原理以加深对插件内开发的理解,因此不会深入探讨背景和原理,代码也尽量专注于核心逻辑。

    达文西
  • 《Android插件化技术——原理篇》

    | 导语 插件化技术最早从2012年诞生至今,已经走过了5个年头。从最初只支持Activity的动态加载发展到可以完全模拟app运行时的沙箱系统,各种开源项目层...

    腾讯Bugly
  • Android基础知识:项目架构基础概述

    前段时间由于工作需要,学习了关于组件化和插件化架构相关的知识。查阅了很多Android开发架构的资料,组件化自己写了个简单的Demo并且开始了原有项目的改造,插...

    Android技术干货分享
  • Android插件化快速入门与实例解析(VirtualApk)

    集成一个第三方相册功能,只需集成一个插件APK到项目中,无需集成额外代码,并且支持随时更新相册功能,无需发布版本更新,无需AndroidManifest中声明四...

    GSYTech
  • Android插件化快速入门与实例解析(VirtualApk)

    集成一个第三方相册功能,只需集成一个插件APK到项目中,无需集成额外代码,并且支持随时更新相册功能,无需发布版本更新,无需AndroidMani...

    用户2802329

扫码关注云+社区

领取腾讯云代金券