Android插件化
最经换了工作,公司的项目比较庞大,很多地方都运用了插件化,插件化说简单就是把部分功能进行打包成专门的apk、dex等文件,当宿主app需要用到此功能的时候才去加载插件;插件不仅可以实现一些功能的热插拔;以及不需要去安装app,只是在使用到的情况下再去下载,这样就减小宿主的apk的体积;还可以去通过更新插件来完成功能的更新。插件化技术已经比较成熟了,很多大公司的产品也都是使用插件化开发,也有很多比较成熟的插件化框架,例如DynamicAPK、RePlugin 、Small等等
插件化的原理
android里面有PathClassLoader以及DexClassLoader:
- PathClassLoader用于加载data/app下的dex、apk、class文件,这个目录就对应了我们安装的一些应用
- DexClassLoader可以用来加载外部的一些dex、apk、class文件
我们可以加载其他地方的dex、apk文件了,并使用相应的class文件
我们还需要获取资源文件
//创建AssetManager对象 AssetManager assets = new AssetManager(); //将apk路径添加到AssetManager中 if (assets.addAssetPath(resDir) == 0){ return null; } //创建Resource对象r = new Resources(assets, metrics, getConfiguration(), compInfo);
只要将插件apk的路径加入到AssetManager中,便能够实现对插件资源的访问。
具体实现时,由于AssetManager并不是一个public的类,需要通过反射去创建,并且部分Rom对创建的Resource类进行了修改,所以需要考虑不同Rom的兼容性。
还有一个问题就是插件的activity没有进行注册,我们在宿主中注册一个空的Activity,专门用来加载插件app中的activity,这个Activity叫ProxyActivity。我们还需要配置一下ProxyActivity
class ProxyActivity : AppCompatActivity() { /** * 要跳转的activity的name */ private var className = "" private var appInterface: AppInterface? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) /** * step1:得到插件app的activity的className */ className = intent.getStringExtra("className") /** * step2:通过反射拿到class, * 但不能用以下方式 * classLoader.loadClass(className) * Class.forName(className) * 因为插件app没有被安装! * 这里我们调用我们重写过多classLoader */ var activityClass = classLoader.loadClass(className) var constructor = activityClass.getConstructor() var instance = constructor.newInstance() appInterface = instance as?AppInterface appInterface?.attach(this) var bundle = Bundle() appInterface?.onCreate(bundle) } override fun onStart() { super.onStart() appInterface?.onStart() } override fun onResume() { super.onResume() appInterface?.onResume() } override fun onDestroy() { super.onDestroy() appInterface?.onDestroy() } override fun getClassLoader(): ClassLoader { //不用系统的ClassLoader,用dexClassLoader加载 return PluginManager.getInstance().getDexClassLoader() as? ClassLoader ?: super.getClassLoader() } override fun getResources(): Resources { //不用系统的resources,自己实现一个resources return PluginManager.getInstance().getResources() ?: super.getResources() }}
还有在使用context的时候,需要使用ProxyActivity的context,因为插件没有上下文,需要依赖宿主的上下文
Android热更新
热修复也是比较热门的技术,热修复的框架也是有很多,阿里AndFix(native解决方案,不需要冷启动)、QQ空间(Dex分包方案)、美团Robust (Instant Run 热插拔原理)、微信Tinker,这些框架的原理各有不同。tinker就是通过ClassLoader来实现热修复的原理。
创作不易喜欢的话记得点击+关注哦