源码分析 — VirtualAPK框架(二)之四大组件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Love667767/article/details/79760911

一、概述

1.1 相关技术点

  1. 设计模式 — 动态代理模式
  2. 源码分析 — Activity的清单注册校验
  3. 源码分析 — ActivityThread(二)之ActivityThread的浅析
  4. 源码分析 — Activity的启动流程
  5. 源码分析 — PackageManagerService(一)之启动流程
  6. 源码分析 — PackageManagerService(二)之resolveIntent()
  7. 源码分析 — Binder机制(一)(进程间通信)
  8. 源码分析 — Binder机制(二)之IActivityManager
  9. 源码分析 — VirtualAPK框架(一)之初始化

1.2 参考文章

1.3 版本

  • Android Framework: Api 23
  • VirtualAPK: com.didi.virtualapk:core 0.9.0

二、Activity

2.1 原理

Activity的启动会涉及到以下两个过程:( 参考:《源码分析 — Activity的清单注册校验》)

  1. 校验: 校验逻辑在 Instrumentation.execStartActivity() 方法中进行;
  2. 创建: 创建Activity类在 Instrumentation.newActivity() 方法中进行;

实现方案:Hook 、Activity占坑

  1. 预先在底座(宿主)中给 Activity 占坑;
  2. Instrumentation.execStartActivity() 中将插件中被启动的Activity替换为底座(宿主)中的 Activity,从而绕过校验;
  3. Instrumentation.newActivity() 中将占坑的 Activity 还原回插件中的 Activity,然后创建 Activity;

2.2 代码分析

CoreLibrary 中 AndroidManifest 里占坑的Activity,各种启动模式都有;

<activity android:name="com.didi.virtualapk.core.A$1" android:launchMode="standard" />
<activity android:name="com.didi.virtualapk.core.A$2" android:launchMode="standard" 
          android:theme="@android:style/Theme.Translucent" />
<activity android:name="com.didi.virtualapk.core.B$1" android:launchMode="singleTop" />
<activity android:name="com.didi.virtualapk.core.C$1" android:launchMode="singleTask" />
<activity android:name="com.didi.virtualapk.core.D$1" android:launchMode="singleInstance" />

下面是执行流程:

说明: VAInstrumentation.execStartActivity() 方法主要有两步操作:

  1. 通过 VAInstrumentation.execStartActivity() 做一些预处理,将目标 Activity 替换为坑位 Activity;
  2. 执行系统的 Instrumentation.execStartActivity() 方法完成校验过程;
// VAInstrumentation.class
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) {
        // 1.根据是否为插件中Activity,来判断是否替换为占坑的Activity;
        this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
    }
    // 2.执行系统的 Instrumentation.execStartActivity() 方法完成校验过程;
    ActivityResult result = realExecStartActivity(who, contextThread, token, target,
                intent, requestCode, options);
    return result;
}

说明: 当目标类在插件中时,执行以下步骤:

  1. 将目标类的包名和类名存储在Bundle中;
  2. 调用 dispatchStubActivity() 方法获取替换的坑位 Activity信息;
// ComponentsHandler.class
public void markIntentIfNeeded(Intent intent) {
    if (intent.getComponent() == null) {
        return;
    }
    String targetPackageName = intent.getComponent().getPackageName();
    String targetClassName = intent.getComponent().getClassName();
    // 这里判断启动的是否是底座(宿主)的Activity;
    if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
        // 1.这里添加一个是否为插件的标记;
        intent.putExtra(Constants.KEY_IS_PLUGIN, true);
        /*
         * 将目标的PackageName和ClassName存储到Bundle中,
         * 便于在newActivity()中获取要还原的目标Activity信息;
         */
        intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
        intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
        // 2.获取替换的坑位 Activity 信息;
        dispatchStubActivity(intent);
    }
}

说明: 将插件中的 Activity 替换为底座(宿主)中的坑位 Activity (启动模式要匹配);

  1. 根据 Intent 从插件中匹配出对应的 ActivityInfo;
  2. 从 ActivityInfo 中获取主题和启动模式;
  3. 根据启动模式去底座(宿主)中查找对应的坑位Activity信息;
  4. 将坑位的 Activity 信息添加进 Intent 中;
