由VirtualAPK了解插件化原理(二)

本文深入探讨Android四大组件插件化的实现原理,包括Activity、Service、ContentProvider和BroadcastReceiver的插件化策略,以及如何利用Hook技术实现组件代理。

Android 四大组件由于要和 AMS 频繁通信,AMS 在 SystemServer 进程中,无法直接修改,所以四大组件的插件化是重中之重。

Activity插件化

首先必须了解 Activity 启动过程,这里不多介绍启动流程,放一张图可以看下,图是7.0的启动顺序。

预先在 AndroidManifest.xml 中注册 Activity 来占坑,用来通过 AMS 的校验。VirtualAPK 中占坑的 Activity。

        <activity
            android:name=".A$1"
            android:exported="false"
            android:launchMode="standard" />

我们启动插件中的 activity 时

            Intent intent = new Intent();
            intent.setClassName(this, "com.didi.virtualapk.demo.aidl.BookManagerActivity");
            startActivity(intent);

对于 AMS 甚至系统类,我们不能直接修改,但可以考虑 Hook 技术替换。

由上图我们关注两个函数,一个是 activity 的 startActivityForResult

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
//内部调用ActivityManager.getService().startActivity与AMS通信
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            //...
        } else {
            //...
        }
    }

一个是 ActivityThread 的 performLaunchActivity

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
//内部Activity activity = (Activity)clazz.newInstance();用类加载器创建Activity实例
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
        //...
    }

注意到 mInstrumentation.execStartActivity 与 mInstrumentation.newActivity 两个方法都是 mInstrumentation 调用,因此 mInstrumentation 是一个好的 Hook 点。

VirtualAPK 的实现

execStartActivity 方法,在自定义的 VAInstrumentation 类中 实现了 execStartActivity 方法

public class VAInstrumentation extends Instrumentation implements Handler.Callback {

    protected Instrumentation mBase;
    
    protected final ArrayList<WeakReference<Activity>> mActivities = new ArrayList<>();

    protected PluginManager mPluginManager;

    public VAInstrumentation(PluginManager pluginManager, Instrumentation base) {
        this.mPluginManager = pluginManager;
        this.mBase = base;
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }
}

其中 injectIntent() 处理 intent

    protected void injectIntent(Intent intent) {
        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);
        }
    }

transformIntentToExplicitAsNeeded() 方法查找插件 apk 中是否包含与调用的 intent 参数一致的组件。

    public Intent transformIntentToExplicitAsNeeded(Intent intent) {
        //应用程序组件信息
        ComponentName component = intent.getComponent();
        //判断组件包名是否是自身
        if (component == null
            || component.getPackageName().equals(mContext.getPackageName())) {
            //查找插件中,最终会调到        List<ResolveInfo> query = LoadedPlugin的queryIntentActivities(intent, flags);方法
            //并返回第一个匹配的
            ResolveInfo info = mPluginManager.resolveActivity(intent);
            if (info != null && info.activityInfo != null) {
            //创建设置intent的Component
                component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
                intent.setComponent(component);
            }
        }

        return intent;
    }

看下匹配逻辑 queryIntentActivities(),其中 mPackage.activities 即为上文解析插件 apk 后缓存的 Activity 集合。

    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
        ComponentName component = intent.getComponent();
        List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>();
        //ContentProvider共享数据是通过定义一个对外开放的统一的接口来实现的
        //封装了ContentProvider保存数据
        ContentResolver resolver = this.mPluginContext.getContentResolver();
        //mPackage保存了解析完成后的apk信息,遍历插件清单文件中的依次比对
        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) {
                //不是插件中的则去系统中查找对应的intent
                for (PackageParser.ActivityIntentInfo intentInfo : activity.intents) {
                    /**
                     * match action-> match data-> match categories,任意一步失败就不在进行后续处理。
                     * match action就是匹配intent的action是否包含在filter的actions里。
                     * match data的思路和uri的语法组成有关[scheme:]scheme-specific-part[#fragment]
                     *  先判filter和intent的types(即MIMI) schemes是否全是null,若是则匹配,取值MATCH_CATEGORY_EMPTY,否则继续
                     *  再匹配scheme,不匹配则结束,匹配则继续
                     *  SchemeSpecificPart部分,若匹配,则进行最后的type(MIME)判断
                     *  SchemeSpecificPart部分,若不匹配,则
                     *  匹配host port部分,成功则继续,否则不匹配
                     *  匹配path部分,成功则继续,否则不匹配
                     *  最后进行type(MIME)的匹配
                     * match categories就是判断intent的所有category是否全都包含啊filter的categories里。
                     */
                    if (intentInfo.match(resolver, intent, true, TAG) >= 0) {
                        ResolveInfo resolveInfo = new ResolveInfo();
                        resolveInfo.activityInfo = activity.info;
                        resolveInfos.add(resolveInfo);
                        break;
                    }
                }
            }
        }

        return resolveInfos;
    }

