内容简介:Android插件化不算是一门新技术,发展了有一些年头了。不同公司的插件化方案大体原理上很相似。本文通过阅读爱奇艺的所谓的插件其实本质上也是一个apk。在原生的Android应用中,apk在运行时会被映射成一个插件的安装分为内置插件(asset目录,sdcard)和线上插件两部分。
Android插件化不算是一门新技术,发展了有一些年头了。不同公司的插件化方案大体原理上很相似。本文通过阅读爱奇艺的 Neptune
框架来介绍插件化的整体思路和流程。
插件化基础知识点
插件应用安装
所谓的插件其实本质上也是一个apk。在原生的Android应用中,apk在运行时会被映射成一个 LoadedApk
对象。插件在安装之后也会被映射成类似的 PluginLoadedApk
对象,统一管理插件的相关信息。
public class PluginLoadedApk { public static final ConcurrentMap<String, Vector<Method>> sMethods = new ConcurrentHashMap<String, Vector<Method>>(1); private static final String TAG = "PluginLoadedApk"; /* 保存注入到宿主ClassLoader的插件 */ private static Set<String> sInjectedPlugins = Collections.synchronizedSet(new HashSet<String>()); /* 保存所有的插件ClassLoader */ private static Map<String, DexClassLoader> sAllPluginClassLoader = new ConcurrentHashMap<>(); /* 宿主的Context */ private final Context mHostContext; /* 宿主的ClassLoader */ private final ClassLoader mHostClassLoader; /* 宿主的Resource对象 */ private final Resources mHostResource; /* 宿主的包名 */ private final String mHostPackageName; /* 插件的路径 */ private final String mPluginPath; /* 插件运行的进程名 */ private final String mProcessName; /* 插件ClassLoader的parent */ private ClassLoader mParent; /* 插件的类加载器 */ private DexClassLoader mPluginClassLoader; /* 插件的Resource对象 */ private Resources mPluginResource; /* 插件的AssetManager对象 */ private AssetManager mPluginAssetManager; /* 插件的全局默认主题 */ private Resources.Theme mPluginTheme; /* 插件的详细信息,主要通过解析AndroidManifest.xml获得 */ private PluginPackageInfo mPluginPackageInfo; /* 插件工程的包名 */ private String mPluginPackageName; /* 插件的Application */ private Application mPluginApplication; /* 自定义插件Context,主要用来改写其中的一些方法从而改变插件行为 */ private PluginContextWrapper mPluginAppContext; /* 自定义Instrumentation,对Activity跳转进行拦截 */ private PluginInstrument mPluginInstrument; ... } 复制代码
插件的安装分为内置插件(asset目录,sdcard)和线上插件两部分。
- 内置插件:
- 约定存放在assets/pluginapp/<plugin_pkg_name>.apk形式,安装时解压到/data/data/<host_pkg_name>/app_pluginapp目录
- sdcard插件,允许调试模式下安装,以<plugin_pkg_name>.apk命名
- 线上插件:直接将插件下载到sdcard目录上,然后拷贝到/data/data/<host_pkg_name>/app_pluginapp目录下;为了减少拷贝操作,可以直接下载到/data/data/<hots_pkg_name>/app_pluginapp目录;
插件的安装通过运行在独立进程的Service完成,主要防止部分机型dexopt hang住主进程。
dexopt
Android根据系统版本不同会采用两种虚拟机。Dalvik虚拟机是JIT方式解释执行dex字节码;ART虚拟机是AOT方式将dex字节码转化为oat机器码。
- Dalvik是运行时解释dex文件,安装比较快,开启应用比较慢,应用占用空间小
- ART是安装的时候字节码预编译成机器码存储在本地,执行的时候直接就可以运行的,安装慢,开启应用快,占用空间大;
如果当前运行在Dalvik虚拟机下,Dalvik会对classes.dex进行一次“翻译”,“翻译”的过程也就是守护进程installd的函数dexopt来对dex字节码进行优化,实际上也就是由dex文件生成odex文件,最终odex文件被保存在手机的VM缓存目录data/dalvik-cache下(注意!这里所生成的odex文件依旧是以dex为后缀名,格式如:system@priv-app@Settings@Settings.apk@classes.dex)。如果当前运行于ART模式下, ART同样会在首次进入系统的时候调用/system/bin/dexopt(此处应该是dex2oat工具吧)工具来将dex字节码翻译成本地机器码,保存在data/dalvik-cache下。 那么这里需要注意的是,无论是对dex字节码进行优化,还是将dex字节码翻译成本地机器码,最终得到的结果都是保存在相同名称的一个odex文件里面的,但是前者对应的是一个.dex文件(表示这是一个优化过的dex),后者对应的是一个.oat文件。通过这种方式,原来任何通过绝对路径引用了该odex文件的代码就都不需要修改了。 由于在系统首次启动时会对应用进行安装,那么在预置APK比较多的情况下,将会大大增加系统首次启动的时间。
对于插件安装来说,插件的安装通过运行在独立进程的Service完成,主要防止部分机型dexopt hang住主进程。
插件安装过程主要执行以下几步:
- 拷贝apk到内置存储区,重命名为<plugin_pkg_name>.apk
- 解压apk中的so库到app_pluginapp/<plugin_pkg_name>/lib目录
- dexopt优化插件dex,Android 7.0以上第一次会使用解释模式执行dex,优化加载速度
类加载
Java中的类都是通过ClassLoader加载的,而Android中类的加载也离不开ClassLoadder。在Android系统中,主要的ClassLoader有三个:
- BootClassLoader:Android系统启动时用来预加载常用的类
- PathClassLoader:用来加载系统和应用程序中的类,如果是非系统应用程序类,则会加载/data/app目录下的dex、apk或jar文件
- DexClassLoader:可以加载指定路径的dex、apk或jar文件,支持从SD卡进行加载,是插件化的技术基础
类加载的双亲委派机制
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
关于插件中类的加载机制有两种处理方式,一种是单类加载机制,另一种是多类加载机制;单类加载器机制,即所有插件APP的类都通过宿主的ClassLoader(即PathClassLoader)进行加载,与MultiDex、Qzone热修复技术类似,通过Dex前插后者后插的方式实现。采用单类加载器模型,随着业务团队和插件的增加,很容易出现类重复问题,无法保证所有类都是独一无二的。多类加载器机制是指每个插件都由一个新的类加载器实例来加载,组件间的类是完全隔离,不能直接互相访问。
利用ClassLoader的双亲委派机制,多类加载有两种思路:
- 自定义代理的ClassLoader设置为PathClassLoader的父类加载器,那么自定义的类加载器就能代理所有的类加载行为;在代理ClassLoader内部做类加载的逻辑分发,先尝试从宿主的ClassLoader加载,再尝试插件的ClassLoader加载。(好处:只需要在启动时hook ClassLoader,添加DelegateClassLoader,后续的类加载由DelegateClassLoader分发;对于未加载的插件,可以通过包名匹配,先触发插件加载,再加载类)
- 每个
PluginLoadedApk
维护一个PluginClassLoader
实例,其父ClassLoader是PathClassLoader;在类加载时,先尝试从宿主的ClassLoader加载,再尝试本插件的ClassLoader加载。(好处:每个插件维护自己的PluginLoadedApk
,不存在分发,类隔离做的更好)
资源加载
Android APP运行除了类还有资源,运行时需要加载资源;对于Android来说,资源是通过AssetManager和Resources这两个类管理。App在运行时查找资源是通过当前Context的Resource实例中查找,在Resource内部是通过AssetManager管理当前的资源,AssetManager维护了资源包路径的数组。插件化的原理,就是将插件的资源路径添加到AssetManager的资源路径数组中,通过反射AssetManager的隐藏方法addAssetPath实现插件资源的加载。
try{ AssetManager am = AssetManager.class.newInstance(); Method addAssetPath = AssetManager.class.getDeclaredMethod("addAssetPath", String.class); addAssetPath.setAccessible(true); addAssetPath.invoke(am, pluginApkPath); Resources pluginResources = new Resources(am, hostResource.getDisplayMetrics(), hostResources.getConfiguration()); } catch (Exception e) { e.printStackTrace(); } 复制代码
各种插件化方案的资源加载原理都是一样,区别主要在于不同插件的资源管理,是公用一套资源还是插件独立资源,插件和宿主的资源访问ID冲突问题。
- 公用一套资源需要采用固定资源id及ID分段机制避免冲突
- 独立资源方案,不同插件管理自己的资源
插件化中资源使用限制
限制:插件不能使用自己的转场动画,只能使用宿主、系统定义的转场动画。
转场动画最终会调用到 IActivityManager
,发起IPC请求,与AMS交互
public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) throws RemoteException; 复制代码
Apk打包流程
先附上两张Android原生打包流程图

