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相关代码,核心代码如下:
1 2 3 4 5 6 7 | /**' * 创建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的资源
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 | /** * 获取到插件中的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)的生命周期
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 | 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里面组件的管理
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | @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(); } } |
以上就是实现插件化的主要过程步骤,具体细节优化读者可以自己扩展优化补充.
七.项目代码目录结构图
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架