匹配的时候先通过自身方法 match 查找插件,不是则再调用 IntentFilter 规则匹配系统中的 intent。

自身 match

    protected boolean match(PackageParser.Component component, ComponentName target) {
        ComponentName source = component.getComponentName();//com.didi.virtualapk.demo.MainActivity
        if (source == target) return true;//target要启动的
        //source不为null、target不为null、类名包名一致
        if (source != null && target != null
                && source.getClassName().equals(target.getClassName())
                && (source.getPackageName().equals(target.getPackageName())
                || mHostContext.getPackageName().equals(target.getPackageName()))) {
            return true;
        }
        return false;
    }

markIntentIfNeeded() 中判断要启动的Activity是否是插件的,是插件的则改变 intent 参数

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

        String targetPackageName = intent.getComponent().getPackageName();//com.didi.virtualapk.demo
        String targetClassName = intent.getComponent().getClassName();//com.didi.virtualapk.demo.aidl.BookManagerActivity
        // search map and return specific launchmode stub activity
        if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
            //是启动插件Activity,intent中添加三个参数
            intent.putExtra(Constants.KEY_IS_PLUGIN, true);//"isPlugin"
            intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);//"target.package"
            intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);//"target.activity"
            dispatchStubActivity(intent);
        }
    }

    private void dispatchStubActivity(Intent intent) {
        //获取ComponentName
        ComponentName component = intent.getComponent();
        //获取intent中要启动的class类名
        String targetClassName = intent.getComponent().getClassName();
        //再次判断
        LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
        ActivityInfo info = loadedPlugin.getActivityInfo(component);

        //获取启动模式launchMode
        int launchMode = info.launchMode;
        //获取主题themeObj
        Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
        themeObj.applyStyle(info.theme, true);
        //由launchMode、themeObj来获取占坑的Activity名字
        String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
        //将占坑的Activity名字设置给intent
        intent.setClassName(mContext, stubActivity);
    }

injectIntent 方法实际就是两点,一是判断要启动的 Activity 是否是插件中的,而是如果是的话,将 intent 替换为占坑的 Activity。最后调用系统原有的 execStartActivity 方法启动 Activity。

其中 intent 中的三个参数表明要启动的插件 Activity


public static final String KEY_IS_PLUGIN = "isPlugin";
public static final String KEY_TARGET_PACKAGE = "target.package";
public static final String KEY_TARGET_ACTIVITY = "target.activity";

handleMessage 的 LAUNCH_ACTIVITY,由上图 execStartActivity 后会走到 handler mH 的 LAUNCH_ACTIVITY 判断中,这里仅仅设置了主题。

