专栏首页CodingBlock小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理

小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理

前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述。前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以决心花些时间研究了一下 DroidPlugin 插件框架的原理,以便再出现问题时也能从容应对。打开源码后发现尽是大把大把的 hook、binder、classloader 等等,很难摸清头绪,幸运的是,有很多热心的大神已经对 DroidPlugin 的原理进行了透彻的剖析,文末会有本人对参考文章的致谢。

  本系列文章的代码已经上传至github,下载地址:https://github.com/lgliuwei/DroidPluginStudy 本篇文章对应的代码在 com.liuwei.proxy_hook.proxy 包内。

· 代理模式

  在 DroidPlugin 中用到了大量的动态代理,所以如果我们想理解 DroidPlugin 的原理,首先我们需要知道什么是动态代理,说到动态代理,我们难免会想起静态代理,那么代理是什么呢?

  代理模式的意图是通过提供一个代理( Proxy )或者占位符来控制对该对象的访问。类比我们生活中,代理也是随处可见,其中中介就是一个很好的例子,把代理看做生活中的中介,将更加易于理解,试想一下,如果我们想租房或者买房的话通过中间是不是就可以让我们非常省心。

一、静态代理

  为了保证与所代理的对象功能行为的一致性,代理类一般需要实现实体类所实现的同一个接口,以下即为一个最基本的代理模式的结构。

  首先先定义一个接口,供实体类和代理类实现。(如:接口 Sbuject1 )

1 /**
2  * Created by liuwei on 17/3/1.
3  */
4 public interface Subject1 {
5     void method1();
6     void method2();
7 }

  然后创建一个 Subject1 的实现类。

 1 /**
 2  * 实体类
 3  * Created by liuwei on 17/3/1.
 4  */
 5 public class RealSubject1 implements Subject1 {
 6     @Override
 7     public void method1() {
 8         Logger.i(RealSubject1.class, "我是RealSubject1的方法1");
 9     }
10     @Override
11     public void method2() {
12         Logger.i(RealSubject1.class, "我是RealSubject1的方法2");
13     }
14 }

  再为 RealSubject1 创建一个代理类。

 1 /**
 2  * 静态代理类
 3  * Created by liuwei on 17/3/1.
 4  */
 5 public class ProxySubject1 implements Subject1 {
 6     private Subject1 subject1;
 7     public ProxySubject1(Subject1 subject1) {
 8         this.subject1 = subject1;
 9     }
10     @Override
11     public void method1() {
12         Logger.i(ProxySubject1.class, "我是代理,我会在执行实体方法1之前先做一些预处理的工作");
13         subject1.method1();
14     }
15     @Override
16     public void method2() {
17         Logger.i(ProxySubject1.class, "我是代理,我会在执行实体方法2之前先做一些预处理的工作");
18         subject1.method2();
19     }
20 } 

  可以发现,代理模式还是很简单的,很快我们就写好一个最基本的代理结构,接下来写个测试类跑一下看看效果。

 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class ProxyTest {
 5     public static void main(String[] args){
 6         // static proxy
 7         ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1());
 8         proxySubject1.method1();
 9         proxySubject1.method2();
10 }

  输出结果非常简单,这里就不再贴出来了。我们看到,在测试类中只需要调用 ProxySubject1 的对像即可对实现对 RealSubject1 的操作。同时我们也发现在初始化 ProxySubject1 时需要传入 RealSubject1 的对象,当然,我们完全可以把获取 RealSubject1 的对象封装到代理类内部,这只是代理模式根据业务需要的不同体现而已。有很多人把这一点作为区分代理模式和适配器模式的依据,这个是不对的,由于本篇的重点是为插件化的原理做铺垫,至于代理模式和适配器模式的区别日后会专门写一篇文章介绍,这里就不细说了。

  其实,从这个简单的示例中也许并没有体现出代理模式的优势,而且还要多创建一个代理类,反而看起来好像更麻烦了。其实代理模式很明显的好处就是通过代理,可以控制对实体对象的访问,从而提高了安全性。而且可以在调用实体类的方法时做一些预处理和善后的工作,这样就保证了实体类可以抛开复杂的业务逻辑而只去实现一些最纯粹的功能,提高了代码的可读性和灵活性。