// ComponentsHandler.class
private void dispatchStubActivity(Intent intent) {
    ComponentName component = intent.getComponent();
    String targetClassName = intent.getComponent().getClassName();
    // 1.根据Intent去插件中查询ActivityInfo;
    LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
    ActivityInfo info = loadedPlugin.getActivityInfo(component);
    if (info == null) {
        throw new RuntimeException("can not find " + component);
    }
    // 2.获取启动模式;
    int launchMode = info.launchMode;
    Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
    themeObj.applyStyle(info.theme, true);
    /* 
     * 3.根据启动模式,去选择与之匹配的坑位,然后通过目标类名作为Key缓存对应的坑位信息,
     * 便于在此启动时能快速进行类型匹配;
     */
    String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
    Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
    // 4.这里就将我们目标的Component信息替换为坑位的信息了;
    intent.setClassName(mContext, stubActivity);
}

说明:

  1. 将插件中的 Activity 替换为底座(宿主)中的坑位 Activity (启动模式要匹配);
  2. 将匹配到的信息存储在Map集合中,便于下次打开时能快速获取;
// StubActivityInfo.class
public String getStubActivity(String className, int launchMode, Theme theme) {
    String stubActivity= mCachedStubActivity.get(className);
    if (stubActivity != null) {
        return stubActivity;
    }
    // 主题
    TypedArray array = theme.obtainStyledAttributes(new int[]{
            android.R.attr.windowIsTranslucent,
            android.R.attr.windowBackground
    });
    boolean windowIsTranslucent = array.getBoolean(0, false);
    array.recycle();

    stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
    // 1.根据给定的启动模式,选择启动模式相同的坑位;
    switch (launchMode) {
        case ActivityInfo.LAUNCH_MULTIPLE: {
            stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, usedStandardStubActivity);
            if (windowIsTranslucent) {
                stubActivity = String.format(STUB_ACTIVITY_STANDARD, corePackage, 2);
            }
            break;
        }
        case ActivityInfo.LAUNCH_SINGLE_TOP: {
            usedSingleTopStubActivity = usedSingleTopStubActivity % MAX_COUNT_SINGLETOP + 1;
            stubActivity = String.format(STUB_ACTIVITY_SINGLETOP, corePackage, usedSingleTopStubActivity);
            break;
        }
        case ActivityInfo.LAUNCH_SINGLE_TASK: {
            usedSingleTaskStubActivity = usedSingleTaskStubActivity % MAX_COUNT_SINGLETASK + 1;
            stubActivity = String.format(STUB_ACTIVITY_SINGLETASK, corePackage, usedSingleTaskStubActivity);
            break;
        }
        case ActivityInfo.LAUNCH_SINGLE_INSTANCE: {
            usedSingleInstanceStubActivity = usedSingleInstanceStubActivity % MAX_COUNT_SINGLEINSTANCE + 1;
            stubActivity = String.format(STUB_ACTIVITY_SINGLEINSTANCE, corePackage, usedSingleInstanceStubActivity);
            break;
        }

        default:break;
    }
    // 2.将启动的Activity类名作为键,底座中启动模式相同的坑位作为Value,便于二次打开后可以快速获取坑位类型;
    mCachedStubActivity.put(className, stubActivity);
    return stubActivity;
}

说明: 通过 VAInstrumentation.execStartActivity() 的预处理,将目标 Activity 替换为坑位 Activity;

  1. 调用 Instrumentation.execStartActivity() 方法完成校验步骤;
// VAInstrumentation.class
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};
        // 这里通过反射调用了Instrumentation.execStartActivity()方法;
        result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,
                "execStartActivity", parameterTypes,
                who, contextThread, token, target, intent, requestCode, options);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return result;
}

以上部分就是绕过Android系统校验Activity的过程;
实际上,在接下来的创建Activity过程中,如果不还原回我们的目标Activity,那么真正启动的会是坑位Activity;
那如何才能启动我们的目标Activity呢?


说明:

  1. 在 VirtualApk 初始化时,将 VAInstrumentation 通过反射赋值给mCallback ( VAInstrumentation实现 Handler.Callback 接口);
  2. mCallback 存在的情况下,会先执行 mCallback.handleMessage(msg) 方法;
  3. 执行 Handler.handlerMessage(msg) 方法;
// Handler.class
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 1.在VirtualApk初始化时,将VAInstrumentation通过反射赋值给mCallback;
        if (mCallback != null) {
            // 2.先执行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 3.后执行(这就可以在系统ActivityThread.H.handleMessage()之前,做预处理了)
        handleMessage(msg);
    }
}

说明:

  1. 设置类加载器;
  2. 设置主题;