ActivityThread 会通过H类将代码的逻辑切换到主线程,H 类是 ActivityThread 的内部类并继承 Handler。H 类重写 handlerMessage 方法会对 LAUNCH_ACTIVITY 类型的消息进行处理。这里也可以作为还原插件 Activity 的 Hook 点。

    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            // r为ActivityClientRecord类型
            Object r = msg.obj;
            try {
        /**
         *     static final class ActivityClientRecord {
         *         //...
         *         Intent intent;
         *         //...
         *         ActivityInfo activityInfo;
         *         //...
         *     }
         */
                Reflector reflector = Reflector.with(r);
                Intent intent = reflector.field("intent").get();//获取intent对象
                intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());//设置自己的ClassLoader
                ActivityInfo activityInfo = reflector.field("activityInfo").get();

                if (PluginUtil.isIntentFromPlugin(intent)) {
                    int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                    if (theme != 0) {
                        //设置主题
                        activityInfo.theme = theme;
                    }
                }
            } catch (Exception e) {
                Log.w(TAG, e);
            }
        }
}

newActivity(),之后通过上图流程到达 newActivity() 方法,newActivity 方法会用类加载器来创建 Activity 实例,这里必须从 intent 中读取要创建的插件 Activity 来创建

    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        try {
            //解析ClassName
            cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            //由intent获取原来插件的信息
            ComponentName component = PluginUtil.getComponent(intent);
            if (component == null) {
                return newActivity(mBase.newActivity(cl, className, intent));
            }
    
            String targetClassName = component.getClassName();//com.didi.virtualapk.demo.aidl.BookManagerActivity
            Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName));
    
            LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);//从缓存中找到相关信息    
            //...
            //调用系统的newActivity
            Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
            activity.setIntent(intent);
            // for 4.1+            
            return newActivity(activity);
        }

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

此方法还原了插件 Activity,并调用系统 newActivity 方法去创建还原的实例。其中自身的 newActivity 实际是将创建出来的 activity 实例进行了缓存。

callActivityOnCreate(),最后调用与 onCreate 方法相关的方法中

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
        injectActivity(activity);
        mBase.callActivityOnCreate(activity, icicle, persistentState);
    }
    
    protected void injectActivity(Activity activity) {
        final Intent intent = activity.getIntent();
        if (PluginUtil.isIntentFromPlugin(intent)) {
            Context base = activity.getBaseContext();
            try {
                LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
                //反射Context将mResources替换为解析插件时候创建的Resources
                Reflector.with(base).field("mResources").set(plugin.getResources());
                //得到插件中Activity实例,替换实例中的mBase、mApplication分别为解析插件时创建的
                Reflector reflector = Reflector.with(activity);
                reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));
                reflector.field("mApplication").set(plugin.getApplication());
                //设置横竖屏
                ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
                if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
                    activity.setRequestedOrientation(activityInfo.screenOrientation);
                }
                //
                ComponentName component = PluginUtil.getComponent(intent);
                Intent wrapperIntent = new Intent(intent);
                wrapperIntent.setClassName(component.getPackageName(), component.getClassName());
                activity.setIntent(wrapperIntent);
                
            } catch (Exception e) {
                Log.w(TAG, e);
            }
        }
    }

设置了 mResources、mBase、mApplication、横竖屏等。注意这里的 mBase 的替换,下文介绍 ContentProvider 插件化时会用到。

注意:迫切需要支持Androidxapi不支持28

VirtualAPK 已有两年没有更新了,有很多坑需要自己挖掘,google 也不支持插件化,但是相关的技术需要了解。