二、动态代理

  动态代理是本文的重点,也是 DroidPlugin 插件化框架的基础。动态代理乍一听起来好像也挺高大上的,但幸运的是,它并没有我们想象中那么高深莫测,所以我们大可不必对它有任何的畏惧之感。

  假设我们在上文静态代理的例子中又多了一个 RealSubject2 的类,它实现的接口是 Subject2,这时候我们如果想对 RealSubject2 进行代理需要如何做?这个简单,我们直接类比 ProxySubject1 再创建一个 ProxySubject2 即可,这样是可以的,但如果有非常多的实体类并且都实现了不同的接口,那我们岂不是需要创建很多的代理类:ProxySubject1,ProxySubject2 ... ProxySubjectN!还有没有更优雅一些的方法?答案是肯定的,动态代理即可解决这个问题。(当然,这并不是动态代理唯一的优点)

  动态代理是在实现阶段不需要关心代理谁,在运行阶段才指定代理对象。创建一个动态代理类很简单,JDK已经给我们提供好了动态代理接口  InvocationHandler 我们只需要实现它即可创建一个动态代理类,以下是一个简单的小例子:

 1 /**
 2  * 动态代理
 3  * Created by liuwei on 17/3/1.
 4  * 注:动态代理的步骤:
 5  *  1、写一个InvocationHandler的实现类,并实现invoke方法,return method.invoke(...);
 6  *  2、使用Proxy类的newProxyInstance方法生成一个代理对象。例如:生成Subject1的代理对象,注意第三个参数中要将一个实体对象传入
 7  *          Proxy.newProxyInstance(
 8                          Subject1.class.getClassLoader(),
 9                          new Class[] {Subject1.class},
10                          new DynamicProxyHandler(new RealSubject1()));
11 
12  */
13 public class DynamicProxyHandler implements InvocationHandler {
14     private Object object;
15 
16     public DynamicProxyHandler(Object object) {
17         this.object = object;
18     }
19 
20     @Override
21     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
22         Logger.i(DynamicProxyHandler.class, "我正在动态代理[" + object.getClass().getSimpleName() + "]的[" + method.getName() + "]方法");
23         return method.invoke(object, args);
24     }
25 
26     /**
27      * 调用Proxy.newProxyInstance即可生成一个代理对象
28      * @param object
29      * @return
30      */
31     public static Object newProxyInstance(Object object) {
32         // 传入被代理对象的classloader,实现的接口,还有DynamicProxyHandler的对象即可。
33         return Proxy.newProxyInstance(object.getClass().getClassLoader(),
34                 object.getClass().getInterfaces(),
35                 new DynamicProxyHandler(object));
36     }
37 } 

  这是一个名为 DynamicProxyHandler 的动态代理类,其中 invoke 方法完成了对代理对象方法的调用,是必须实现的。接下来使用此类代理其他的实体类也非常简单,只需使用 Proxy 的newProxyInstance() 方法并传入相应的参数即可获取一个代理对象,接下来我们在测试类里面添加一下代码,代码如下:

 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class ProxyTest {
 5     public static void main(String[] args){
 6         // static proxy
 7         ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1());
 8         proxySubject1.method1();
 9         proxySubject1.method2();
10 
11         // 如果想对RealSubject2代理显然不得不重新再写一个代理类。
12         ProxySubject2 proxySubject2 = new ProxySubject2(new RealSubject2());
13         proxySubject2.method1();
14         proxySubject2.method2();
15 
16         Logger.i(ProxyTest.class, "----------分割线----------\n");
17 
18         // 如果写一个代理类就能对上面两个都能代理就好了,动态代理就解决了这个问题
19         Subject1 dynamicProxyHandler1 = (Subject1) DynamicProxyHandler.newProxyInstance(new RealSubject1());
20         dynamicProxyHandler1.method1();
21         dynamicProxyHandler1.method2();
22 
23         Subject2 dynamicProxyHandler2 = (Subject2)DynamicProxyHandler.newProxyInstance(new RealSubject2());
24         dynamicProxyHandler2.method1();
25         dynamicProxyHandler2.method2();
26     }
27 }

  输出结果非常简单,这里不再给出。

