从启动一个未安装Apk的页面入门插件化开发
来源:互联网 发布:马云会编程吗 编辑:程序博客网 时间:2024/05/29 11:14
什么是插件化开发
一个Android应用在开发到了一定阶段以后,功能模块将会越来越多,APK安装包也越来越大,用户在使用过程中也没有办法选择性的加载自己需要的功能模块。此时可能就需要考虑如何分拆整个应用了。
举个栗子,好比小时候玩的小霸王游戏机,你刚买回家的只是一个插卡带的机器,就好比最初下载的apk,当你发现你想玩某一款游戏的时候,就去买专门的卡带插进去即可,而不是非得把游戏一下子全买下来.
插件化优点:
- 应用程序非常容易扩展,比如一个新的领域要加到旧的应用程序中,只需把这个新的领域做为一个插件,只开发这个小的app就可以了,旧的应用程序可能会原分不动,就连编译打包都不需要。
- 解除单个dex函数不能超过 65535的限制
- 模块升级,而不需要将 整个功能全部下载安装
- 高效开发(编译速度更快)
- 用户可以根据需要下载模块,减小初次安装的apk体积
插件化的两种方式
1.安装式:通过网络下载插件apk,安装(这样似乎和重新装了一个apk没啥区别感觉),中间可以通过一个webview 动态更新,便于加载新的模块.通过js调用intent启动即可,因为是安装的apk,系统会为apk注入上下文,运行比较方便
2.不安装式:因为apk没有安装,在宿主apk中,无法加载安装包中的资源文件,类文件,注入content,系统也不会为apk管理生命周期,需要通过宿主apk协助加载,本文重点讨论这种方式
话不多说,切入正题……
可是怎么启动一个未安装的apk页面呢??
先po出用于展示插件页面的代理activity吧(当然是在宿主app里的) ,先简单说说里面的几个关键的东西是干啥的,一会在细说(提前抛出一个坑,所有的关于context 的东西,必须重写,因为系统没有入住context,需要调用我们宿主的context)
ProxyInterface 是一个lib 中的接口,用于将插件页面的生命周期关联到宿主activity,其中多一个attach(context) 用于模仿系统为apk注入上下文
PluginManager.getInstance().dexClassLoader 用于根据插件apk路径构造新的类加载器
PluginManager.getInstance().resources 用于根据插件apk路径加载新的Resources对象
通过传入的类名,加上自定义的加载器,这样我们就能通过classloader.loadclass加载未安装的apk中的类啦~~,但是activity 只加载类还是不够滴,activity 需要通过反射拿到构造函数构建,并且,因为不是系统打开的页面,没有context上下文,需要重写getwindow ,getDecoreview等方法,用我们宿主传入的context 为activity获得构造需要的东西, 这样我们的activity差不多已经是8个月大的baby啦,已经具备了展示自己的能力,在关联生命周期之后,就可以展示出来啦……
private String className; private ProxyInterface realActivity; @Override public void startActivity(Intent intent) { Intent in = new Intent(this,ProxyActivity.class); String className = intent.getStringExtra("className"); in.putExtra("className", className); super.startActivity(in); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); className = getIntent().getStringExtra("className"); //这种不能获得,因为类在包外面// getClassLoader().loadClass(className) try { //activity 构造私有,反射构建 Class loadClass = getClassLoader().loadClass(className); Constructor constructor = loadClass.getConstructor(new Class[]{}); realActivity = ((ProxyInterface) constructor.newInstance(new Object[]{})); realActivity.onAttach(this); /** * 定义标准 * 传递生命周期 */ Bundle bundle = new Bundle(); realActivity.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onStart() { super.onStart(); realActivity.onStart(); } @Override protected void onResume() { super.onPause(); realActivity.onResume(); } @Override protected void onStop() { super.onStop(); realActivity.onStop(); } @Override protected void onDestroy() { super.onDestroy(); realActivity.onDestroy(); } @Override protected void onPause() { super.onPause(); realActivity.onPause(); } @Override public ClassLoader getClassLoader() { return PluginManager.getInstance().dexClassLoader; } @Override public Resources getResources() { return PluginManager.getInstance().resources; }}
大概…需要三个东西吧
1.重写类加载器
在插件apk中,系统并不认为你是一个应用程序,所以不会为你分配虚拟机,木有虚拟机,就不会有类加载机制,那么所有的类,在宿主app中是不可以通过反射或者etClassLoader().loadClass(“类名”)去加载的.
那..这就有点尴尬了,好在…可以通过重写类加载器去加载文件里的类
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE); dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath(), null, context.getClassLoader());
DexClassLoader 构造 加载器一共四个参数,所需条件并不是很难获得
1. dexPath 加载dex文件的绝对路径,dex在安装包内,就传插件安装包路径就行
2. optimizedDirectory 加载优化路径
3. 引用类库路径
4. 父加载器
2.重新资源Resource对象
//构建新包的资源加载器 //对于AssetManager因为构造方法添加了Hide注解,所以必须通过反射获取 try { AssetManager assetManager = AssetManager.class.newInstance(); Method method = AssetManager.class.getMethod("addAssetPath", String.class); method.invoke(assetManager,path); resources = new Resources(assetManager ,context.getResources().getDisplayMetrics(), context.getResources().getConfiguration()); } catch (Exception e) { e.printStackTrace(); }
3.最后一步…..
从宿主的mainActivity中跳转到 代理ProxyActivity中,并且传入要加载的插件apk中启动页面的类名,完事…..
4.关于插件apk中页面的跳转
是不能够通过startActivity调用的,原因..还是因为context 为null ,跳转需要将目标页面的类名再次传入ProxyActivity ,也就是每次启动插件页面,其实都是开启一个ProxyActivity
最后贴个github地址 :https://github.com/fushuangdage/ProxyDemo
- 从启动一个未安装Apk的页面入门插件化开发
- Android插件化开发---运行未安装apk中的Service
- Android插件化开发---运行未安装apk中的Service
- Android插件化开发,运行未安装apk中的Service
- 插件化开发—动态加载技术加载已安装和未安装的apk
- 插件化开发—动态加载技术加载已安装和未安装的apk
- 插件化开发—动态加载技术加载已安装和未安装的apk
- 插件化开发—动态加载技术加载已安装和未安装的apk
- 插件化开发—动态加载技术加载已安装和未安装的apk
- Android插件化开发之运行未安装apk的activity
- 插件化加载未安装APK
- 插件化开发系列之二—动态加载技术加载已安装和未安装的apk
- Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤
- 【Android】从当前Apk启动另外一个已经安装的Apk
- android apk嵌套,从一个apk启动另外一个apk,在代码中安装apk
- Android 通过反射启动未安装的APK中的Activity
- Android 插件化技术 加载任意未安装apk
- Android 插件化技术 加载任意未安装apk
- 策略模式+观察者模式+装饰者模式
- MongoDb 入门教程
- 理解LabVIEW的执行系统即线程的切换
- 如何在本地调试好phpcms v9网站再转移到服务器上
- ansj 分词
- 从启动一个未安装Apk的页面入门插件化开发
- python中set和frozenset方法和区别
- 用链表实现冒泡排序!
- vmware 下安装的linux误删bin目录恢复成功(全过程)避开各种坑
- mysql中条件限制语句(二)
- LeetCode
- springboot学习----自动配置
- DreamWeaver下如何应用CSS样式
- 关于JS堆栈与拷贝