最后 Hook mInstrumentation和Handler

    protected void hookInstrumentationAndHandler() {
        try {
            //绕过系统隐藏API,获取ActivityThread实例
            ActivityThread activityThread = ActivityThread.currentActivityThread();
            //拿到系统原有的Instrumentation
            Instrumentation baseInstrumentation = activityThread.getInstrumentation();
            //创建自己的Instrumentation
            /**
             *     protected VAInstrumentation createInstrumentation(Instrumentation origin) throws Exception {
             *         return new VAInstrumentation(this, origin);
             *     }
             */
            final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation);
            //activityThread中mInstrumentation替换为instrumentation
            Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
            //获取activityThread中getHandler()方法
            /**
             *     final Handler getHandler() {
             *         return mH;
             *     }
             */
            Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
            //Handler中mCallback替换为instrumentation,instrumentation实现了Handler.Callback接口
            Reflector.with(mainHandler).field("mCallback").set(instrumentation);
            //给mInstrumentation赋值
            this.mInstrumentation = instrumentation;
            Log.d(TAG, "hookInstrumentationAndHandler succeed : " + mInstrumentation);
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

还有另一种 Hook

在 android源码中的反射、代理的应用 文章中对 8.0 和 7.0 的 AMS 做了介绍。7.0 调用getDefault() 方法,返回了 IActivityManager 类型的对象,IActivityManager 借助了Singleton 来实现单例,而 getDefault() 又是静态的,因此 IActivityManager 是一个比较好的 Hook 点。8.0 中 getService 方法返回了 IActivityManager 类型的对象,并且 IActivityManager 借助了 Singleton 类实现单例,因此 IActivityManager 是比较好的 Hook 点。

    protected void hookSystemServices() {
        try {
            //Hook IActivityManager开始
            //Singleton也是隐藏API,这里也实现了
            /**
             * public abstract class Singleton<T> {
             *     public Singleton() {
             *         throw new RuntimeException("Stub!");
             *     }
             *
             *     protected abstract T create();
             *
             *     public T get() {
             *         throw new RuntimeException("Stub!");
             *     }
             * }
             */
            Singleton<IActivityManager> defaultSingleton;
            //8.0后不一致
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                //8.0中getService方法返回了IActivityManager类型的对象,并且IActivityManager借助了Singleton类实现单例
                //获取ActivityManager中IActivityManagerSingleton实例,类型是Singleton<IActivityManager>
                /**
                 *     private static final Singleton<IActivityManager> IActivityManagerSingleton =
                 *             new Singleton<IActivityManager>() {
                 *                 @Override
                 *                 protected IActivityManager create() {
                 *                     final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                 *                     final IActivityManager am = IActivityManager.Stub.asInterface(b);
                 *                     return am;
                 *                 }
                 *             };
                 */
                defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
            } else {
                //7.0调用getDefault()方法,返回了IActivityManager类型的对象,IActivityManager借助了Singleton来实现单例,而getDefault()又是静态的
                defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
            }
            //获取IActivityManager实例
            IActivityManager origin = defaultSingleton.get();
            //创建ActivityManagerProxy对象,将原来的作为参数,实现了InvocationHandler函数
            ActivityManagerProxy proxy = createActivityManagerProxy(origin);
            //代理对象activityManagerProxy
            IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{IActivityManager.class}, proxy);

            //将defaultSingleton中mInstance替换为我们创建的代理对象activityManagerProxy
            /**
             * public abstract class Singleton<T> {
             *     private T mInstance;
             * }
             */
            Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy);

            if (defaultSingleton.get() == activityManagerProxy) {
                //赋值mActivityManager
                this.mActivityManager = activityManagerProxy;
                Log.d(TAG, "hookSystemServices succeed : " + mActivityManager);
            }
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

自然还原 Activity 可以 Hook H 类。

Service 插件化

Activity 插件化需要保证它的生命周期,而 Service 插件化重点是保证它的优先级,这时候就需要一个真正的 Service 来实现。这就会用到上文 Hook 的 ActivityManagerProxy 对象。

启动插件 Service

            Intent intent = new Intent();
            intent.setClassName(this, "com.didi.virtualapk.demo.messenger.MessengerService");
            startService(intent);

 VirtualAPK 中注册的 Service

        <service
            android:name="com.didi.virtualapk.delegate.LocalService"
            android:exported="false" />

        <!-- Daemon Service running in child process -->
        <service
            android:name="com.didi.virtualapk.delegate.RemoteService"
            android:exported="false"
            android:process=":daemon">
            <intent-filter>
                <action android:name="${applicationId}.intent.ACTION_DAEMON_SERVICE" />
            </intent-filter>
        </service>