// VAInstrumentation.class
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");
            // 1.设置类加载器
            intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());
            ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");
            // 2.设置插件中目标Activity的主题
            if (PluginUtil.isIntentFromPlugin(intent)) {
                int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                if (theme != 0) {
                    activityInfo.theme = theme;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    return false;
}

说明: 通过 VAInstrumentation.newActivity() 进行预处理,将坑位 Activity 替换为目标 Activity;

  1. 从 Intent 的 Bundle 中获取之前的目标类;
  2. 调用系统的 Instrumentation.newActivity() 方法来创建目标 Activity 对象
  3. 将坑位接收的 Intent 信息传递给目标 Activity;
// VAInstrumentation.class
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
   try {
       // 这里是启动宿主(底座)的Activity;
        cl.loadClass(className);
    } catch (ClassNotFoundException e) {
        LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
        // 1.从Intent的Bundle中获取之前目标类;
        String targetClassName = PluginUtil.getTargetActivity(intent);

        if (targetClassName != null) {
            /* 
             * 2.调用系统的 Instrumentation.newActivity() 方法来创建目标 Activity 对象;
             * 注:这里要使用插件的ClassLoader加载器;
             */
            Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
            // 3.将坑位接收的Intent信息传递给目标Activity;
            activity.setIntent(intent);

            try {
                // for 4.1+
                ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
            } catch (Exception ignored) {
                // ignored.
            }
            return activity;
        }
    }
    // 4.调用系统的 Instrumentation.newActivity()方法;
    return mBase.newActivity(cl, className, intent);
}

说明: 在 Activity 创建之后,会调用到 Instrumentation.callActivityOnCreate() 方法;

  1. 将目标Activity里的 mResources,mBase,mApplication,替换为 LoadedPlugin 中生成的可以用于加载插件资源的相应 Resources 和 Context;
    1. 每个Activity都持有一个ContextImpl实例;
    2. 启动插件的Activity,其内部的 mApplication、mResources、mBase 等仍然是宿主(底座)的,需要替换成插件的mApplication、mResources、mBase才能操作插件内部的资源等;
  2. 设置Activity的横竖屏方向;
// VAInstrumentation.class
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    final Intent intent = activity.getIntent();
    // 判断是否是插件
    if (PluginUtil.isIntentFromPlugin(intent)) {
        Context base = activity.getBaseContext();
        try {
            /*
             * 1.将目标Activity里的mResources,mBase,mApplication,
             * 替换为LoadedPlugin中生成的可以用于加载插件资源的相应Resources和Context;
             * 注:
             * 1)每个Activity都持有一个ContextImpl实例;
             * 2)启动插件的Activity,其内部的mApplication、mResources、mBase等仍然是宿主(底座)的,需要替换成插件的mApplication、mResources、mBase才能操作插件内部的资源等;
             */
            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());

            // 2.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);
}

到此,整个Activity 的创建就已经结束了;



三、BroadcastReceiver

说明: 广播注册由 静态 转 动态

  1. 在初始化插件时,会将插件清单文件中静态广播动态的注册到底座(宿主)中;
LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws PackageParser.PackageParserException {

    // Register broadcast receivers dynamically
    Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
    for (PackageParser.Activity receiver : this.mPackage.receivers) {
        receivers.put(receiver.getComponentName(), receiver.info);

        try {
            BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
            for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
                // 动态注册到底座(宿主)中;
                this.mHostContext.registerReceiver(br, aii);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    this.mReceiverInfos = Collections.unmodifiableMap(receivers);
    this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
}


四、Service

CoreLibrary 中 AndroidManifest 里占坑的 Service,一种是主进程,一种的其他进程;

<!-- Local Service running in main process -->
<service android:name="com.didi.virtualapk.delegate.LocalService" />

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

说明: 启动Service时,会调用系统的 ActivityManagerProxy.startService() 方法,而之前提到过,系统的 ActivityManagerProxy 已经被 VirtualApk 中的 ActivityManagerProxy 动态代理了,所以最终会先调用 VirtualApk 中的 ActivityManagerProxy.invoke() 方法;

// ActivityManagerProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   if ("startService".equals(method.getName())) {
        return startService(proxy, method, args);
    }
    // ...省略stopService()、stopServiceToken()、bindService()、unbindService()等方法...

    try {
        // sometimes system binder has problems.
        return method.invoke(this.mActivityManager, args);
    } catch (Throwable th) {
        // ...
}

说明:

  1. 根据给定的 Intent 去插件中匹配对应的 Service;
  2. 如果没有匹配到,说明目标 Service 在底座中;
  3. 如果匹配到,则说明目标 Service 在某一个已经被加载的插件中;
private Object startService(Object proxy, Method method, Object[] args) throws Throwable {
    IApplicationThread appThread = (IApplicationThread) args[0];
    Intent target = (Intent) args[1];
    // 1.mPluginManager是ActivityManagerProxy对象
    ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
    if (null == resolveInfo || null == resolveInfo.serviceInfo) {
        // 2.启动底座(宿主)的Service
        return method.invoke(this.mActivityManager, args);
    }
    // 3.启动插件中的Service
    return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
}

说明:

  1. 给目标 Service 设置 ComponentName;
  2. 判断当前 Service 启动的进程:
    1. 若在主进程,使用 LocalService 进行分发;
    2. 若不在主进程,则使用 RemoteService 进行分发;
  3. 新建一个 Intent,存储底座中真正代理的 Service 信息;
  4. 将目标 Service 信息存入到代理 Service 的 Intent 中;
  5. 启动底座中的代理 Service;
private ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
    Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
    // 5.启动底座的Service,让它去分发插件中的Service;
    return mPluginManager.getHostContext().startService(wrapperIntent);
}

private Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
    // 1.fill in service with ComponentName
    target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
    String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();

    // 2.start delegate service to run plugin service inside
    boolean local = PluginUtil.isLocalService(serviceInfo);
    Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;
    Intent intent = new Intent();
    // 3.启动宿主的Service,用来分发插件中的Service
    intent.setClass(mPluginManager.getHostContext(), delegate);
    // 4.将插件中真正要启动的 Service 信息存入代理 Service 的 Intent 中;
    intent.putExtra(RemoteService.EXTRA_TARGET, target);
    intent.putExtra(RemoteService.EXTRA_COMMAND, command);
    intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
    if (extras != null) {
        intent.putExtras(extras);
    }

    return intent;
}

说明: 由上一步可知,最终底座的代理 Service 被启动;

  1. 根据是否有EXTRA_TARGET 或 EXTRA_COMMAND 来判断启动的是否是插件的 Service;
  2. 取出了插件中真正要启动的 Service 信息;
  3. 判断这个插件的 Service 是否被启动过;
  4. 通过反射构造插件的 Service 对象;
  5. 调用 Service.attach() 进行关联;
  6. 代理执行插件中Service.onCreate()方法;
  7. 将启动过的 Service 存储起来(当Service被销毁时,从这里移除);
  8. 代理执行插件中Service.onStartCommand()方法;
// LocalService.class
public int onStartCommand(Intent intent, int flags, int startId) {
    // 1.根据是否有EXTRA_TARGET 或 EXTRA_COMMAND 来判断启动的是否是插件的 Service;
    if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) {
        return START_STICKY;
    }
    // 2.取出了插件中真正要启动的 Service 信息;
    Intent target = intent.getParcelableExtra(EXTRA_TARGET);
    int command = intent.getIntExtra(EXTRA_COMMAND, 0);
    if (null == target || command <= 0) {
        return START_STICKY;
    }

    ComponentName component = target.getComponent();
    LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);

    switch (command) {
        case EXTRA_COMMAND_START_SERVICE: {
            ActivityThread mainThread = (ActivityThread)ReflectUtil.getActivityThread(getBaseContext());
            IApplicationThread appThread = mainThread.getApplicationThread();
            Service service;
            // 3.判断这个插件的 Service 是否被启动过;
            if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
                service = this.mPluginManager.getComponentsHandler().getService(component);
            } else {
                try {
                    // 4.通过反射构造插件的 Service 对象;
                    service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();

                    Application app = plugin.getApplication();
                    IBinder token = appThread.asBinder();
                    Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
                    IActivityManager am = mPluginManager.getActivityManager();
                    // 5.调用 Service.attach() 进行关联;
                    attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
                    // 6.代理执行插件中Service.onCreate()方法
                    service.onCreate();
                    // 7.将启动过的 Service 存储起来(当Service被销毁时,从这里移除)
                    this.mPluginManager.getComponentsHandler().rememberService(component, service);
                } catch (Throwable t) {
                    return START_STICKY;
                }
            }
            // 8.代理执行插件中Service.onStartCommand()方法
            service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
            break;
        }
        case EXTRA_COMMAND_BIND_SERVICE: 
        case EXTRA_COMMAND_STOP_SERVICE:
        case EXTRA_COMMAND_UNBIND_SERVICE:
    }

    return START_STICKY;
}


五、ContentProvider

猜你喜欢

转载自blog.csdn.net/Love667767/article/details/79760911