在插件编译打包时,需要完成以下几件事:
- 插件的资源和宿主的资源通过不同的资源分段区分
- 在插件化中,如果插件需要引用宿主的资源,则需要将宿主的资源id进行固定
- 处理插件aapt的编译产物,不将宿主的资源打入apk中
- 处理Manifest文件,将占坑的四大组件写入Manifest文件中
- 在字节码层面对代码做修改
Hook点
- Hook MergeResources Task,将public.xml文件拷贝至资源merge完成的目录
- Hook ProcessAndroidResources Task,修改生成的arsc文件。
- Hook ManifestProcessorTask, 在Manifest中插入特定信息。
- Hook dexTask/Transform,最源代码的修改
四大组件的插件化
Activity的插件化
Activity启动可以分为两个阶段:往AMS发起启动Activity的请求、AMS校验后执行Activity启动。
往AMS发起请求
在Android 8.0(api 26)以下,应用往AMS发起启动Activity请求的流程如上。在Android 8.0及以上版本,AMN、AMP已经被弃用,而是使用 ActivityManager
类; 参考文章 。
Hook点:
- Hook Instrumentation类,代理execStartActivity方法
- Hook AMN(<26)/ActivityManager(>=26),动态代理IActivityManager接口的实例对象
AMS校验后启动Activity
Android P(api 28)对Activity的启动过程做了修改;在Android P之前,是在H类的handleMessage方法的switch分支语句中,有专门处理启动Activity的逻辑
public void handleMessage(Message msg) { switch (msg.what) { case LAUNCH_ACTIVITY: { final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); } break; //以下省略很多代码 } } 复制代码
在Android P中,启动Activity的这部分逻辑,被转移到了LaunchActivityItem类的execute方法中
public class LaunchActivityItem extends ClientTransactionItem { @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mIsForward, mProfilerInfo, client); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); } } 复制代码
Android P把H类中的100-109这10个消息都删除了,取而代之的是159这个消息,名为EXECUTE_TRANSACTION。收敛了Activity相关的message分发。
Hook点:
- Hook H类,将占坑Activity替换成真实的Activity(需要做Android P的适配)
- Hook Instrumentation类,替换成自定义的Instrument,重写newActivity、callActivityOnCreate等方法
Service的插件化
Service启动可以分为两个阶段:往AMS发起启动Service的请求、AMS校验后执行Service启动。
往AMS发起启动Service的请求
在Android 8.0(api 26)以下,应用往AMS发起启动Service请求的流程如上。在Android 8.0及以上版本,AMN、AMP已经被弃用,而是使用ActivityManager类。
Hook点
- Hook ContextWrapper;替换成自定义的ContextWrapper,将Service替换成占坑的Service
- Hook AMN(<26)/ActivityManager(>=26),动态代理IActivityManager接口的实例对象
AMS校验后执行Service启动
Hook点:
- 在占坑Service的onStartCommand提取真实Service信息,并分发执行真实Service逻辑
- Hook handleServiceCreate方法,发射获取ServiceInfo,修改ServiceInfo的name字段为真实Service的名字。加载真实的Service类。
BroadCastReceiver插件化
广播分为静态广播、动态广播。动态广播在运行时向AMS注册相关信息。
Hook点:
- 静态广播转换为动态广播
ContentProvider插件化
Hook点:
- Hook AMN(<26)/ActivityManager(>=26),动态代理IActivityManager接口的实例对象;将目标ContentProvider的信息放在query参数中
Neptune源码分析
Neptune类
Neptune
类是整个插件系统的入口类,主要方法如下
- init()方法
/** * 初始化Neptune插件环境 * * @param application 宿主的Appliction * @param config 配置信息 */ public static void init(Application application, NeptuneConfig config) { sHostContext = application; sGlobalConfig = config != null ? config : new NeptuneConfig.NeptuneConfigBuilder().build(); PluginDebugLog.setIsDebug(sGlobalConfig.isDebug()); boolean hookInstr = VersionUtils.hasPie() || sGlobalConfig.getSdkMode() != NeptuneConfig.LEGACY_MODE; if (hookInstr) { //Hook Instrumentation hookInstrumentation(); } // 调用getInstance()方法会初始化bindService,管理插件安装的Service PluginPackageManagerNative.getInstance(sHostContext).setPackageInfoManager(sGlobalConfig.getPluginInfoProvider()); // 注册插件卸载监听广播 PluginManager.registerUninstallReceiver(sHostContext); } 复制代码
- launchPlugin()方法
/** * 启动插件 * * @param mHostContext 主工程的上下文 * @param mIntent 需要启动的组件的Intent * @param mServiceConnection bindService时需要的ServiceConnection,如果不是bindService的方式启动组件,传入Null * @param mProcessName 需要启动的插件运行的进程名称,插件方可以在Application的android:process指定 * 如果没有指定,则有插件中心分配 */ public static void launchPlugin(final Context mHostContext, final Intent mIntent, final ServiceConnection mServiceConnection, final String mProcessName) { final String packageName = tryParsePkgName(mHostContext, mIntent); if (TextUtils.isEmpty(packageName)) { if (null != mHostContext) { deliver(mHostContext, false, mHostContext.getPackageName(), ErrorType.ERROR_PLUGIN_LOAD_NO_PKGNAME_INTENT); } PluginDebugLog.runtimeLog(TAG, "enterProxy packageName is null return! packageName: " + packageName); return; } // 处理不同进程跳转 final String targetProcessName = TextUtils.isEmpty(mProcessName) ? ProcessManager.chooseDefaultProcess(mHostContext, packageName) : mProcessName; String currentProcess = FileUtils.getCurrentProcessName(mHostContext); if (!TextUtils.equals(currentProcess, targetProcessName)) { // 启动进程和目标进程不一致,需要先启动目标进程,初始化PluginLoadedApk Intent transIntent = new Intent(); transIntent.setAction(IntentConstant.ACTION_START_PLUGIN); //目标进程的Service中重新通过mIntent启动插件 transIntent.putExtra(IntentConstant.EXTRA_START_INTENT_KEY, mIntent); transIntent.putExtra(IntentConstant.EXTRA_TARGET_PROCESS, targetProcessName); try { String proxyServiceName = ComponentFinder.matchServiceProxyByFeature(targetProcessName); transIntent.setClass(mHostContext, Class.forName(proxyServiceName)); mHostContext.startService(transIntent); } catch (ClassNotFoundException e) { e.printStackTrace(); } return; } LinkedBlockingQueue<Intent> cacheIntents = PActivityStackSupervisor.getCachedIntent(packageName); //该插件有其他任务排队中,mIntent添加到队尾 if (cacheIntents != null && cacheIntents.size() > 0) { cacheIntents.add(mIntent); PluginDebugLog.runtimeLog(TAG, "LoadingMap is not empty, Cache current intent, intent: " + mIntent + ", packageName: " + packageName); return; } boolean isLoadAndInit = isPluginLoadedAndInit(packageName); if (!isLoadAndInit) { if (null == cacheIntents) { cacheIntents = new LinkedBlockingQueue<Intent>(); PActivityStackSupervisor.addCachedIntent(packageName, cacheIntents); } // 缓存这个intent,等待PluginLoadedApk加载到内存之后再启动这个Intent PluginDebugLog.runtimeLog(TAG, "Environment is initializing and loading, cache current intent first, intent: " + mIntent); cacheIntents.add(mIntent); } else { PluginDebugLog.runtimeLog(TAG, "Environment is already ready, launch current intent directly: " + mIntent); //可以直接启动组件 readyToStartSpecifyPlugin(mHostContext, mServiceConnection, mIntent, true); return; } // 处理插件的依赖关系 final PluginLiteInfo info = PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext()) .getPackageInfo(packageName); //获取插件的依赖关系 final List<String> mPluginRefs = PluginPackageManagerNative.getInstance(mHostContext) .getPluginRefs(packageName); if (info != null && mPluginRefs != null && mPluginRefs.size() > 0) { PluginDebugLog.runtimeLog(TAG, "start to check dependence installation size: " + mPluginRefs.size()); //依赖的总数量 final AtomicInteger count = new AtomicInteger(mPluginRefs.size()); for (String pkgName : mPluginRefs) { PluginDebugLog.runtimeLog(TAG, "start to check installation pkgName: " + pkgName); final PluginLiteInfo refInfo = PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext()) .getPackageInfo(pkgName); PluginPackageManagerNative.getInstance(mHostContext.getApplicationContext()).packageAction(refInfo, new IInstallCallBack.Stub() { @Override public void onPackageInstalled(PluginLiteInfo packageInfo) { //未ready的依赖数量 count.getAndDecrement(); PluginDebugLog.runtimeLog(TAG, "check installation success pkgName: " + refInfo.packageName); if (count.get() == 0) { PluginDebugLog.runtimeLog(TAG, "start Check installation after check dependence packageName: " + packageName); //真正加载插件 checkPkgInstallationAndLaunch(mHostContext, info, mServiceConnection, mIntent, targetProcessName); } } @Override public void onPackageInstallFail(PluginLiteInfo info, int failReason) throws RemoteException { PluginDebugLog.runtimeLog(TAG, "check installation failed pkgName: " + info.packageName + " failReason: " + failReason); count.set(-1); } }); } } else if (info != null) { PluginDebugLog.runtimeLog(TAG, "start Check installation without dependence packageName: " + packageName); //真正加载插件 checkPkgInstallationAndLaunch(mHostContext, info, mServiceConnection, mIntent, targetProcessName); } else { //异常case PluginDebugLog.runtimeLog(TAG, "pluginLiteInfo is null packageName: " + packageName); PActivityStackSupervisor.clearLoadingIntent(packageName); if (PluginDebugLog.isDebug()) { throw new IllegalStateException("pluginLiteInfo is null when launchPlugin " + packageName); } } } 复制代码
/** * 真正启动一个组件 * * @param mHostContext 主工程Context * @param mLoadedApk 需要启动的插件的PluginLoadedApk * @param mIntent 需要启动组件的Intent * @param mConnection bindService时需要的ServiceConnection,如果不是bindService的方式启动组件,传入Null */ private static void doRealLaunch(Context mHostContext, PluginLoadedApk mLoadedApk, Intent mIntent, ServiceConnection mConnection) { String targetClassName = ""; ComponentName mComponent = mIntent.getComponent(); if (mComponent != null) { //显式启动 targetClassName = mComponent.getClassName(); PluginDebugLog.runtimeLog(TAG, "launchIntent_targetClassName:" + targetClassName); if (TextUtils.isEmpty(targetClassName)) { targetClassName = mLoadedApk.getPluginPackageInfo().getDefaultActivityName(); } } String pkgName = mLoadedApk.getPluginPackageName(); Class<?> targetClass = null; if (!TextUtils.isEmpty(targetClassName) && !TextUtils.equals(targetClassName, IntentConstant.EXTRA_VALUE_LOADTARGET_STUB)) { try { //插件ClassLoader加载类 targetClass = mLoadedApk.getPluginClassLoader().loadClass(targetClassName); } catch (Exception e) { deliver(mHostContext, false, pkgName, ErrorType.ERROR_PLUGIN_LOAD_COMP_CLASS); PluginDebugLog.runtimeLog(TAG, "launchIntent loadClass failed for targetClassName: " + targetClassName); executeNext(mLoadedApk, mConnection, mHostContext); return; } } String action = mIntent.getAction(); if (TextUtils.equals(action, IntentConstant.ACTION_PLUGIN_INIT) || TextUtils.equals(targetClassName, IntentConstant.EXTRA_VALUE_LOADTARGET_STUB)) { PluginDebugLog.runtimeLog(TAG, "launchIntent load target stub!"); //通知插件初始化完毕 if (targetClass != null && BroadcastReceiver.class.isAssignableFrom(targetClass)) { Intent newIntent = new Intent(mIntent); newIntent.setComponent(null); newIntent.putExtra(IntentConstant.EXTRA_TARGET_PACKAGE_KEY, pkgName); newIntent.setPackage(mHostContext.getPackageName()); //通过广播通知插件加载完成 mHostContext.sendBroadcast(newIntent); } // 表示后台加载,不需要处理该Intent executeNext(mLoadedApk, mConnection, mHostContext); return; } mLoadedApk.changeLaunchingIntentStatus(true); PluginDebugLog.runtimeLog(TAG, "launchIntent_targetClass: " + targetClass); if (targetClass != null && Service.class.isAssignableFrom(targetClass)) { //处理的是Service, 宿主启动插件Service只能通过显式启动 ComponentFinder.switchToServiceProxy(mLoadedApk, mIntent, targetClassName); if (mConnection == null) { mHostContext.startService(mIntent); } else { mHostContext.bindService(mIntent, mConnection, mIntent.getIntExtra(IntentConstant.BIND_SERVICE_FLAGS, Context.BIND_AUTO_CREATE)); } } else { //处理的是Activity ComponentFinder.switchToActivityProxy(pkgName, mIntent, -1, mHostContext); PActivityStackSupervisor.addLoadingIntent(pkgName, mIntent); Context lastActivity = null; PActivityStackSupervisor mActivityStackSupervisor = mLoadedApk.getActivityStackSupervisor(); lastActivity = mActivityStackSupervisor.getAvailableActivity(); if (mHostContext instanceof Activity) { mHostContext.startActivity(mIntent); } else if (lastActivity != null) { // Clear the Intent.FLAG_ACTIVITY_NEW_TASK int flag = mIntent.getFlags(); flag = flag ^ Intent.FLAG_ACTIVITY_NEW_TASK; mIntent.setFlags(flag); lastActivity.startActivity(mIntent); } else { // Add the Intent.FLAG_ACTIVITY_NEW_TASK mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mHostContext.startActivity(mIntent); } } // 执行下一个Intent executeNext(mLoadedApk, mConnection, mHostContext); } 复制代码
/** * 异步初始化插件,宿主静默加载插件 * * @deprecated 不建议使用 */ @Deprecated public static void initPluginAsync(final Context mHostContext, final String packageName, final String processName, final org.qiyi.pluginlibrary.listenter.IPluginStatusListener mListener) { // 插件已经加载 if (PluginManager.isPluginLoadedAndInit(packageName)) { if (mListener != null) { mListener.onInitFinished(packageName); } return; } BroadcastReceiver recv = new BroadcastReceiver() { public void onReceive(Context ctx, Intent intent) { String curPkg = IntentUtils.getTargetPackage(intent); if (IntentConstant.ACTION_PLUGIN_INIT.equals(intent.getAction()) && TextUtils.equals(packageName, curPkg)) { PluginDebugLog.runtimeLog(TAG, "收到自定义的广播org.qiyi.pluginapp.action.TARGET_LOADED"); //插件初始化结束 if (mListener != null) { mListener.onInitFinished(packageName); } mHostContext.getApplicationContext().unregisterReceiver(this); } } }; PluginDebugLog.runtimeLog(TAG, "注册自定义广播org.qiyi.pluginapp.action.TARGET_LOADED"); IntentFilter filter = new IntentFilter(); filter.addAction(IntentConstant.ACTION_PLUGIN_INIT); //注册广播 mHostContext.getApplicationContext().registerReceiver(recv, filter); Intent intent = new Intent(); intent.setAction(IntentConstant.ACTION_PLUGIN_INIT); intent.setComponent(new ComponentName(packageName, recv.getClass().getName())); //发送一个启动插件的intent launchPlugin(mHostContext, intent, processName); } 复制代码
NeptuneInstrument(PluginInstrument)类
Hook系统原生的Instrument,替换成NeptuneInstrument。 NeptuneInstrument
继承 PluginInstrument
。 PluginInstrument
负责往AMS发送的请求, NeptuneInstrument
负责AMS返回的结果处理。
/** * 负责转移插件的跳转目标<br> * 用于Hook插件Activity中Instrumentation * * @see android.app.Activity#startActivity(android.content.Intent) */ public class PluginInstrument extends Instrumentation { private static final String TAG = "PluginInstrument"; private static ConcurrentMap<String, Vector<Method>> sMethods = new ConcurrentHashMap<String, Vector<Method>>(5); Instrumentation mHostInstr; private String mPkgName; private ReflectionUtils mInstrumentRef; /** * 插件的Instrumentation */ public PluginInstrument(Instrumentation hostInstr) { this(hostInstr, ""); } public PluginInstrument(Instrumentation hostInstr, String pkgName) { mHostInstr = hostInstr; mInstrumentRef = ReflectionUtils.on(hostInstr); mPkgName = pkgName; } /** * 如果是PluginInstrumentation,拆装出原始的HostInstr * * @param instrumentation * @return */ public static Instrumentation unwrap(Instrumentation instrumentation) { if (instrumentation instanceof PluginInstrument) { return ((PluginInstrument) instrumentation).mHostInstr; } return instrumentation; } /** * @Override */ public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ... } /** * @Override */ public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) { ... } /** * @Override For below android 6.0 */ public ActivityResult execStartActivityAsCaller( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, int userId) { ... } /** * @Override For android 6.0 */ public ActivityResult execStartActivityAsCaller( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, int userId) { ... } /** * @Override */ public void execStartActivitiesAsUser( Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId) { ... } /** * @Override For below android 6.0, start activity from Fragment */ public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options) { ... } /** * @Override For android 6.0, start activity from Fragment */ public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { ... } } 复制代码
/** * 自定义的全局的Instrumentation * 负责转移插件的跳转目标和创建插件的Activity实例 * 用于Hook ActivityThread中的全局Instrumentation */ public class NeptuneInstrument extends PluginInstrument { private static final String TAG = "NeptuneInstrument"; private PluginActivityRecoveryHelper mRecoveryHelper = new PluginActivityRecoveryHelper(); public NeptuneInstrument(Instrumentation hostInstr) { super(hostInstr); } @Override public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { ... } @Override public void callActivityOnCreate(Activity activity, Bundle icicle) { ... } @Override public void callActivityOnDestroy(Activity activity) { ... } @Override public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { ... } /** * 将Activity反射相关操作分发给插件Activity的基类 */ private boolean dispatchToBaseActivity(Activity activity) { //这个模式已弃用 return Neptune.getConfig().getSdkMode() == NeptuneConfig.INSTRUMENTATION_BASEACT_MODE && activity instanceof IPluginBase; } } 复制代码
NeptuneInstrument
类中的几个方法有一些特殊的逻辑处理,下面单独分析:
@Override public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { if (className.startsWith(ComponentFinder.DEFAULT_ACTIVITY_PROXY_PREFIX)) { // 插件代理Activity,替换回插件真实的Activity String[] result = IntentUtils.parsePkgAndClsFromIntent(intent); String packageName = result[0]; String targetClass = result[1]; PluginDebugLog.runtimeLog(TAG, "newActivity: " + className + ", targetClass: " + targetClass); if (!TextUtils.isEmpty(packageName)) { //找到对应插件 PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(packageName); if (loadedApk != null && targetClass != null) { Activity activity = mHostInstr.newActivity(loadedApk.getPluginClassLoader(), targetClass, intent); activity.setIntent(intent); if (!dispatchToBaseActivity(activity)) { // 这里需要替换Resources,是因为ContextThemeWrapper会缓存一个Resource对象,而在Activity#attach()和 // Activity#onCreate()之间,系统会调用Activity#setTheme()初始化主题,Android 4.1+ //替换成插件的Resource资源 ReflectionUtils.on(activity).setNoException("mResources", loadedApk.getPluginResource()); } return activity; } else if (loadedApk == null) { // loadedApk 为空,可能是正在恢复进程,跳转到 RecoveryActivity return mHostInstr.newActivity(cl, mRecoveryHelper.selectRecoveryActivity(className), intent); } } } return mHostInstr.newActivity(cl, className, intent); } 复制代码
@Override public void callActivityOnCreate(Activity activity, Bundle icicle) { boolean isRecovery = activity instanceof TransRecoveryActivity0; if (isRecovery) { //插件加载中的Activity,使用宿主的Instrument mRecoveryHelper.saveIcicle(activity, icicle); mHostInstr.callActivityOnCreate(activity, null); return; } final Intent intent = activity.getIntent(); String[] result = IntentUtils.parsePkgAndClsFromIntent(intent); boolean isLaunchPlugin = false; if (IntentUtils.isIntentForPlugin(intent)) { String packageName = result[0]; String targetClass = result[1]; if (!TextUtils.isEmpty(packageName)) { PluginDebugLog.runtimeLog(TAG, "callActivityOnCreate: " + packageName); PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(packageName); if (loadedApk != null) { icicle = mRecoveryHelper.recoveryIcicle(activity, icicle); // 设置 extra 的 ClassLoader,不然可能会出现 BadParcelException, ClassNotFound if (icicle != null) { icicle.setClassLoader(loadedApk.getPluginClassLoader()); } if (!dispatchToBaseActivity(activity)) { // 如果分发给插件Activity的基类了,就不需要在这里反射hook替换相关成员变量了 try { ReflectionUtils activityRef = ReflectionUtils.on(activity); //设置为插件资源 activityRef.setNoException("mResources", loadedApk.getPluginResource()); //设置插件的Application activityRef.setNoException("mApplication", loadedApk.getPluginApplication()); Context pluginContext = new PluginContextWrapper(activity.getBaseContext(), packageName); //替换为PluginContextWrapper,处理inflate相关 ReflectionUtils.on(activity, ContextWrapper.class).set("mBase", pluginContext); // 5.0以下ContextThemeWrapper内会保存一个mBase,也需要反射替换掉 ReflectionUtils.on(activity, ContextThemeWrapper.class).setNoException("mBase", pluginContext); //替换为插件的Instrumentation ReflectionUtils.on(activity).setNoException("mInstrumentation", loadedApk.getPluginInstrument()); // 修改插件Activity的ActivityInfo, theme, window等信息 PluginActivityControl.changeActivityInfo(activity, targetClass, loadedApk); } catch (Exception e) { PluginDebugLog.runtimeLog(TAG, "callActivityOnCreate with exception: " + e.getMessage()); } } if (activity.getParent() == null) { //Activity栈的逻辑是怎么处理的? loadedApk.getActivityStackSupervisor().pushActivityToStack(activity); } isLaunchPlugin = true; } } IntentUtils.resetAction(intent); //恢复Action } try { mHostInstr.callActivityOnCreate(activity, icicle); if (isLaunchPlugin) { NotifyCenter.notifyPluginStarted(activity, intent); NotifyCenter.notifyPluginActivityLoaded(activity); } //check是否需要hook callActivityOnRestoreInstanceState方法 mRecoveryHelper.mockActivityOnRestoreInstanceStateIfNeed(this, activity); } catch (Exception e) { ErrorUtil.throwErrorIfNeed(e); if (isLaunchPlugin) { NotifyCenter.notifyStartPluginError(activity); } activity.finish(); } } 复制代码
@Override public void callActivityOnDestroy(Activity activity) { mHostInstr.callActivityOnDestroy(activity); if (activity.getParent() != null) { return; } final Intent intent = activity.getIntent(); String pkgName = IntentUtils.parsePkgNameFromActivity(activity); if (IntentUtils.isIntentForPlugin(intent) || intent == null) { // intent为null时,如果能够从Activity中解析出pkgName,也应该是插件的页面 if (!TextUtils.isEmpty(pkgName)) { PluginDebugLog.runtimeLog(TAG, "callActivityOnDestroy: " + pkgName); PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(pkgName); if (loadedApk != null) { //退出插件的Activity栈 loadedApk.getActivityStackSupervisor().popActivityFromStack(activity); } } } } 复制代码
@Override public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { if (activity instanceof TransRecoveryActivity0) { mRecoveryHelper.saveSavedInstanceState(activity, savedInstanceState); return; } if (IntentUtils.isIntentForPlugin(activity.getIntent())) { String pkgName = IntentUtils.parsePkgAndClsFromIntent(activity.getIntent())[0]; PluginLoadedApk loadedApk = PluginManager.getPluginLoadedApkByPkgName(pkgName); if (loadedApk != null && savedInstanceState != null) { //用插件的ClassLoader恢复数据 savedInstanceState.setClassLoader(loadedApk.getPluginClassLoader()); } } mHostInstr.callActivityOnRestoreInstanceState(activity, savedInstanceState); } 复制代码
插件Activity任务栈
/** * 插件的Activity栈抽象, 和系统的{@link com.android.server.am.ActivityStack}类似 */ public class PActivityStack { private final LinkedList<Activity> mActivities; // taskAffinity private String taskName; PActivityStack(String taskName) { this.taskName = taskName; mActivities = new LinkedList<>(); } /** * 获取当前任务栈的名称 */ public String getTaskName() { return taskName; } public LinkedList<Activity> getActivities() { return mActivities; } public int size() { return mActivities.size(); } public synchronized boolean isEmpty() { return mActivities.isEmpty(); } // 放入链表的前面 public synchronized void push(Activity activity) { mActivities.addFirst(activity); } public synchronized void insertFirst(Activity activity) { mActivities.addLast(activity); } public synchronized boolean pop(Activity activity) { return mActivities.remove(activity); } public synchronized Activity getTop() { return mActivities.getFirst(); } /** * 清空当前任务栈里的Activity */ public void clear(boolean needFinish) { Iterator<Activity> iterator = mActivities.iterator(); while (iterator.hasNext()) { Activity activity = iterator.next(); if (activity != null && needFinish && !FileUtils.isFinished(activity)) { activity.finish(); } iterator.remove(); } } } 复制代码
PActivityStackSupervisor
管理Activity任务栈
/** * 处理Activity的launchMode,给Intent添加相关的Flags */ public void dealLaunchMode(Intent intent) { if (null == intent) { return; } String targetActivity = IntentUtils.getTargetClass(intent); if (TextUtils.isEmpty(targetActivity)) { return; } PluginDebugLog.runtimeLog(TAG, "dealLaunchMode target activity: " + intent + " source: " + targetActivity); // 不支持LAUNCH_SINGLE_INSTANCE ActivityInfo info = mLoadedApk.getPluginPackageInfo().getActivityInfo(targetActivity); if (info == null || info.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { return; } boolean isSingleTop = info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP || (intent.getFlags() & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0; boolean isSingleTask = info.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK; boolean isClearTop = (intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0; PluginDebugLog.runtimeLog(TAG, "dealLaunchMode isSingleTop " + isSingleTop + " isSingleTask " + isSingleTask + " isClearTop " + isClearTop); int flag = intent.getFlags(); PluginDebugLog.runtimeLog(TAG, "before flag: " + Integer.toHexString(intent.getFlags())); if ((isSingleTop || isSingleTask) && (flag & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) { flag = flag ^ Intent.FLAG_ACTIVITY_SINGLE_TOP; } if ((isSingleTask || isClearTop) && (flag & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { flag = flag ^ Intent.FLAG_ACTIVITY_CLEAR_TOP; } intent.setFlags(flag); PluginDebugLog.runtimeLog(TAG, "after flag: " + Integer.toHexString(intent.getFlags())); if (isSingleTop && !isClearTop) { // 判断栈顶是否为需要启动的Activity, 只需要处理前台栈 Activity activity = null; if (!mFocusedStack.isEmpty()) { activity = mFocusedStack.getTop(); } boolean hasSameActivity = false; String proxyClsName = ComponentFinder.findActivityProxy(mLoadedApk, info); if (activity != null) { // 栈内有实例, 可能是ProxyActivity,也可能是插件真实的Activity //Fix: 新的实现中只有插件真实的Activity if (TextUtils.equals(proxyClsName, activity.getClass().getName()) || TextUtils.equals(targetActivity, activity.getClass().getName())) { String key = getActivityStackKey(activity); if (!TextUtils.isEmpty(key) && TextUtils.equals(targetActivity, key)) { intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); hasSameActivity = true; } } } if (hasSameActivity) { handleOtherPluginActivityStack(activity, mFocusedStack); } } else if (isSingleTask || isClearTop) { PActivityStack targetStack; // 需要搜索的任务栈 boolean fromBackStack = false; if (isClearTop) { targetStack = mFocusedStack; } else { // singleTask if (mLastFocusedStack != null && TextUtils.equals(mLastFocusedStack.getTaskName(), matchTaskName(info.taskAffinity))) { // 后台栈和Activity的taskAffinity匹配 targetStack = mLastFocusedStack; fromBackStack = true; PluginDebugLog.runtimeLog(TAG, "dealLaunchMode search in background stack: " + info.taskAffinity); } else { // 前台栈中搜索 targetStack = mFocusedStack; } } // 查找栈中是否存在已有实例 Activity found = null; // 遍历已经起过的activity for (Activity activity : targetStack.getActivities()) { String proxyClsName = ComponentFinder.findActivityProxy(mLoadedApk, info); if (activity != null) { if (TextUtils.equals(proxyClsName, activity.getClass().getName()) || TextUtils.equals(targetActivity, activity.getClass().getName())) { String key = getActivityStackKey(activity); if (!TextUtils.isEmpty(key) && TextUtils.equals(targetActivity, key)) { PluginDebugLog.runtimeLog(TAG, "dealLaunchMode found:" + IntentUtils.dump(activity)); found = activity; break; } } } } // 栈中已经有当前activity if (found != null) { // 处理其他插件的逻辑 // 在以这两种SingleTask, ClearTop flag启动情况下,在同一个栈的情况下 handleOtherPluginActivityStack(found, targetStack); // 处理当前插件的Activity List<Activity> popActivities = new ArrayList<Activity>(5); for (Activity activity : targetStack.getActivities()) { if (activity == found) { if (isSingleTask || isSingleTop) { PluginDebugLog.runtimeLog(TAG, "dealLaunchMode add single top flag!"); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } PluginDebugLog.runtimeLog(TAG, "dealLaunchMode add clear top flag!"); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); break; } popActivities.add(activity); } for (Activity act : popActivities) { PluginDebugLog.runtimeLog(TAG, "dealLaunchMode popActivities finish " + IntentUtils.dump(act)); popActivityFromStack(act); if (!FileUtils.isFinished(act)) { act.finish(); } } // 如果Activity是在后台堆栈中找到的,需要合并前后台栈 if (fromBackStack) { // https://developer.android.com/guide/components/activities/tasks-and-back-stack // 把返回栈中的Activity全部推到前台 PActivityStack sysForeStack = findAssociatedStack(mFocusedStack); PActivityStack sysBackStack = findAssociatedStack(mLastFocusedStack); mergeActivityStack(sysBackStack, sysForeStack); // 处理插件自身的栈 mergeActivityStack(mLastFocusedStack, mFocusedStack); // 切换前后台堆栈 switchToBackStack(mFocusedStack, mLastFocusedStack); } mLoadedApk.quitApp(false); } else { // 堆栈里没有找到,遍历还未启动cache中的activity记录 LinkedBlockingQueue<Intent> records = sIntentCacheMap .get(mLoadedApk.getPluginPackageName()); if (null != records) { Iterator<Intent> recordIterator = records.iterator(); String notLaunchTargetClassName = null; while (recordIterator.hasNext()) { Intent record = recordIterator.next(); if (null != record) { if (null != record.getComponent()) { notLaunchTargetClassName = record.getComponent().getClassName(); } if (TextUtils.equals(notLaunchTargetClassName, targetActivity)) { PluginDebugLog.runtimeLog(TAG, "sIntentCacheMap found: " + targetActivity); if (isSingleTask || isSingleTop) { intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); break; } } } } // 遍历启动过程中的activity记录 List<Intent> loadingIntents = sIntentLoadingMap.get(mLoadedApk.getPluginPackageName()); if (null != loadingIntents) { Iterator<Intent> loadingRecordIterator = loadingIntents.iterator(); String notLaunchTargetClassName = null; while (loadingRecordIterator.hasNext()) { Intent record = loadingRecordIterator.next(); if (null != record) { notLaunchTargetClassName = IntentUtils.getTargetClass(record); if (TextUtils.equals(notLaunchTargetClassName, targetActivity)) { PluginDebugLog.runtimeLog(TAG, "sIntentLoadingMap found: " + targetActivity); if (isSingleTask || isSingleTop) { intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); break; } } } } if (isSingleTask) { // 是否需要放到单独的任务栈 String taskName = matchTaskName(info.taskAffinity); if (!TextUtils.equals(mFocusedStack.getTaskName(), taskName)) { PluginDebugLog.runtimeLog(TAG, "dealLaunchMode push activity into separated stack: " + taskName); PActivityStack stack = mActivityStacks.get(taskName); if (stack == null) { // 创建一个新的任务栈 stack = new PActivityStack(taskName); mActivityStacks.put(taskName, stack); } // 切换前后台栈 switchToBackStack(mFocusedStack, stack); } else { PluginDebugLog.runtimeLog(TAG, "dealLaunchMode push activity into current stack: " + taskName); } } } } PluginDebugLog.runtimeLog(TAG, "dealLaunchMode end: " + intent + " " + targetActivity); } 复制代码
处理插件中的广播
/** * 动态注册插件中的静态Receiver */ private void installStaticReceiver() { if (mPluginPackageInfo == null || mHostContext == null) { return; } Map<String, PluginPackageInfo.ReceiverIntentInfo> mReceiverIntentInfos = mPluginPackageInfo.getReceiverIntentInfos(); if (mReceiverIntentInfos != null) { Set<Map.Entry<String, PluginPackageInfo.ReceiverIntentInfo>> mEntrys = mReceiverIntentInfos.entrySet(); Context mGlobalContext = mHostContext.getApplicationContext(); for (Map.Entry<String, PluginPackageInfo.ReceiverIntentInfo> mEntry : mEntrys) { PluginPackageInfo.ReceiverIntentInfo mReceiverInfo = mEntry.getValue(); if (mReceiverInfo != null) { try { BroadcastReceiver mReceiver = BroadcastReceiver.class.cast(mPluginClassLoader. loadClass(mReceiverInfo.mInfo.name).newInstance()); List<IntentFilter> mFilters = mReceiverInfo.mFilter; if (mFilters != null) { for (IntentFilter mItem : mFilters) { mGlobalContext.registerReceiver(mReceiver, mItem); } } } catch (Exception e) { e.printStackTrace(); } } } } } 复制代码
处理插件中的Service
PluginContextWrapper
类中完成了startService等方法的代理
@Override public ComponentName startService(Intent service) { PluginDebugLog.log(TAG, "startService: " + service); PluginLoadedApk mLoadedApk = getPluginLoadedApk(); if (mLoadedApk != null) { ComponentFinder.switchToServiceProxy(mLoadedApk, service); } return super.startService(service); } @Override public boolean stopService(Intent name) { PluginDebugLog.log(TAG, "stopService: " + name); PluginLoadedApk mLoadedApk = getPluginLoadedApk(); if (mLoadedApk != null) { String actServiceClsName = ""; if (name.getComponent() != null) { actServiceClsName = name.getComponent().getClassName(); } else { ServiceInfo mServiceInfo = getPluginPackageInfo().resolveService(name); if (mServiceInfo != null) { actServiceClsName = mServiceInfo.name; } } PluginServiceWrapper plugin = PServiceSupervisor .getServiceByIdentifer(PluginServiceWrapper.getIdentify(getPluginPackageName(), actServiceClsName)); if (plugin != null) { plugin.updateServiceState(PluginServiceWrapper.PLUGIN_SERVICE_STOPED); plugin.tryToDestroyService(); return true; } } return super.stopService(name); } @Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { PluginDebugLog.log(TAG, "bindService: " + service); PluginLoadedApk mLoadedApk = getPluginLoadedApk(); if (mLoadedApk != null) { ComponentFinder.switchToServiceProxy(mLoadedApk, service); } if (conn != null) { if (mLoadedApk != null && service != null) { String serviceClass = IntentUtils.getTargetClass(service); String packageName = mLoadedApk.getPluginPackageName(); if (!TextUtils.isEmpty(serviceClass) && !TextUtils.isEmpty(packageName)) { PServiceSupervisor.addServiceConnectionByIdentifer(packageName + "." + serviceClass, conn); } } } return super.bindService(service, conn, flags); } @Override public void unbindService(ServiceConnection conn) { super.unbindService(conn); PServiceSupervisor.removeServiceConnection(conn); PluginDebugLog.log(TAG, "unbindService: " + conn); } 复制代码
总结
Neptune
框架注释比较清晰。但是由于 Neptune
框架代码存在两套对Activity插件化的方案实现(版本迭代,一套老的,一套新的),】,导致代码逻辑不是很统一。在阅读代码的时候,把握住Hook思路,只看相关的代码,还是比较容易理解的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Aura插件化框架演进以及思考
- 如何创建与框架无关的 JavaScript 插件
- 如何创建与框架无关的 JavaScript 插件
- Android 插件化框架 DynamicLoadApk 源码分析
- 从插件入手:挖掘流行框架的“后入式BUG”
- ExtAnalysis:一款浏览器插件安全分析框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。