ActivityManagerProxy 的 Hook 已说,看下它的实现。

 public class ActivityManagerProxy implements InvocationHandler {   
    private PluginManager mPluginManager;
    private IActivityManager mActivityManager;

    public ActivityManagerProxy(PluginManager pluginManager, IActivityManager activityManager) {
        this.mPluginManager = pluginManager;
        this.mActivityManager = activityManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e("ActivityManagerProxy invoke", method.getName());
        if ("startService".equals(method.getName())) {
            try {
                return startService(proxy, method, args);
            } catch (Throwable e) {
                Log.e(TAG, "Start service error", e);
            }
        } else if ("stopService".equals(method.getName())) {
            //其他
        }

    }
}

我们在 Activity 中调用 startService 会调用 invoke 方法,这里根据方法名进行拦截,走进自己实现的 startService(proxy, method, args);

    protected Object startService(Object proxy, Method method, Object[] args) throws Throwable {
        IApplicationThread appThread = (IApplicationThread) args[0];
        Intent target = (Intent) args[1];
        //与Activity一致,从缓存中匹配intent,判断Service是否在插件中
        ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
        if (null == resolveInfo || null == resolveInfo.serviceInfo) {
            //不存在
            return method.invoke(this.mActivityManager, args);
        }
        return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
    }

由 Activity 的基础,这里与之相似,直接看下 startDelegateServiceForTarget() 的实现。

    protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
        Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
        return mPluginManager.getHostContext().startService(wrapperIntent);
    }

    protected Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
        //com.didi.virtualapk.demo   com.didi.virtualapk.demo.messenger.MessengerService
        target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
        ///storage/emulated/0/app-beijing-release.apk
        String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();
        //这里启动RemoteService
        boolean local = PluginUtil.isLocalService(serviceInfo);//false
        Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;
        Intent intent = new Intent();
        intent.setClass(mPluginManager.getHostContext(), delegate);
        intent.putExtra(RemoteService.EXTRA_TARGET, target);//"target"
        intent.putExtra(RemoteService.EXTRA_COMMAND, command);//"command" RemoteService.EXTRA_COMMAND_START_SERVICE
        intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);//"plugin_location"
        if (extras != null) {
            intent.putExtras(extras);
        }

        return intent;
    }

startDelegateServiceForTarget 将原本要启动的插件 Service 对象在 intent 内保存,之后启动 RemoteService。之后就是在 RemoteService 中代理分发了。

    public static final String EXTRA_TARGET = "target";
    public static final String EXTRA_COMMAND = "command";
    public static final String EXTRA_PLUGIN_LOCATION = "plugin_location";

RemoteService 继承 LocalService

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return super.onStartCommand(intent, flags, startId);
        }
        //得到要启动的Intent target
        Intent target = intent.getParcelableExtra(EXTRA_TARGET);
        if (target != null) {
            //得到插件Service的位置
            String pluginLocation = intent.getStringExtra(EXTRA_PLUGIN_LOCATION);
            //插件Service的包名信息
            ComponentName component = target.getComponent();
            LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(component);
            //如果没有解析插件Apk,则需要解析下
            if (plugin == null && pluginLocation != null) {
                try {
                    PluginManager.getInstance(this).loadPlugin(new File(pluginLocation));
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
            }
        }
        //调用父类LocalService的方法
        return super.onStartCommand(intent, flags, startId);
    }

