资讯专栏INFORMATION COLUMN

人人都会设计模式:代理模式--Proxy

tuniutech / 497人阅读

摘要:话说谁还干类似的事,就在文章末尾点个赞代销店等其实就是现在的商店,以前小的时候听家乡人叫代销店,也是一种代理模式。可以说是系统中最重要的架构之一。

PS:转载请注明出处
作者: TigerChain
地址: http://www.jianshu.com/p/1b3b6b003032
本文出自 TigerChain 简书 人人都会设计模式

教程简介

1、阅读对象
本篇教程适合新手阅读,老手直接略过

2、教程难度
初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢

正文

一、什么是代理模式 1、生活中的代理

1、微商代理

代理在生活中就太多了,比如微商,在朋友圈中很多时候都可以看到微商说城招全国代理「不需要货源,不需要启动资金,只需要一个电话就能做生意,好吧我口才不好,没有人家吹的好」,这类代理就是替卖家出售商品

2、追女孩

遥想当年情窦初开「初中的时候」,喜欢上了一个女子,可是迫于害羞,就给女孩子写了几封情书,买了一束花「但是自己没有那个贼胆送」,就让我们班里一个和女孩认识的朋友交给她,现在想来原来帮我送情书的女生就是我的代理呀「帮我完成我想要完成的事」~~嘻嘻。话说谁还干类似的事,就在文章末尾点个赞

3、代销店等

其实就是现在的商店,以前小的时候听家乡人叫代销店,也是一种代理模式。细细一想,跑业务的也是代理,律师也是代理,明星的助理就是代理,京东送货机器人是代理,共享"女友",那个"女友"也是代理「你懂得」,等等等等。不敢再说了,再说万物都成代理了「不好意思,又忘了吃药了」

2、程序中的代理

其实程序中使用的代理是非常多的,我们在编写 MVC 业务的时候就可以使用代理模式「可以让客户端使用代理仿问接口」,一般使用最多的是动态代理

代理模式的定义

所谓代理就是代表某个真实对象,也就是代理拿到真实对象的引用然后就可以实现真实对象中的功能了

代理模式的结构

角色 类别 说明
AbstractObject 接口或抽象类 抽象出共同的属性
RealObject 真实的类 实现了抽象角色
Prxoy 代理的类 实现了抽象角色,持有真实类的引用

代理模式简单的 UML

代理模式的分类

远程代理:为不同地理的对象提供局域网代表对象

虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候再创建

安全代理:控制用户的访问权限

智能代理:提供对目标对象额外的服务「使用最多的」

代理模式的实现方式「属于智能代理」

静态代理方法

动态代理方法

二、代理模式举例

1、帮忙追 MM

话说在高中期间,小明看上了我们班一位女同学,可是小明是一个害羞胆小的人「有贼心没贼胆」,于是小明跑到我的跟前:Chain 哥,我看上了咱们班的小倩,你能帮我追一下吗 .... 。听小明巴拉巴拉一大堆,本着哥们义气的我非常爽快的答应了,就有了下面的追 MM 手段

简单的 UML

根据 UML 撸码--这里使用静态代理方法

1、要追 MM 首先肯定有 MM ,定义 MM.java

public class MM {
    private String name ; // 姓名 
    private int age ;//年龄 
    private String address ; // 住址