三、小结

  至此,相信我们对动态代理已经有一个基本的认识,其实代理模式除了上文中提到的普通代理(静态代理的一种)、动态代理之外还有很多种方式,如远程代理、虚拟代理、智能代理等等,这里就不一一介绍了。

  其实插件化的原理简单来说是使用动态代理,通过反射等机制将系统中的一些方法hook掉,从而达到劫持系统方法的目的以实现对系统方法的篡改。例如通过 hook 掉 AMS 的 startActivity 方法来启动一个没有在清单文件中配置的 Activity 。下一篇文章将详细介绍 Hook 机制,以及反射在 Hook 中的实际体现。  

  致谢:最后我想说的是“吃水不忘挖井人”!非常感谢术哥《Android插件化原理解析——概要》系列文章,本人正是在参考了这些内容的思路之后才有能力写下本系列文章。本人在Android的插件化领域可以说算是一个小白,写下本系列文章的目的一方面是在实践中加深自己的理解,另一方面是根据本人以小白角度对插件化原理的体会用更加简单易懂的方式传达出来,从而帮助小白也能读懂插件化!

本文链接:http://www.cnblogs.com/codingblock/p/6580364.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android学习笔记(二)Manifest文件节点详解

      在上一篇博文中简单介绍了Manifest文件及其存放位置,本篇就来详细介绍一下Manifest文件中的节点和一些节点的基本作用,首先看一下Manifest文...

    codingblock
  • Java数据结构和算法总结-数组、二分查找

    前言:在平时开发中数组几乎是最基本也是最常用的数据类型,相比链表、二叉树等又简单很多,所以在学习数据和算法时用数组来作为一个起点再合适不过了。本篇博文的所有代...

    codingblock
  • 设计模式之单例模式

      无论什么开发中,设计模式都起着关键的作用,其中比较常用的当属单例了,所谓单例,就是让一个类在项目中只存在一个对象,即使用到这个类的地方很多,也只存在一个对象...

    codingblock
  • 【鼠年大吉】【设计模式自习室】详解代理模式

    该系列会逐步更新于我的博客和公众号(博客见文章底部),也希望各位观众老爷能够关注我的个人公众号:后端技术漫谈,不会错过精彩好看的文章。

    Rude3Knife的公众号
  • 【设计模式自习室】详解代理模式

    通俗的来讲,代理模式就是我们生活中常见的中介。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。

    后端技术漫谈
  • 利用 Flask+Redis 维护 IP 代理池

    目前有很多网站提供免费代理,而且种类齐全,比如各个地区、各个匿名级别的都有,不过质量实在不敢恭维,毕竟都是免费公开的,可能一个代理无数个人在用也说不定。所以我们...

    崔庆才
  • 2.3.1 理解动态代理 -《SSM深入解析与项目实战》

    Spring中AOP的拦截功能就是使用Java中的动态代理实现的。也就是在被代理类(方法)的基础上增加切面逻辑,生成代理类(方法)。切面的逻辑可以在目标类函数执...

    谙忆
  • 手把手教你利用Pyecharts库对IP代理数据进行数据可视化分析

    前几天小编发布了手把手教你使用Python爬取西次代理数据(上篇)和手把手教你使用Python爬取西次代理数据(下篇),木有赶上车的小伙伴,可以戳进去看看。今天...

    Python进阶者
  • 所有和Java中代理有关的知识点都在这了。

    对于每一个Java开发来说,代理这个词或多或少都会听说过。你可能听到过的有代理模式、动态代理、反向代理等。那么,到底什么是代理,这么多代理又有什么区别呢。本文就...

    java思维导图
  • Spring知识点(五)代理模式

    使用代理模式的目的是为了将原来类生成一个代理类,由代理类来执行原来类的一些增强方法,但是也不影响原来类中方法的执行。

    虞大大

扫码关注云+社区

领取腾讯云代金券