LocalService 的 onStartCommand()

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) {
            return START_STICKY;
        }
        //得到要启动的target
        Intent target = intent.getParcelableExtra(EXTRA_TARGET);
        //得到command
        int command = intent.getIntExtra(EXTRA_COMMAND, 0);
        if (null == target || command <= 0) {
            return START_STICKY;
        }
        //启动的intent target获取插件的包名信息
        ComponentName component = target.getComponent();
        LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);
        
        if (plugin == null) {
            Log.w(TAG, "Error target: " + target.toURI());
            return START_STICKY;
        }
        //设置ClassLoader
        target.setExtrasClassLoader(plugin.getClassLoader());
        //startService传入的是EXTRA_COMMAND_START_SERVICE
        switch (command) {
            case EXTRA_COMMAND_START_SERVICE: {
                //获取当前的ActivityThread实例,隐藏API
                ActivityThread mainThread = ActivityThread.currentActivityThread();
                //获取IApplicationThread 的binder对象
                IApplicationThread appThread = mainThread.getApplicationThread();
                Service service;
                //缓存中检测是否已经存在了,存在直接返回,不存在则创建
                /**
                 * ComponentsHandler类中
                 *     private ArrayMap<ComponentName, Service> mServices = new ArrayMap<ComponentName, Service>();
                 */
                if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
                    service = this.mPluginManager.getComponentsHandler().getService(component);
                } else {
                    try {
                        //创建Service实例,已经加载插件Service实例
                        service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
                        //得到Application实例
                        Application app = plugin.getApplication();
                        //得到Token,隐藏API,同样方法获取
                        IBinder token = appThread.asBinder();
                        //获取方法attach
                        /**
                         *     public final void attach(
                         *             Context context,
                         *             ActivityThread thread, String className, IBinder token,
                         *             Application application, Object activityManager) {
                         *         attachBaseContext(context);
                         *         mThread = thread;           // NOTE:  unused - remove?
                         *         mClassName = className;
                         *         mToken = token;
                         *         mApplication = application;
                         *         mActivityManager = (IActivityManager)activityManager;
                         *         mStartCompatibility = getApplicationInfo().targetSdkVersion
                         *                 < Build.VERSION_CODES.ECLAIR;
                         *     }
                         */
                        Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
                        //得到IActivityManager实例
                        IActivityManager am = mPluginManager.getActivityManager();
                        //反射执行插件Service的attach方法,并传入此前得到的参数
                        attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
                        //执行onCreate方法
                        service.onCreate();
                        //放入缓存
                        this.mPluginManager.getComponentsHandler().rememberService(component, service);
                    } catch (Throwable t) {
                        return START_STICKY;
                    }
                }
                //执行Service的onStartCommand方法
                /**
                 * ComponentsHandler类中
                 *     private ArrayMap<Service, AtomicInteger> mServiceCounters = new ArrayMap<Service, AtomicInteger>();
                 */
                service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
                break;
            }
        }

        return START_STICKY;
    }

ActivityManagerProxy 类中还定义了 stopService、bindService、unbindService 等方法,原理与之类似。

ContentProvider 插件化

ContentProvider 插件化的关键在于将 ContentProvider 插件共享给整个系统。和 Service 插件化类似,需要一个真正的 ContentProvider 作为代理 ContentProvider,并把这个代理 ContentProvider 共享给整个系统,对于插件 ContentProvider 请求会全部交由代理 ContentProvider 处理并分发给对应的插件 ContentProvider。

ContentProvider 流程图

VirtualAPK 中注册的 ContentProvider

        <provider
            android:name="com.didi.virtualapk.delegate.RemoteContentProvider"
            android:authorities="${applicationId}.VirtualAPK.Provider"
            android:exported="false"
            android:process=":daemon" />

启动插件 ContentProvider

            Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book");
            LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(pkg);
            bookUri = PluginContentResolver.wrapperUri(plugin, bookUri);

            Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
            if (bookCursor != null) {
                while (bookCursor.moveToNext()) {
                    int bookId = bookCursor.getInt(0);
                    String bookName = bookCursor.getString(1);
                    Log.d("ryg", "query book:" + bookId + ", " + bookName);
                }
                bookCursor.close();
            }

getContentResolver() 方法调用 mBase 的 getContentResolver() 方法

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

在执行 VAInstrumentation 的 callActivityOnCreate 时,已经将 mBase 替换为我们创建的插件 mBase。创建 PluginContext 在上文解析 apk 是提到。

reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));

所以这里调用 getContentResolver 方法实际是调用 PluginContext 的 getContentResolver 方法。

    @Override
    public ContentResolver getContentResolver() {
        return new PluginContentResolver(getHostContext());
    }

创建了 PluginContentResolver

public class PluginContentResolver extends ContentResolverWrapper {
    private PluginManager mPluginManager;

    public PluginContentResolver(Context context) {
        super(context);
        mPluginManager = PluginManager.getInstance(context);
    }
}