    public MM(String name){
        this.name = name ;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2、定义一个追 MM 方法的接口 ZhuimmWay.java

/**
 * Created by TigerChain
 * 追 MM 的方法,是一个抽象角色
 */
public interface ZhuimmWay  {
    // 送花
    void giveFlowers() ;
    // 写情书
    void writeLoveLetters() ;
    // 买衣服
    void buyClothes() ;
    // 干一些其它的事
    void doSomthing() ;
}

3、主人公小明上场 XiaoMing.java

/**
 * Created by TigerChain
 * 主人公小明,真正的角色
 */
public class XiaoMing implements ZhuimmWay {

    // 要追的 MM
    private MM mm ;

    public void like(MM mm){
        this.mm = mm ;
    }

    @Override
    public void giveFlowers() {
        System.out.println(mm.getName()+" 送给你一朵花");
    }

    @Override
    public void writeLoveLetters() {
        System.out.println(mm.getName()+" 给你八封情书");
    }

    @Override
    public void buyClothes() {
        System.out.println(mm.getName()+" 这是给你买的衣服");
    }

    @Override
    public void doSomthing() {
        System.out.println("给 "+mm.getName()+"说好听的话");
        System.out.println("给 "+mm.getName()+"洗衣服,买单等等一系列手段");
    }
}

4、代理人 TigerChain 上场 ProxyTigerChain.java

/**
 * Created by TigerChain
 * 代理人,我上场了,感觉像媒婆
 */
public class ProxyTigerChain implements ZhuimmWay {

    private XiaoMing xiaoMing ;

    public ProxyTigerChain(XiaoMing xiaoMing, MM mm){
        this.xiaoMing = xiaoMing ;
        xiaoMing.like(mm);
    }

    @Override
    public void giveFlowers() {
        xiaoMing.giveFlowers();
    }

    @Override
    public void writeLoveLetters() {
        xiaoMing.writeLoveLetters();
    }

    @Override
    public void buyClothes() {
        xiaoMing.buyClothes();
    }

    @Override
    public void doSomthing() {
        xiaoMing.doSomthing();
    }
}

5、一切准备就绪,开始追吧,来个测试类 Test.java

public class Test {
    public static void main(String args[]){
        // 主人公小明
        XiaoMing xiaoMing = new XiaoMing();
        // 要追的人小倩
        MM xiaoqian = new MM("小倩") ;
        
        // 小明委托我去帮他追小倩
        ProxyTigerChain proxyChain = new ProxyTigerChain(xiaoMing,xiaoqian) ;
        proxyChain.giveFlowers();
        proxyChain.writeLoveLetters();
        proxyChain.buyClothes();
        proxyChain.doSomthing();
    }
}

6、运行查看结果

上面的代码完美吗?完美个鸟鸟,试想把 Test 比做一个场景:比如是在 KTV ,我靠,小明不是害羞吗?竟然也出现在 KTV 中「如果小明能当明看着你帮他追小倩,早就自己动手了」,所以按正常逻辑小明不应该出现在 KTV「Test 中」

7、修改代码,我们添加一个 ZhuimmFactory.java

/**
 * Created by TigerChain
 * 定义一个工厂类,这样就屏蔽了客户端对代理的感知
 */
public class ZhuimmFactory {

    public static ZhuimmWay getInstance(String name){
        return new ProxyTigerChain(new XiaoMing(),new MM(name)) ;
    }
}

嘻嘻,不知不觉又用到以前学到的简单工厂模式了「学以致用,不错不错」,我们把代理事情都放在工厂中去做,这样客户端对代理是无感知的,这也符合程序开发的正常逻辑

8、修改 Test 端调用代码

public class Test {
    public static void main(String args[]){
        // 调用者不知道调用的是代理类还是真实类,这才是正常的逻辑呀
        ZhuimmWay zhuimmWay = ZhuimmFactory.getInstance("小倩") ;
        zhuimmWay.giveFlowers();
        zhuimmWay.writeLoveLetters();
        zhuimmWay.buyClothes();
        zhuimmWay.doSomthing();
    }
}

9、运行查看结果

想知道结局吗?很不幸,小倩也有点"白痴",我提醒好多次是小明喜欢她「其实我最多是代理小明送花等这些事情,也就是说钱花小明的,美女我来追」,可是她最终还是看上我了「有点自恋」,所以以后追 MM 的时候,千万千万不要找代理「以上故事纯属虚构,如有雷同,那么小明以后就张点心吧」

2、真假美猴王

1、使用静态代理完成

六耳猕猴梦想简单的 UML

根据 UML 撸码

1、定义抽象接口 IToWest.java

/**
 * Created 抽象类,去西天的条件
 */
public interface IToWest {
    //保护唐僧
    void baohuTangSeng() ;
    //降妖除魔
    void xiangYaoChuMo() ;
    //上天入地
    void shangTianRuDi() ;
}

2、定义孙悟空类 SunWuKong.java

/**
 * Created by Tigerchain
 * 悟空
 */
public class SunWuKong implements IToWest{
    @Override
    public void baohuTangSeng() {
        System.out.println("我孙悟空能 保护唐僧");
    }

    @Override
    public void xiangYaoChuMo() {
        System.out.println("我孙悟空能 降妖除魔");
    }

    @Override
    public void shangTianRuDi() {
        System.out.println("我孙悟空能 能上天入地");
    }
}

3、定义六耳猕猴类「代理角色」 LiuErMiHou.java

package prxoy.monkeyking;

/**
 * Created by Tigerchain
 * 悟空的代理六耳猕猴
 */
public class LiuErMiHou extends SunWuKong implements IToWest {

    @Override
    public void baohuTangSeng() {
        super.baohuTangSeng();
    }

    @Override
    public void xiangYaoChuMo() {
        super.xiangYaoChuMo();
    }

    @Override
    public void shangTianRuDi() {
        super.shangTianRuDi();
    }
}

4、测试 Test.java

/**
 * Created by TigerChain
 * 测试类 六耳 代理悟空
 */
public class Test {
    public static void main(String args[]){

        IToWest liuErMiHou = new LiuErMiHou() ;
        liuErMiHou.baohuTangSeng();
        liuErMiHou.xiangYaoChuMo();
        liuErMiHou.shangTianRuDi();

        System.out.println("我孙悟空能去得了西天");
    }
}

5、运行查看结果

好了,上面我们看到我们使用代理类直接继承了真实的类「这也是代理的一个变种」,但是根据多用类组合少用继承的规则,我们还是少用这种继承形式的代理

以上是静态代理,静态代理有局限性,想如果悟空多了项技能,六耳猕猴就得学此项技能「感觉很像我们搞技术的,技术日新月异,得不断的学习才能进步」

静态代理的缺点:

1、代理的方法如果很多,那么就要为每个方法都要代理,规模大的程序受不了

2、如果真实类中新添加一个方法或功能,那么代理类中就一一对应的写出来,这样不利于扩展并且增加代码维护成本

3、一个代理类只能代理一个真实的对象

2、使用动态代理完成

动态代理就是代理类不是在代码中定义的,而是根据我们的指示动态生成的「通过反射机制动态生成代理者对象」,在编码阶段,你从代码上根本不知道谁代理谁,具体代理谁,好吧太绕了,直接看代码

1、Proxy 类

说动态代理之前,我们先来看看 Java 中提供的 Proxy 类

看看这个类的注释一部分

/* {@code Proxy} provides static methods for creating dynamic proxy
 * classes and instances, and it is also the superclass of all
 * dynamic proxy classes created by those methods.
 * .....
 *
/
 public class Proxy implements java.o.Serializable {
     .... 省略代码 
 }

从注释可以看出 Proxy 提供一些静态方法来创建动态代理类和实例

Proxy 简单的 UML

Proxy 主要方法讲解

Proxy 主要方法就是 newProxyInstance 这个方法

   public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h){

    ... // 省略若干代码 
     
    // 取得代理类
    Class cl = getProxyClass0(loader, intros)

    ... // 省略若干代码 

    // 调用代理类的构造方法
    final Constructor cons = cl.getConstructor(constructorParams);
 
    ... // 省略若干代码 
   
    final InvocationHandler ih = h;
    
    ... // 省略若干代码 

    // 通过代理类的构造方法生成代理类的实例
    return cons.newInstance(new Object[]{h});
    
   }

其中三个参数:

ClassLoader loader:代理类的类加载器

Class[] interfaces:代理类要实现的接口列表

InvocationHandler h:调用处理程序

从 newProxyInstance 方法中我们知道了代理对象是如何产生的了「注释很清楚了」

再看看 InvocationHandler

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

其中三个参数:

Object proxy: 被代理的对象

Method method:要操作的方法

Object[] args:方法要传入的参数,可以没有,也可以有多个或 null

InvocationHandler 接口中的方法就是执行被代理对象中的方法

2、使用动态代理修改真假美猴王代码

动态代理悟空 简单的UML

根据 UML 撸码

只需要在原有代码的基础上添加一个动态类并且删掉六耳猕猴类「动态代理来了,小六你还不快撤」,然后修改 Test 即可

1、添加动态代理类 ToWestProxy.java

/**
 * 动态代理类
 */
public class ToWestProxy implements InvocationHandler {
    // 需要代理的对象即真实对象
    private Object delegate ;

    public Object getProxy(Object delegate){
        this.delegate = delegate ;
        // 动态构建一个代理
        return  Proxy.newProxyInstance(delegate.getClass().getClassLoader(),delegate.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(delegate,args) ; // 通过反射调用真实对象对应的方法
    }
}

我们看到上在被代理的对象是一个 Object 类型,所以可以看出这个代理类就是一个万能的代理,不仅仅可以代理悟空,牛魔王也能代理「扯远了」

2、修改 Test.java

/**
 * Created by TigerChain
 * 测试类
 */
public class Test {
    public static void main(String args[]){

        IToWest sunWuKong = new SunWuKong() ;

        // 取得动态代理
        IToWest proxy = (IToWest) new ToWestProxy().getProxy(sunWuKong);

        proxy.baohuTangSeng();
        proxy.xiangYaoChuMo();
        proxy.shangTianRuDi();
        System.out.println("我孙悟空能去得了西天");
    }
}

看到了,真实对象悟空随便你改,我再添加接口,方法,我动态代理不用动「如果是静态代理六耳猕猴,那就得随着悟空的修改必须得修改自己」

而且,我们还可以得出,这个动态代理不仅仅可以代理悟空,简直可以代理一切对象「不信你定义一个牛魔王试试」

3、运行查看结果

简直 perfect 有木有

3、自动售票机

随着科技的发达,我们现在买车票的时候可以在自动售票机「代理售票人员」上购买

自动售票机简单的 UML

根据 UML 撸码--采用动态代理技术

1、先来一个抽象角色 ISellTicket.java

/**
 * Created by TigerChain
 * 定义一个抽象接口
 */
public interface ISellTicket {
    // 售票
    void sellTicket() ;
}

2、要出票,当然有买的票的人 User.java

/**
 * Created by TigerChain
 * 买票的人
 */
public class User {
    private String uname ; //姓名
    private String address ; // 地址
    private String sex ;     // 性别
    private String idNum ;   // 身份证号
    private String pay ;     // 掏票钱

    public String getUname() {
        return name;;
    }

    public void setUname(String uname) {
        this.uname = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getIdNum() {
        return idNum;
    }

    public void setIdNum(String idNum) {
        this.idNum = idNum;
    }

    public String getPay() {
        return pay;
    }

    public void setPay(String pay) {
        this.pay = pay;
    }
}

3、真实对象售票员小张 XiaoZhangSeller.java

/**
 * Created 真实的售票员小张
 */
public class XiaoZhangSeller implements ISellTicket {

    private User user ;

    public XiaoZhangSeller(User user){
        this.user = user ;
    }

    @Override
    public void sellTicket() {
        if(null !=user){
            System.out.println("买票者的信息===============");

            System.out.println("买票者姓名:"+user.getUname());
            System.out.println("买票性别:"+user.getSex());
            System.out.println("买票者身份证号:"+user.getIdNum());
            System.out.println("买票者住址:"+user.getUname());

            System.out.println("==============================") ;

            System.out.println("正在验证信息...信息无误,请支付票钱");
            System.out.println("买票者支付:"+user.getPay()+" 元");
            System.out.println("请稍等正在出票.....");
            System.out.println("出票成功:从西安到宝鸡大巴进站去坐");
        }
    }
}

4、动态代理 DyAutoSellerProxy.java

/**
 * Created by TigerChain
 * 自动出票机,为了演示名字这样想,其实这是一个万能的动态代理
 */
public class DyAutoSellerProxy implements InvocationHandler {

    private Object object ;

    public DyAutoSellerProxy(Object object){
        this.object = object ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(object,args) ;
    }
}

5、测试一下 Test.java

/**
 * Created by TigerChain
 * 测试类
 */
public class Test {
    public static  void main(String args[]){
        // 定义个买票者
        User tigerChain = new User() ;
        tigerChain.setUname("TigerChain");
        tigerChain.setAddress("中国陕西");
        tigerChain.setSex("男");
        tigerChain.setIdNum("610326************");
        tigerChain.setPay("45.00");

        // 真实的买票员小张
        ISellTicket iSellTicket = new XiaoZhangSeller(tigerChain) ;

        // 动态代理
        DyAutoSellerProxy dyAutoSellerProxy = new DyAutoSellerProxy(iSellTicket) ;

        // 动态创建一个出票机,把出票交给出票机去处理
        ISellTicket iSellTicket1 = (ISellTicket) Proxy.newProxyInstance(iSellTicket.getClass().getClassLoader(),iSellTicket.getClass().getInterfaces(),dyAutoSellerProxy);

        iSellTicket1.sellTicket();
    }
}

6、运行查看结果

自么样一个自动售票机就完成了「完全代理了人工去卖票」

PS:这个 Demo 使用动态代理实现的,请大家自行使用静态代理实现本 Demo ,一定要动手实践哦

4、AIDL 进行进程间通讯「远程代理」

AIDL「Android 接口定义语言,是一种语言,其实就是 Android 中的远程 Service」,再说 AIDL 之前就不得不说 Binder「这里简洁明了的说一下 Binder 是什么,不展开深入讨论,如果深入展开,三天三夜也说不完」

什么是 Binder

由于两个进程不能直接进行通讯「为了安全系统有进程隔离机制」,所以两个进程之间是不能直接进行通讯的。Binder 可以说是 Android 系统中最重要的架构之一。Binder 是连接 Client「进程」 和 Server「进程」 的一个桥梁,Binder 是进程间通信的方式之一,在 Android 用的灰常灰常的多

我们先来看看 Android 的架构图像

图片来自 Android 的源码官站:https://source.android.com/devices/

从 Android 的框架图中我们可以看到,应用程序框架层和系统服务层之间就是通过 Binder IPC 进行通讯的,说 Binder 机制前,我们先了解几个特点

1、两个进程之间不能直接通信

2、内核可以仿问进程中的所有数据

3、两个进程之间不能直接进行通信,我们可以借助内核做中转达到间接通信的目的「Binder 就是这种机制」

Binder 下两个进程通信的简易流程

PS: 以上图是便于理解所以抽象出来一张图,真实的 Binder 比这个过程复杂的多,这牵扯到 java 层的 Binder ,native 层的 Binder 等等「这不是我们讨论的重点」,方便我们理解,我们可以认为客户端的进程拿到服务端的引用,所以就可以调用服务端进程的方法了

说了这么多,这跟代理有个毛关系呢,别急我们写一个 AIDL 的实例分析一下:

AIDL demo 简单的 UML

根据 uml 写代码

我们写一个简单的通过 Client 进程调用 Server 进程返回一个字符串功能,为了方便起见,我们直接在一个项目中创建「Server 开启在另一个进程中,开两个 APP 进行通信大家可以自行试一下,道理一模一样的」

1、在项目中新建一个 AIDL 文件「在 AS 中的 APP上直接右键 new AIDL 即可」

interface CustomAIDL {

    String getStr() ;
}

此时我们点击一下图标构造一下项目,此时会在 appuildgeneratedsourceaidldebug包名CustomAIDL.java 文件「把 AS 切换到 project 视图下很容易找到」,这是 IDE 帮我们自动生成的

2、定义一个远程服务 AIDLRemoteService.java

/**
 * @Description 创建一个远程服务
 * @Creator TigerChain(创建者)
 */
public class AIDLRemoteService extends Service {

    private final CustomAIDL.Stub aidl = new CustomAIDL.Stub() {
        @Override
        public String getStr() throws RemoteException {
            return " 我是远程服务返回的 HELLO ";
        }
    } ;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return aide;
    }
}

3、定义 AidlActivity 测试调用 「核心代码给出,其余代码看 Demo 即可」

public class AidlActivity extends AppCompatActivity implements View.OnClickListener{

 private CustomAIDL customAIDL ;
... 省略若干代码

// 客户端连接服务
private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            customAIDL = CustomAIDL.Stub.asInterface(service) ;
            Log.e("service:","onServiceConnected") ;
            isServerStarted = true ;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            customAIDL = null ;
            Log.e("service:","onServiceDisconnected") ;
            isServerStarted = false ;

        }
    } ;

 @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_bind_service:
                // 绑定服务
                bindService(new Intent(AidlActivity.this,AIDLRemoteService.class),serviceConnection, Context.BIND_AUTO_CREATE) ;
                break ;
            case R.id.btn_test_method:
                if(!isServerStarted){
                    Toast.makeText(AidlActivity.this,"请先绑定服务先",Toast.LENGTH_SHORT).show();
                    return ;
                }
                try {
                    String str = customAIDL.getStr();
                    Toast.makeText(AidlActivity.this,str,Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();

                }
                break ;
            default:
                break ;
        }
    }

... 省略若干代码

}

