插件化

Android 插件化基础知识

2017-09-05  本文已影响17人  GordenNee

[TOC]

一 .概述

插件化技术听起来高深莫测,实际上要解决的就是两个问题:

  1. 代码加载
  2. 资源加载

代码加载

类的加载可以使用Java的ClassLoader机制,但是对于Android来说,并不是说类加载进来就可以用了,很多组件都是有“生命”的;因此对于这些有血有肉的类,必须给它们注入活力,也就是所谓的组件生命周期管理

另外,如何管理加载进来的类也是一个问题。假设多个插件依赖了相同的类,是抽取公共依赖进行管理还是插件单独依赖?这就是ClassLoader的管理问题

资源加载

资源加载方案大家使用的原理都差不多,都是用AssetManager的隐藏方法addAssetPath;但是,不同插件的资源如何管理?是公用一套资源还是插件独立资源?共用资源如何避免资源冲突?对于资源加载,有的方案共用一套资源并采用资源分段机制解决冲突(要么修改aapt要么添加编译插件);有的方案选择独立资源,不同插件管理自己的资源。

目前国内开源的较成熟的插件方案有DLDroidPlugin;但是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机制之动态代理

代理是什么

为什么需要代理呢?其实这个代理与日常生活中的“代理”,“中介”差不多;比如你想海淘买东西,总不可能亲自飞到国外去购物吧,这时候我们使用第三方海淘服务比如惠惠购物助手等;同样拿购物为例,有时候第三方购物会有折扣比如当初的米折网,这时候我们可以少花点钱;当然有时候这个“代理”比较坑,坑我们的钱,坑我们的货。

从这个例子可以看出来,代理可以实现方法增强,比如常用的日志,缓存等;也可以实现方法拦截,通过代理方法修改原方法的参数和返回值,从而实现某种不可告人的目的~接下来我们用代码解释一下。

img

可见,Hook确实成功了!这就是使用代理进行Hook的原理——偷梁换柱。整个Hook过程简要总结如下:

  1. 寻找Hook点,原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配。
  2. 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
  3. 偷梁换柱——用代理对象替换原始对象

完整代码参照:understand-plugin-framework;里面留有一个作业:我们目前仅Hook了Context类的startActivity方法,但是Activity类却使用了自己的mInstrumentation;你可以尝试Hook掉Activity类的startActivity方法。

上一篇 下一篇

猜你喜欢

热点阅读