public abstract class ContentResolverWrapper extends ContentResolver {
    
    ContentResolver mBase;
    
    public ContentResolverWrapper(Context context) {
        super(context);
        mBase = context.getContentResolver();
    }
}

调用 query 方法,query 方法会调用到 acquireUnstableProvider 方法。

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected IContentProvider acquireUnstableProvider(Context context, String auth) {
        //判断插件中是否存在
        if (mPluginManager.resolveContentProvider(auth, 0) != null) {
            return mPluginManager.getIContentProvider();
        }
        return super.acquireUnstableProvider(context, auth);
    }

getIContentProvider()

    public synchronized IContentProvider getIContentProvider() {
        if (mIContentProvider == null) {
            hookIContentProviderAsNeeded();
        }
        return mIContentProvider;
    }

重点在 hookIContentProviderAsNeeded()

    protected void hookIContentProviderAsNeeded() {
        //获取代理ContentResolverde Uri
        Uri uri = Uri.parse(RemoteContentProvider.getUri(mContext));
        //调用call方法,mContext是宿主的,所以这里调用的是宿主的ContentProviderde call方法
        mContext.getContentResolver().call(uri, "wakeup", null, null);
        try {
            Field authority = null;
            Field provider = null;
            //获取ActivityThread
            ActivityThread activityThread = ActivityThread.currentActivityThread();
            //得到ActivityThread中mProviderMap
            /**
             *    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
             */
            Map providerMap = Reflector.with(activityThread).field("mProviderMap").get();
            //遍历mProviderMap找到匹配的IContentProvider
            Iterator iter = providerMap.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                Object key = entry.getKey();
                //ProviderClientRecord实例
                Object val = entry.getValue();
                String auth;
                if (key instanceof String) {
                    auth = (String) key;
                } else {
                    if (authority == null) {
                        authority = key.getClass().getDeclaredField("authority");
                        authority.setAccessible(true);
                    }
                    auth = (String) authority.get(key);
                }
                if (auth.equals(RemoteContentProvider.getAuthority(mContext))) {
                    if (provider == null) {
                        /**
                         *     final class ProviderClientRecord {
                         *         final String[] mNames;
                         *         final IContentProvider mProvider;
                         *     }
                         */
                        provider = val.getClass().getDeclaredField("mProvider");
                        provider.setAccessible(true);
                    }
                    IContentProvider rawProvider = (IContentProvider) provider.get(val);
                    /**
                     *     public static IContentProvider newInstance(Context context, IContentProvider iContentProvider) {
                     *         return (IContentProvider) Proxy.newProxyInstance(iContentProvider.getClass().getClassLoader(),
                     *                 new Class[] { IContentProvider.class }, new IContentProviderProxy(context, iContentProvider));
                     *     }
                     */
                    IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider);
                    //用代理的IContentProviderProxy替换IContentProvider,完成Hook IContentProvider
                    mIContentProvider = proxy;
                    Log.d(TAG, "hookIContentProvider succeed : " + mIContentProvider);
                    break;
                }
            }
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

将系统的 IContentProvider 替换成我们自己的代理对象 IContentProviderProxy,之后相关逻辑就在 IContentProviderProxy 中处理。