4、在 mainifests 中注册服务

 

我这里给服务定义了一个 process ,那说明这个服务是运行在一个新进程中的

5、测试一下,运行查看结果

我们看一下当前项目进程情况

的确是两个进程「AidlActivity 和 AIDLRemoteService 分别在两个进程中」,我们定义的 remote 也显示出来了,看一下结果

怎么样,两个进程之间完美的进行了通信了

通个毛呢?这和 proxy 有个啥关系呀「巴拉巴拉这么久」,不要急吗?软件开发有一条宗旨:先让它运行起来「我们先把 Demo 运行起来再说吗:咳咳又到了吃药的时间了」,我们来分析一下上面的调用过程

过程分析

1、还记得我们上面说的 AD 帮我们自动生成的 CustomAIDL.java 文件吗,我们来一窥它的真容「以下代码是格式化后的」

// 这里的 IInterface 代表远程 Server 对象有什么能力
public interface CustomAIDL extends android.os.Interface {
    /**
     * Local-side IPC implementation stub class.
     */
    // 在 server 端调用
    public static abstract class Stub extends android.os.Binder implements designpattern.jun.com.designpattern.CustomAIDL {
        private static final java.lang.String DESCRIPTOR = "designpattern.jun.com.designpattern.CustomAIDL";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an designpattern.jun.com.designpattern.CustomAIDL interface,
         * generating a proxy if needed.
         * 其中的 android.os.IBinder obj 对象是驱动给们的,这个就是我们绑定 service ,在 onServiceConnecttion 回调里面这个对象拿到一个远程的 Service 
         */
        public static designpattern.jun.com.designpattern.CustomAIDL asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof designpattern.jun.com.designpattern.CustomAIDL))) {
                // client 和 Server 在同一个进程调用 后面 debug 可以验证
                return ((designpattern.jun.com.designpattern.CustomAIDL) win);
            }
            // cliet 和 Server 不在同一个进程调用代理对象 后面 debug 可以验证
            return new designpattern.jun.com.designpattern.CustomAIDL.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            // 给客户端写数据
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getStr: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getStr();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        // 运行在客户端 server 进程的远程代理,实现对远程对象的仿问
        private static class Proxy implements designpattern.jun.com.designpattern.CustomAIDL {
            private android.os.Binder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.lang.String getStr() throws android.os.RemoteException {
                // 读取服务端写过来的数据
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStr, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getStr = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public java.lang.String getStr() throws android.os.RemoteException;
}

这下看到 Proxy 了吧「是不是有点小激动呢」,我们来分析一下

上面的图就是一个简单的 AIDL 的流程图,方便理解认为 CustomAIDL.stub 就是远程进程,它把信息注册到 Binder 中, CustomAIDL.Stub.Proxy 就是一个代理,代理什么呢?代理远程的 Binder ,远程 Binder 把方法传给 Client 就完成了两个进程间通信「详细过程比这个复杂」,对于 Binder 的入门介绍可以参看:Binder 学习指南 还是非常不错的,建议看三遍以上

PS:这里再说一点,以上情况是针对 client 和 server 在两个进程间的通信,如果 client 和 server 在一个进程中,则 CustomAIDL.Stub.Proxy 就不会调用「在同一个进程中,我自己就能调自己还代理个毛呀」,不信?以结果征服你

client 和 server 同一进程和不同进程分析

1、不同进程

 

通过以上配置,我们可以看到 AIDLRemoteService 是运行在多带带进程中的,我们在 CustomAIDL.java 中的 asInterface 方法中 debug 跟一下看看结果

通过图我们可以看出,如果 client 和 server 不在同一个进程中,那么代码就会走到

调用代理的地方---CustomAIDL.Stub.Proxy,并传递远程代理的对象

2、在同一进程

去掉 service 中的 android:process=":reomte" 则 client 和 server 就在同一进程了

        

同理 debug 看结果

对比上面的图我们就知道了,这里的 iin 不为空,进入了 if 的方法体「没有调用代理」,至此上面的结果验证完毕

关于 AIDL 远程代理就说到这里了,如果对 Binder 想要深入了解,可以自行回去研究「这不在本节的范围内」

WTF 一个 AIDL 说了这么大半天,希望大家不要晕「我都有点晕了」

源码地址: https://github.com/githubchen001/DesignPattern 看 proxy/aidl 这部分

三、Android 源码中的代理模式

其实通过上面的 AIDL 实验,我们就可以知道 Binder 使用的就是远程代理模式,Android 中的源码使用非常多,我就不一一分析了「说的太多人会受不鸟」,感兴趣的朋友可以自行分析,我这里贴出一张图,大家可以看

我们看看应用程序框架层的 XXXManager 对应田系统层的 XXXService 它们之间通过使用 AIDL 来进行跨进程通信,有兴趣可以扒扒这部分的源码看一下

四、代理模式的优缺点

优点

1、代理模式拿到的真实对象的引用,把真实对象很好的保护起来安全性高

2、扩展性好

缺点

增加了系统的复杂度,增加了额外好多的代码「设计模式好像都是这样」

到此为止,我们把代理模式就说完了,由于这篇篇幅比较大,Android 源码也没有给大家分析「希望大家自行去看看,希望你有一种哦~原来是这样的赶脚」,其它的虚拟代理,缓存代理大家有兴趣也可以试试

参考资料

小米开放平台:彻底理解ANDROID BINDER通信架构(上)

Binder学习指南 建议看三遍以上,非常基础的一步步介绍 Binder

以后文章会第一时间发在公号,请大家添加博文的公号,扫描添加即可关注
公众号:TigerChain

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70717.html

相关文章

  • 人人都能懂的Vue源码系列—07—initProxy

    摘要:不同的代码运行环境赋值的结果不同。当访问的属性不是类型或者属性值在被代理的对象上不存在,则抛出错误提示,否则就返回该属性值。该方法可以在开发者错误的调用属性时,提供提示作用。只不过目前规范还没有很完善,使用的时候要稍加注意。 前几篇文章中,我们主要讲了merge options的一些操作。今天我们回到init方法往下讲。 if (process.env.NODE_ENV !== pro...

    vspiders 评论0 收藏0
  • 【Copy攻城狮日志】Docker部署D2Admin 人人企业版

    摘要:介于目前项目的前端开发基于人人企业版有了快狗团队的手摸手,很快就能用部署这样一个后台管理平台。构建镜像,部署静态资源这里借助获取镜像,通镜像作为基础来构建人人企业版镜像。本许可协议授权之外的使用权限可以从处获得。 Created by huqi at 2019-5-24 21:01:30 Updated by huqi at 2019-5-26 00:00:42 前言 最近后端的小...

    JessYanCoding 评论0 收藏0
  • 【Copy攻城狮日志】Docker部署D2Admin 人人企业版

    摘要:介于目前项目的前端开发基于人人企业版有了快狗团队的手摸手,很快就能用部署这样一个后台管理平台。构建镜像,部署静态资源这里借助获取镜像,通镜像作为基础来构建人人企业版镜像。本许可协议授权之外的使用权限可以从处获得。 Created by huqi at 2019-5-24 21:01:30 Updated by huqi at 2019-5-26 00:00:42 前言 最近后端的小...

    YancyYe 评论0 收藏0
  • [面试专题]一线互联网大厂面试总结

    摘要:道阻且长啊前端面试总结前端面试笔试面试腾讯一面浏览器工作原理浏览器的主要组件包括用户界面包括地址栏后退前进按钮书签目录浏览器引擎用来查询及操作渲染引擎的接口渲染引擎渲染界面和是基于两种渲染引擎构建的,使用自主研发的渲染引擎,和都使用网络用来 道阻且长啊TAT(前端面试总结) 前端 面试 笔试 面试 腾讯一面 1.浏览器工作原理 浏览器的主要组件包括: 用户界面- 包括地址栏、后退/前...

    lemanli 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<