概述
在进行插件化了解之前,需要先了解ClassLoader的相关概念
大家应该都知道java中的类加载机制,采用的是双亲委派机制,即链式调用高层次的classLoader即 parentClassLoader去加载,
如果高层次的classLoader无法加载该类,再由当前的ClassLoader去加载。
在android中,插件化技术相关的classLoader分别是PathClassLoader 以及 DexClassLoader, 前者只能加载当前已安装的apk的class,后者用来加载插件apk的class.
接下来我们来分析一下DynamicLoadApk的设计和实现
先了解这个框架中的一些角色:
DlPluginManger: 用于loadApk以及启动Activity ,Service
DlPlugin : interface定义了activity相关同名接口,所有的pluginActivity均需要implement it
DlProxyActivity:作为 PluginActivity的代理类,用于承载界面展示, 生命周期的控制,activity 的调用,如setContentView,finish
DlPluginBaseActivity: 它就是一个PluginActivity,用于提供界面展示的布局,以及相关逻辑控制
DlProxyImpl: 他是DlProxyActivity和DlPluginBaseActivity建立通讯的桥梁,通过两个attach,让DlProxyActivity和DlPluginBaseActivity分别持用对方的引用,实现通讯
APK 角色划分: 宿主apk(已安装在手机上的) 和 插件apk(没有被安装)
由于宿主apk 已被安装,下面我们先来分析一下插件apk的加载过程,这个过程通过DlPluginManager的loadapk-->preparePluginEnv 去实现,
private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) { DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName); if (pluginPackage != null) { return pluginPackage ; } DexClassLoader dexClassLoader = createDexClassLoader(dexPath); AssetManager assetManager = createAssetManager(dexPath); Resources resources = createResources(assetManager); // create pluginPackage pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo); mPackagesHolder.put(packageInfo.packageName, pluginPackage); return pluginPackage; }
从上面代码中可知:准备工作主要做了几件事:
1 判断是否加载过这个插件,没有加载则继续向下执行,
2 做了三步初始化:
1) 创建dexClassLoader, 用于加载插件apk中的java类,
2)通过反射调用assetManager的addAssetPath,将插件apk的资源路径加载到宿主apk的资源池中
3)用当前的assetsmanager 创建resource,用于访问资源
到此 插件apk的初始化完成。接下来,我们来分析一下 从宿主apk中启动插件apk的activity
DlpluginManager的startPluginActivityForResult() 这个方法是启动插件Apk中Activity的入口,代码如下:
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode) { if (mFrom == DLConstants.FROM_INTERNAL) { dlIntent.setClassName(context, dlIntent.getPluginClass()); performStartActivityForResult(context, dlIntent, requestCode); return DLPluginManager.START_RESULT_SUCCESS; } String packageName = dlIntent.getPluginPackage(); if (TextUtils.isEmpty(packageName)) { throw new NullPointerException("disallow null packageName."); } DLPluginPackage pluginPackage = mPackagesHolder.get(packageName); if (pluginPackage == null) { return START_RESULT_NO_PKG; } final String className = getPluginActivityFullPath(dlIntent, pluginPackage); Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className); if (clazz == null) { return START_RESULT_NO_CLASS; } // get the proxy activity class, the proxy activity will launch the // plugin activity. Class<? extends Activity> activityClass = getProxyActivityClass(clazz); if (activityClass == null) { return START_RESULT_TYPE_ERROR; }
// put extra data
dlIntent.putExtra(DLConstants.EXTRA_CLASS, className); dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName); dlIntent.setClass(mContext, activityClass); performStartActivityForResult(context, dlIntent, requestCode); return START_RESULT_SUCCESS; }
接下来我们分析一下这个函数:
if (mFrom == FROM_EXTERNAL) 代表被启动的target Activity在插件apk中,否则 target Activity在宿主apk中。
开始的判断主要是为了判断target activity所在的包名是否有效, 然后通过初始化时的DexClassLoader去加载target Activity,
然后获取target Activity的proxyActivity, DLProxyActivity 为普通Activity的代理类,DLProxyFragmentActivity为fragmentActivity的代理类,然后启动proxyActivity,并将targetActivity的实例传递给proxyActivity
从而我们得出一个结论,所有的被启动的插件apk的activity 都被宿主的proxyactivity 代理,通过proxyActivity 来显示targetActivity并控制其生命周期
proxyActivity 与 targetActivity的通信桥梁是DlProxyImpl,查看DLProxyActivity代码可知,它重写了大量的Activity的函数,本身不做任何事情,完全交给 ProxyImpl 和 RemoteActivity去实现。
先看ProxyActivity的onCreate,它直接调用了DLProxyImpl的onCreate,代码如下:
public void onCreate(Intent intent) { // set the extra's class loader intent.setExtrasClassLoader(DLConfigs.sPluginClassloader); mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE); mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS); Log.d(TAG, "mClass=" + mClass + " mPackageName=" + mPackageName); mPluginManager = DLPluginManager.getInstance(mProxyActivity); mPluginPackage = mPluginManager.getPackage(mPackageName); mAssetManager = mPluginPackage.assetManager; mResources = mPluginPackage.resources; initializeActivityInfo(); handleActivityInfo(); launchTargetActivity(); }
前面是获取插件apk的一些基本信息,并设置theme,最后启动target activity
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) protected void launchTargetActivity() { try { Class<?> localClass = getClassLoader().loadClass(mClass); Constructor<?> localConstructor = localClass.getConstructor(new Class[] {}); Object instance = localConstructor.newInstance(new Object[] {}); mPluginActivity = (DLPlugin) instance; ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager); Log.d(TAG, "instance = " + instance); // attach the proxy activity and plugin package to the mPluginActivity mPluginActivity.attach(mProxyActivity, mPluginPackage); Bundle bundle = new Bundle(); bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL); mPluginActivity.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } }
分析一下这个函数,首先加载target activity实例,然后将pluginActivity和proxyactivity attatch, 在proxyActivity中的mRemoteActivity就是PluginActivity,最后调用PluginActivity.onCreate(),其他生命周期的代理也是如此,在proxyActivity中通过调用remoteActivity(这个就是pluginactivity)的生命周期。如下所示:
@Override protected void onResume() { mRemoteActivity.onResume(); super.onResume(); } @Override protected void onPause() { mRemoteActivity.onPause(); super.onPause(); } @Override protected void onStop() { mRemoteActivity.onStop(); super.onStop(); } @Override protected void onDestroy() { mRemoteActivity.onDestroy(); super.onDestroy(); }
接下来我们来分析PluginActivity,这里有DlPlugin接口,定义了Activity相关的大部分生命周期,所有的pluginActivity都实现了这个接口,以DlBasePluginActivity为例。
DlBasePluginActivity第一个被调用的函数是attach,在上面的 DlProxyImpl的oncreate中调用的
mPluginActivity.attach(mProxyActivity, mPluginPackage);
看一下DlBasePluginActivity的attach函数实现
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) { Log.d(TAG, "attach: proxyActivity= " + proxyActivity); mProxyActivity = (Activity) proxyActivity; that = mProxyActivity; mPluginPackage = pluginPackage; }
这里主要是获取了proxyactivity的对象that, 因为pluginActivity如果是插件apk中的activity,它没有被安装,本身不具备activity的特性, 只能通过ProxyActivity展示,所以更多的看来,当PluginActivity在插件apk中,其作用更多是提供数据(界面布局),逻辑控制等,ProxyActivity来承载它的展示。 如下所示:
@Override public void setContentView(View view) { if (mFrom == DLConstants.FROM_INTERNAL) { super.setContentView(view); } else { mProxyActivity.setContentView(view); } } @Override public void setContentView(View view, LayoutParams params) { if (mFrom == DLConstants.FROM_INTERNAL) { super.setContentView(view, params); } else { mProxyActivity.setContentView(view, params); } }
当pluginActivity在插件apk中即(mFrom != DlConstants.From_INTERNAL), 通过调用ProxyActivity的setContentView才能加载布局,本身不能加载。
区分this 和 that, 当pluginActivity在插件中,that 代表了 ProxyActivity, 当其在宿主apk中,that == this
总结: DynamicLoadApk这个开源库的实现是采用代理的方式
缺点: 不支持插件activity的launchmode
此博文中可能存在很多缺点和漏洞,希望大家积极的提出,一起进步
最后
以上就是魁梧航空为你收集整理的插件化之Dynamicloadapk分析的全部内容,希望文章能够帮你解决插件化之Dynamicloadapk分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复