IContentProviderProxy 的 invoke 实现

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.v(TAG, method.toGenericString() + " : " + Arrays.toString(args));
        wrapperUri(method, args);

        try {
            return method.invoke(mBase, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

wrapperUri()

    private void wrapperUri(Method method, Object[] args) {
        //...

        PluginManager pluginManager = PluginManager.getInstance(mContext);
        ProviderInfo info = pluginManager.resolveContentProvider(uri.getAuthority(), 0);
        if (info != null) {
            String pkg = info.packageName;
            LoadedPlugin plugin = pluginManager.getLoadedPlugin(pkg);
            //获取插件Uri,并拼接成一个新的Uri
            /**
             *         String pkg = loadedPlugin.getPackageName();
             *         String pluginUriString = Uri.encode(pluginUri.toString());
             *         StringBuilder builder = new StringBuilder(RemoteContentProvider.getUri(loadedPlugin.getHostContext()));
             *         builder.append("/?plugin=" + loadedPlugin.getLocation());
             *         builder.append("&pkg=" + pkg);
             *         builder.append("&uri=" + pluginUriString);
             *         Uri wrapperUri = Uri.parse(builder.toString());
             */
            Uri wrapperUri = PluginContentResolver.wrapperUri(plugin, uri);
            if (method.getName().equals("call")) {
                bundleInCallMethod.putString(RemoteContentProvider.KEY_WRAPPER_URI, wrapperUri.toString());
            } else {
                //替换Uir
                args[index] = wrapperUri;
            }
        }
    }

这里并没有和 Activity、Service 一致用三个静态属性,但是却一一对应。

    public static final String KEY_PKG = "pkg";
    public static final String KEY_PLUGIN = "plugin";
    public static final String KEY_URI = "uri";

这样我们启动插件的 ContentProvider 时,会先启动代理的 ContentProvider。

RemoteContentProvider 中 query()

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        //获取ContentProvider ,调用它的query()
        ContentProvider provider = getContentProvider(uri);
        Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI));
        if (provider != null) {
            return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder);
        }

        return null;
    }

getContentProvider() 和 service 中 onStartCommand 方法类似,创建 ContentProvider,条例清晰。

    private ContentProvider getContentProvider(final Uri uri) {
        final PluginManager pluginManager = PluginManager.getInstance(getContext());
        Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI));
        final String auth = pluginUri.getAuthority();
        //缓存
        /**
         *     private static Map<String, ContentProvider> sCachedProviders = new HashMap<>();
         */
        ContentProvider cachedProvider = sCachedProviders.get(auth);
        if (cachedProvider != null) {
            return cachedProvider;
        }

        synchronized (sCachedProviders) {
            //由pkg得到LoadedPlugin
            LoadedPlugin plugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG));
            //插件没有加载则加载插件apk
            if (plugin == null) {
                try {
                    pluginManager.loadPlugin(new File(uri.getQueryParameter(KEY_PLUGIN)));
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
            }
            //从已经加载的apk中得到匹配auh的ProviderInfo,根据ProviderInfo就可以创建插件ContentProvider
            final ProviderInfo providerInfo = pluginManager.resolveContentProvider(auth, 0);
            if (providerInfo != null) {
                //切主线程
                RunUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            LoadedPlugin loadedPlugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG));
                            //创建ContentProvider实例
                            ContentProvider contentProvider = (ContentProvider) Class.forName(providerInfo.name).newInstance();
                            //调用插件的attachInfo,其内部会为插件ContentProvider配置参数并调用它的onCreate方法,这样插件就启动了。
                            contentProvider.attachInfo(loadedPlugin.getPluginContext(), providerInfo);
                            //放入缓存。
                            sCachedProviders.put(auth, contentProvider);
                        } catch (Exception e) {
                            Log.w(TAG, e);
                        }
                    }
                }, true);
                return sCachedProviders.get(auth);
            }
        }

        return null;
    }

BroadcastReceiver 插件化

BroadcastReceiver 插件化上文已经提到,因为<intent-filer>标签无法预料,所以不能用占坑方式创建,只有全部动态的注册来处理,这里不说介绍了。

总结

Google 在 2018 年推出了Android App Bundles,它可以实现模块的动态下载,但是与插件化不同的是,它并不支持四大组件代理的能力。

和热修复一样,如果使用 AppComponentFactory API,我们也可以实现插件化的四大组件代理。但是具体实现上依然需要在 AndroidManifest 中预先注册四大组件,然后具体的替换规则可以在我们自定义的 AppComponentFactory 实现类中埋好。

热修复和插件化作为 Native 动态化方案,它们有一定的局限性。随着移动技术的发展,部分功能可能会被替换成小程序等其他动态化方案。但是从目前来看,它们依然有非常大的存在价值和使用场景。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值