我是靠谱客的博主 魁梧航空,这篇文章主要介绍插件化之Dynamicloadapk分析,现在分享给大家,希望可以做个参考。

在进行插件化了解之前,需要先了解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 去实现,

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的入口,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@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; }
复制代码
1
// put extra data
复制代码
1
2
3
4
5
6
dlIntent.putExtra(DLConstants.EXTRA_CLASS, className); dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName); dlIntent.setClass(mContext, activityClass); performStartActivityForResult(context, dlIntent, requestCode); return START_RESULT_SUCCESS; }
复制代码
1

接下来我们分析一下这个函数:

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,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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(); } }
复制代码
1

分析一下这个函数,首先加载target activity实例,然后将pluginActivity和proxyactivity  attatch, 在proxyActivity中的mRemoteActivity就是PluginActivity,最后调用PluginActivity.onCreate(),其他生命周期的代理也是如此,在proxyActivity中通过调用remoteActivity(这个就是pluginactivity)的生命周期。如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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中调用的

复制代码
1
mPluginActivity.attach(mProxyActivity, mPluginPackage);
复制代码
1

看一下DlBasePluginActivity的attach函数实现

复制代码
1
2
3
4
5
6
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来承载它的展示。 如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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分析内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部