Adroid动态加载Apk-插件化技术框架(动态代理方案)
概述
详细
Android动态加载Apk-插件化技术(动态代理方案)
一.概述
为什么要使用插件化?在开发中,一个项目只会越做越大。初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能。所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位。但是也会带来问题:插件和插件之间的交互的复杂性更高、底层支持库因为多个插件需要使用相同的代码可能会变得很大。所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高。
要想实现插件化,最快的方法就是找一个第三方框架接入。但是要想真正理解,需要真正自己写一个.下面本文就带大家写一个动态加载插件化的框架
二. 什么是插件化
1. 主App(宿主App)加载插件apk的实现
2. 每个业务组件模块形成一个独立的Apk, 然后通过主App动态加载部署业务组件模块Apk的一种方案
三.效果演示图&应用场景
1.效果演示图:
2.实际开发中,比如微信和支付宝的如下页面就是典型的插件化应用场景
三.插件化的优点好处
1. 业务组件解耦,能够实现业务组件模块的热插拔
2. 更改产品迭代模式,可分为主App和次Apk(动态加载业务组件模块)
3. 改善产品更新过程,可以在不影响用户的情况下实现业务组件模块更新以及重要Bug修复
4. 减轻主App的内存和CPU占用,提高应用的性能.
四.插件化的思想
动态加载Apk的主要思想是:主App是被系统(PMS)安装,被系统(AMS)调用,整个过程都是由系统提供的,而插件Apk并非一个真正的Apk,只是一个打包成Apk的一个组件模块,因为它并非被系统安装调用.简言之,需要讲插件Apk看成一个”非Apk”文件,只是一个结构比较复杂的压缩打包成Apk格式的文件.调用插件即用某种特殊技术手段打开文件并执行其相关代码.
五.插件化的步骤-分析主App
1.主APp打包完成解压后,会有dex,images,xml,asset等类型文件
2.Dex靠PathClassLoader加载运行
3.图片以及xml等资源依靠Resources&AssetManager加载管理
六.插件化的实现流程
六. 插件化的代码实现步骤
1.创建DexClassLoader加载插件化Apk相关代码,核心代码如下:
/**' * 创建DexClassLoader */ private DexClassLoader createDexClassLoader(String apkPath) { File file = mContext.getDir("dex",Context.MODE_PRIVATE); return new DexClassLoader(apkPath,file.getAbsolutePath(),null,mContext.getClassLoader()); }
2.创建Resources&AssetManager来加载插件化Apk的资源
/** * 获取到插件中的Resource */ private Resources createResources(AssetManager am) { Resources resources = mContext.getResources(); return new Resources(am,resources.getDisplayMetrics(),resources.getConfiguration()); } /** * 获取插件的AssetManager */ private AssetManager createAssetManager(String apkPath) { try { AssetManager am = AssetManager.class.newInstance(); Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class); method.invoke(am,apkPath); return am; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; }
3.管理插件Apk里的组件(如Activity)的生命周期
package com.czm.pluginlib; import android.app.Activity; import android.content.Intent; import android.os.Bundle; /** * Created by caizhiming on 2018/3/3. */ public interface IPlugin { int FROM_INTERNAL = 0;//内部跳转 int FROM_EXTERNAL = 1;//外部跳转 void attach(Activity activity); void onCreate(Bundle bundle); void onStart(); void onRestart(); void onActivityResult(int requestCode, int resultCode, Intent data); void onResume(); void onPause(); void onStop(); void onDestroy(); }
4.通过代理模式实现对插件Apk里面组件的管理
@Override protected void onCreate( Bundle savedInstanceState) { super.onCreate(savedInstanceState); mClassName = getIntent().getStringExtra("className"); mPluginApk = XCPluginManager.getInstance().getPluginApk(); launchPluginActivity(); } private void launchPluginActivity() { if(mPluginApk == null){ throw new RuntimeException("请先加载插件Apk"); } try { //clazz 就是Activity的实例对象,但是该对象没有生命周期,没有上下文环境 Class<?> clazz = mPluginApk.mDexClassLoader.loadClass(mClassName); Object object = clazz.newInstance(); if(object instanceof IPlugin) { mIPlugin = (IPlugin) object; mIPlugin.attach(this); Bundle bundle = new Bundle(); bundle.putInt("FROM",IPlugin.FROM_EXTERNAL); mIPlugin.onCreate(bundle); } } catch (Exception e) { e.printStackTrace(); } } @Override public Resources getResources() { if(mPluginApk != null) { return mPluginApk.mResources; } else { return super.getResources(); } } @Override public AssetManager getAssets() { if(mPluginApk != null) { return mPluginApk.mAssetManager; }else { return super.getAssets(); } } @Override public ClassLoader getClassLoader() { if(mPluginApk != null) { return mPluginApk.mDexClassLoader; }else { return super.getClassLoader(); } }
以上就是实现插件化的主要过程步骤,具体细节优化读者可以自己扩展优化补充.
七.项目代码目录结构图