当前位置:   article > 正文

Android插件化原理(一)—— 插件类加载、类加载原理、(双亲委托机制)

android插件化原理

记录一下,这是2020年第一篇帖子,今年立了一个flag–经常写帖子。因为疫情的原因,只能每天在家养肚皮,躺床上为社会做贡献。实在是坐不住了,就开始写这篇文章吧。希望新的一年自己越来越厉害,也希望疫情早点过去


(一)什么是插件化

插件化技术最初源于免安装运行apk的想法,这个免安装的apk就可以理解为插件,而支持插件的app我们一般称之为宿主

(二)为什么要插件化

1.app功能模块越来越多,体积越来越大,维护变得困难;
2.模块之间的耦合度高,协同开发沟通成本越来越高;
3.方法数目可能超过65535,APP占用的内存越来越大;
4.增加功能只敢做加法。

(三)插件化和模块(模块)化的爱恨纠葛

  • 组件化开发主要是通过gradle配置,将一个app分成多个模块,每个模块都是一个组件,开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件同意合并成一个apk,这就是组件化开发。
  • 插件化和组件化略有不同,插件化开发是将整个apk拆分成多个模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk,最终打包的时候宿主apk和插件apk分开打包。
  • 插件化其实是建立在模块化基础上的,插件化在实施之前需要先进性模块化。

(四)核心-插件化原理(方案之一)

各大平台方案对比
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
各大平台插件化方案对比
不再一一介绍原理,本次主题是基于 360的DroidPlugin

接下来是插件化的核心:

  • 如何加载插件的类?
  • 如何加载插件的资源?
  • 如何调用插件的类(四大组件)?

一. 如何加载插件的类

1. 类加载器

毫无疑问,类是通过类加载器classLoader加载的,我们先看几种类加载器.

1.1 PathClassLoader和DexClassLoader区别

首先我们来看DexClassLoader:

DexClassLoader 用于加载.jar .apk .dex中的类,也可以用来加载不是应用程序中的代码

api-25-7.1 -->
DexClassLoader-api-25

api-26-8.0 -->

DexClassLoader-api-26

api-28-9.0 -->

DexClassLoader-api-28

可以发现,DexClassLoader里什么也没有做,只是重写了BaseDexClassLoader的构造函数,注意api28里面的这段标红注释:该参数在api 26之后就废弃了,后面会讲到。

再看BaseDexClassLoader

api-25-7.1 -->
在这里插入图片描述

api-26-8.0 & api-28-9.0 -->
BaseDexClassLoader-api26

我们把焦点放在optimizedDirectory上:应该被写入的dex所在的目录。通过构造三个版本的函数可以看出:8.0之前使用了optimizedDirectory参数,8.0及8.0之后不再使用该参数

我们再移步看一下PathClassLoader:PathClassLoader是系统类和系统应用的类加载器:
PathClassLoader
PathClassLoader8.0前后并没有什么区别,同样重写了四个参数的构造函数,optimizedDirectory为null。

总结:

  • 8.0之前,DexClassLoader供系统(谷歌工程师)使用,PathClassLoader提供给开发者使用;
  • 8.0及之后并无区别。后来谷歌工程师越来越觉得写两个加载器简直是闲得蛋疼,干脆就不再区分了。

区别: 在8.0之后并无区别,

1.2 BootClassLoader 和PathClassLoader
/**
*注意这个类派生自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());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

让我的小助理运行一下代码,小手那么一点,运行结果如下所示:

在这里插入图片描述

通过代码,我们可以得知:
MainActivity 的classLoader是 PathClassLoader;Activity 的classLoader是BootClassLoader; PathClassLoader的parent是BootClassLoader。

其实他们两个的作用

  • BootClassLoader – 加载FramWork的class文件;
  • PathClassLoader – 加载应用内的class文件,包含implementation到的第三方库以及.jar、.aar等。

2. 类的生命周期

在这里插入图片描述

这里我们重点了解一下类加载阶段主要做的事情:

  • 通过一个类的全限定名来获取定义此类的二进制字节流;
  • 将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
  • 在java堆中生成一个代表这个类的Class对象,作为方法区域数据的访问入口。

其它阶段不是我们本章的重点,这里不再赘述,可参考Java类的生命周期


3 加载一个类

注:演示代码是基于Android API 9.0的
注:演示代码是基于Android API 9.0的
注:演示代码是基于Android API 9.0的

重要的话说三遍

3.1 「双亲委托」

首先通过代码演示如何加载一个类来请出一个传说中的大“人物”——双亲委托机制

       String dexPath = "";
       DexClassLoader dexClassLoader = new DexClassLoader(dexPath, this.getCacheDir().getAbsolutePath(),
                    null, getClassLoader());
       Class<?> clazz = dexClassLoader.loadClass("com.margin.plugin.ProxyActivity");
  • 1
  • 2
  • 3
  • 4

这样,一个类(插件中的类)就被加载成功了。那么,类究竟是如何加载的呢,请小助理位于ClassLoader中的loadClass把源码拿过来:

  • loadClass-1 :
    在这里插入图片描述

首先通过findLoadedClass检查是否已经加载过了;如果没有加载过,检查是否存在parent(注意这个parent父母即双亲,并不是指父类,而是上一级),如果双亲存在则调用parent的loadClass()方法,依次递归;

清楚此流程了,那么一个几乎所有小孩子都问过的问题来了——我是从哪里来的?不不不,是parent从哪里来的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/861116
推荐阅读
相关标签