Android 插件化基础知识
[TOC]
一 .概述
插件化技术听起来高深莫测,实际上要解决的就是两个问题:
- 代码加载
- 资源加载
代码加载
类的加载可以使用Java的ClassLoader
机制,但是对于Android来说,并不是说类加载进来就可以用了,很多组件都是有“生命”的;因此对于这些有血有肉的类,必须给它们注入活力,也就是所谓的组件生命周期管理;
另外,如何管理加载进来的类也是一个问题。假设多个插件依赖了相同的类,是抽取公共依赖进行管理还是插件单独依赖?这就是ClassLoader的管理问题;
资源加载
资源加载方案大家使用的原理都差不多,都是用AssetManager
的隐藏方法addAssetPath
;但是,不同插件的资源如何管理?是公用一套资源还是插件独立资源?共用资源如何避免资源冲突?对于资源加载,有的方案共用一套资源并采用资源分段机制解决冲突(要么修改aapt
要么添加编译插件);有的方案选择独立资源,不同插件管理自己的资源。
目前国内开源的较成熟的插件方案有DL和DroidPlugin;但是DL方案仅仅对Frameworl的表层做了处理,严重依赖that
语法,编写插件代码和主程序代码需单独区分;而DroidPlugin通过Hook增强了Framework层的很多系统服务,开发插件就跟开发独立app差不多;就拿Activity生命周期的管理来说,DL的代理方式就像是牵线木偶,插件只不过是操纵傀儡而已;而DroidPlugin则是借尸还魂,插件是有血有肉的系统管理的真正组件;DroidPlugin Hook了系统几乎所有的Sevice,欺骗了大部分的系统API;掌握这个Hook过程需要掌握很多系统原理,因此学习DroidPlugin对于整个Android FrameWork层大有裨益。
二. Android 类加载器
二.反射基础知识
反射机制的概念就不描述了,看了只会更晕,直接看反射机制能干嘛:
反射机制的作用:
1,反编译:.class-->.java
2,通过反射机制访问java对象的属性,方法,构造方法等;
反射机制中的类:
java.lang.reflect.Constructor;
java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
很多反射中的方法,属性等操作我们可以从这四个类中查询。
具体实现:
1,反射机制获取类有三种方法,我们来获取Student类型
//第一种方式:
Classc1 = Class.forName("Student");
//第二种方式:
//java中每个类型都有class 属性.
Classc2 = Student.class;
//第三种方式:
//java语言中任何一个java对象都有getClass 方法
Employeee = new Student();
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Student)
2,创建对象:获取类以后我们来创建它的对象,利用newInstance:
Class c =Class.forName("Student");
//创建此Class 对象所表示的类的一个新实例
Objecto = c.newInstance(); //调用了Student的无参数构造方法.
3,获取属性,这里我们先只看获取属性,方法是同样的道理。获取属性也分为分为所有的属性和指定的属性:
- 先看获取所有的属性的写法
//获取整个类
Class c = Class.forName("java.lang.Integer");
//获取所有的属性?
Field[] fs = c.getDeclaredFields();
//定义可变长的字符串,用来存储属性
StringBuffer sb = new StringBuffer();
//通过追加的方法,将每个属性拼接到此字符串中
//最外边的public定义
sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");
//里边的每一个属性
for(Field field:fs){
sb.append("\t");//空格
sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等
sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字
sb.append(field.getName()+";\n");//属性的名字+回车
}
sb.append("}");
System.out.println(sb);
这里可以通过getDeclaredFields
拿到所有的属性数组,然后对于每一个属性,可以通过
getModifiers 获得属性的修饰符,例如public,static等等
getSimpleName 获得属性的类型名称
getName 获得属性的名称
field.get(Object object) 获得相应Field的值
- 指定的属性
public static void main(String[] args) throws Exception{
//获取类
Class c = Class.forName("User");
//获取id属性
Field idF = c.getDeclaredField("id");
//实例化这个类赋给o
Object o = c.newInstance();
//打破封装
idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。设置为true就可以访问private修饰的东西,否则无法访问
//给o对象的id属性赋值"110"
idF.set(o, "110"); //set
//get
System.out.println(idF.get(o));
}
通过getDeclaredField 来拿到指定的属性
4.获取方法,和构造方法 同理

获取类的无参构造函数,并实例化类
Class clazz = Class.forName("com.yano.reflect.Person");
Constructor c = clazz.getConstructor(null);
Person p = (Person) c.newInstance(null);
获取类的含参私有构造函数,并实例化类
Class clazz = Class.forName("com.yano.reflect.Person");
Constructor c = clazz
.getDeclaredConstructor(new Class[] { String.class });
// 由于构造函数是 private 的,所以需要屏蔽Java语言的访问检查
c.setAccessible(true);
Person p = (Person) c
.newInstance(new Object[] { "I'm a reflect name!" });
获取并调用类的无参方法
Class clazz = Class.forName("com.yano.reflect.Person");
Constructor c = clazz.getConstructor(null);
Person p = (Person) c.newInstance(null);
Method method = clazz.getMethod("fun", null);
method.invoke(p, null);
获取并调用类的含参方法
Class clazz = Class.forName("com.yano.reflect.Person");
Constructor c = clazz.getConstructor(null);
Person p = (Person) c.newInstance(null);
Method method = clazz.getMethod("fun", new Class[] { String.class });
method.invoke(p, new Object[] { "I'm a reflect method!" });
二. Android插件化原理解析——Hook机制之动态代理
代理是什么
为什么需要代理呢?其实这个代理与日常生活中的“代理”,“中介”差不多;比如你想海淘买东西,总不可能亲自飞到国外去购物吧,这时候我们使用第三方海淘服务比如惠惠购物助手等;同样拿购物为例,有时候第三方购物会有折扣比如当初的米折网,这时候我们可以少花点钱;当然有时候这个“代理”比较坑,坑我们的钱,坑我们的货。
从这个例子可以看出来,代理可以实现方法增强,比如常用的日志,缓存等;也可以实现方法拦截,通过代理方法修改原方法的参数和返回值,从而实现某种不可告人的目的~接下来我们用代码解释一下。

可见,Hook确实成功了!这就是使用代理进行Hook的原理——偷梁换柱。整个Hook过程简要总结如下:
- 寻找Hook点,原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配。
- 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
- 偷梁换柱——用代理对象替换原始对象
完整代码参照:understand-plugin-framework;里面留有一个作业:我们目前仅Hook了Context
类的startActivity
方法,但是Activity
类却使用了自己的mInstrumentation
;你可以尝试Hook掉Activity类的startActivity
方法。