赞
踩
记录一下,这是2020年第一篇帖子,今年立了一个flag–经常写帖子。因为疫情的原因,只能每天在家养肚皮,躺床上为社会做贡献。实在是坐不住了,就开始写这篇文章吧。希望新的一年自己越来越厉害,也希望疫情早点过去
插件化技术最初源于免安装运行apk的想法,这个免安装的apk就可以理解为插件,而支持插件的app我们一般称之为宿主
1.app功能模块越来越多,体积越来越大,维护变得困难;
2.模块之间的耦合度高,协同开发沟通成本越来越高;
3.方法数目可能超过65535,APP占用的内存越来越大;
4.增加功能只敢做加法。
各大平台方案对比
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
不再一一介绍原理,本次主题是基于 360的DroidPlugin
接下来是插件化的核心:
- 如何加载插件的类?
- 如何加载插件的资源?
- 如何调用插件的类(四大组件)?
毫无疑问,类是通过类加载器classLoader加载的,我们先看几种类加载器.
首先我们来看DexClassLoader:
DexClassLoader 用于加载.jar .apk .dex中的类,也可以用来加载不是应用程序中的代码
api-25-7.1 -->
api-26-8.0 -->
api-28-9.0 -->
可以发现,DexClassLoader里什么也没有做,只是重写了BaseDexClassLoader的构造函数,注意api28里面的这段标红注释:该参数在api 26之后就废弃了,后面会讲到。
再看BaseDexClassLoader:
api-25-7.1 -->
api-26-8.0 & api-28-9.0 -->
我们把焦点放在optimizedDirectory上:应该被写入的dex所在的目录。通过构造三个版本的函数可以看出:8.0之前使用了optimizedDirectory参数,8.0及8.0之后不再使用该参数。
我们再移步看一下PathClassLoader:PathClassLoader是系统类和系统应用的类加载器:
PathClassLoader8.0前后并没有什么区别,同样重写了四个参数的构造函数,optimizedDirectory为null。
总结:
- 8.0之前,DexClassLoader供系统(谷歌工程师)使用,PathClassLoader提供给开发者使用;
- 8.0及之后并无区别。后来谷歌工程师越来越觉得写两个加载器简直是闲得蛋疼,干脆就不再区分了。
区别: 在8.0之后并无区别,
/** *注意这个类派生自AppCompatActivity,而非Activity */ public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findClassLoader(); } private void findClassLoader() { ClassLoader classLoader = getClassLoader(); while (classLoader !=null){ Log.d(TAG, "findClassLoader: classLoader : "+classLoader); classLoader = classLoader.getParent(); } Log.d(TAG, "findClassLoader: classLoader : "+ Activity.class.getClassLoader()); } }
让我的小助理运行一下代码,小手那么一点,运行结果如下所示:
通过代码,我们可以得知:
MainActivity 的classLoader是 PathClassLoader;Activity 的classLoader是BootClassLoader; PathClassLoader的parent是BootClassLoader。
其实他们两个的作用
- BootClassLoader – 加载FramWork的class文件;
- PathClassLoader – 加载应用内的class文件,包含implementation到的第三方库以及.jar、.aar等。
这里我们重点了解一下类加载阶段主要做的事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流;
- 将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
- 在java堆中生成一个代表这个类的Class对象,作为方法区域数据的访问入口。
其它阶段不是我们本章的重点,这里不再赘述,可参考Java类的生命周期。
注:演示代码是基于Android API 9.0的
注:演示代码是基于Android API 9.0的
注:演示代码是基于Android API 9.0的
重要的话说三遍
首先通过代码演示如何加载一个类来请出一个传说中的大“人物”——双亲委托机制:
String dexPath = "";
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, this.getCacheDir().getAbsolutePath(),
null, getClassLoader());
Class<?> clazz = dexClassLoader.loadClass("com.margin.plugin.ProxyActivity");
这样,一个类(插件中的类)就被加载成功了。那么,类究竟是如何加载的呢,请小助理位于ClassLoader中的loadClass把源码拿过来:
首先通过findLoadedClass检查是否已经加载过了;如果没有加载过,检查是否存在parent(注意这个parent父母即双亲,并不是指父类,而是上一级),如果双亲存在则调用parent的loadClass()方法,依次递归;
清楚此流程了,那么一个几乎所有小孩子都问过的问题来了——我是从哪里来的?不不不,是parent从哪里来的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。