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

C++STL学习笔记(第一篇:stl是什么?为什么要学习stl?迭代器在stl中扮演着什么角色?) c++STL入门 阅读详情

教程大纲

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

教程简介

  • 1、阅读对象
    本篇教程适合新手阅读,老手直接略过
  • 2、教程难度
    初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢

正文

一、什么是代理模式

1、生活中的代理

1、微商代理

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

2、追女孩

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

3、代销店等

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

2、程序中的代理

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

代理模式的定义

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

代理模式的结构

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

代理模式简单的 UML

代理模式简单的 UML

代理模式的分类

  • 远程代理:为不同地理的对象提供局域网代表对象
  • 虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候再创建
  • 安全代理:控制用户的访问权限
  • 智能代理:提供对目标对象额外的服务「使用最多的」

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

  • 静态代理方法
  • 动态代理方法

二、代理模式举例

1、帮忙追 MM

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

简单的 UML

我帮小明追 MM

根据 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、运行查看结果

追 MM 方法验证

上面的代码完美吗?完美个鸟鸟,试想把 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 方法验证

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

2、真假美猴王

1、使用静态代理完成

六耳猕猴梦想简单的 UML

六耳猕猴梦想简单的 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 简单的 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
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

根据 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

根据 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 的架构图

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

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

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

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

binder_arc

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

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

AIDL demo 简单的 UML

AIDL demo 简单的 UML

根据 uml 写代码

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

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

    String getStr() ;
}

此时我们点击一下图标构造一下项目,此时会在 app\build\generated\source\aidl\debug\包名\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 中注册服务
 <service android:name=".Proxy.AIDL.AIDLRemoteService"
            android:process=":reomte"></service>

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

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

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

当前 Demo 进程情况

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

AIDL demo 结果

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

通个毛呢?这和 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 简单的流程图

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

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

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

  • 1、不同进程
 <service android:name=".Proxy.AIDL.AIDLRemoteService"
            android:process=":reomte"></service>

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

AIDL 调用 Proxy

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

调用代理

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

  • 2、在同一进程

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

 <service android:name=".Proxy.AIDL.AIDLRemoteService"/>       

同理 debug 看结果

不调用 proxy

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

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

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

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

三、Android 源码中的代理模式

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

IPC

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

四、代理模式的优缺点

优点

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

缺点

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

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

参考资料

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

igh ethercat 实时性测试 组备注std(us)max(us)a无锁核,无rt补丁8.38108b锁核+FIFO调度+内存锁定,无rt补丁0.9216c锁核+FIFO调度+内存锁定,+rt补丁0.9922可以看到 a组 使用ethercat不做其他优化,周期抖动非常大,且max能达到 100 多 us;通过 锁核+FIFO调度+内存锁定 进行优化,可以看到 抖动明显降低,且max 为 20 us 左右; 阅读详情

相关推荐

声纹识别实战:用Python从零搭建一个简易声纹验证系统(附完整代码)

本文是一份详细的声纹识别实战指南,教你使用Python从零搭建一个简易的声纹验证系统。文章涵盖了从音频采集、预处理、MFCC特征提取,到基于高斯混合模型(GMM)的建模与验证全流程,并提供了完整的代码实现,帮助开发者快速掌握这项生物特征识别核心技术。

ik6789012的博客 232

机械方面的词汇

b0001 机械制图 technical drawing for mechanical engineering b0002 齿轮制图 technical drawings - representation of gears b0003-1 制图 – 螺丝及螺丝附件 – 第一部分:基本规定 technical drawings-screw threads and thr...

weixin_33895604的博客 3万+

STM32F103跑LVGL,用DMA加速刷屏,800*480大屏也能丝滑

本文详细介绍了如何在STM32F103芯片上通过DMA加速和内存优化,实现800*480大屏的LVGL流畅运行。通过FSMC+DMA硬件架构优化、双缓冲机制和LVGL参数调优,显著提升了界面刷新率和响应速度,适用于嵌入式GUI开发中的高性能需求场景。

weixin_30357231的博客 376

获取http://www.youtube.com/watch?v=30P8DSNOZuU的下载地址(随记)

在临时文件夹中搜索30P8DSNOZuU找到[url]http://www.youtube.com/player2.swf?hl=en&video_id=30P8DSNOZuU&l=456&t=OEgsToPDskJybYUnFJzVfbE8IbDrvS4L&soff=1&sk=Zy5NAZQwn0j1soxQnrdKSgC[/url],那么[url]http://www.youtube.com/...

mathsfan的blog 1万+

高通跃龙QCS6490部署yolov11_obb实战:QNN SDK工具链全解析与避坑指南

本文详细解析了在高通跃龙QCS6490平台上部署yolov11_obb模型的实战过程,重点介绍了QNN SDK工具链的使用与优化技巧。从环境配置、模型转换到性能调优,提供了全面的避坑指南和DSP加速配置建议,帮助开发者高效实现旋转框检测任务在边缘设备上的部署。

weixin_29232121的博客 305

【人工智能】《Deep Dive into LLMs like ChatGPT 》—— Andrej Karpathy 第三阶段:强化学习(Reinforcement Learning, RL)

摘要:强化学习(RL)阶段通过让模型自主生成并验证答案,突破SFT阶段的模仿局限,使其在数学、代码等领域形成独特的推理路径。RL流程包括批量生成解答、自动批改筛选正确路径,并采用PPO/GRPO算法优化模型。模型会自发涌现自我检查、回滚等策略,显著提升正确率(如AIME竞赛准确率从15%升至56%)。与SFT模型相比,RL模型具备主动链式思维、验算能力,响应时间更长但效果更优,形成真正的“思考模型”。三阶段对比显示,RL通过可验证题的对错奖励机制,赋予模型超越人类范式的推理能力。

奶爸的编程之路,也就一周冷个三天 149

手把手教你撸最新Youtube视频 拖拽动画效果

前言 又到了金三银四的季节了,忙的人特别忙,面试啊,加班啊,闲的人也是特别闲吧,就比如我,天天划水,闲的写文章,做动画,同时呢各种新技术在不断的涌进,推动者软件行业的发展,不要焦虑,不要着急,学好本分,再扩展技能。不多说了,给大家看看做的效果吧。 这个是原生的效果 这个是做出来的效果 动画分解 动画分解其实很重要,如果说想模仿一个App的动画及交互效果,一定要仔细的观察他在交互细到放慢每一...

weixin_34146986的博客 1万+

代码的自由之旅:用AI写出无版权烦恼的Verilog

他们还基于这个数据集,训练了一个名叫“FreeV”的AI模型,不仅能写出好用的Verilog代码,还把版权侵权的风险降到了仅3%。为了让AI安全地写Verilog,研究团队提出了三个“法宝”:一个检测AI是否会生成侵权代码的测试方法、一个干净的Verilog数据集FreeSet,还有一个基于FreeSet训练的AI模型FreeV。AI的记忆力超强,能记住训练时见过的代码。测试发现,一些用Verilog数据训练过的AI,比如VeriGen,生成侵权代码的概率从9%涨到了15%,说明它们可能学了不该学的代码。

步子哥的博客 1810

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

教程大纲 版权声明:本文为博主原创文章,未经博主允许不得转载 PS:转载请注明出处作者: TigerChain地址: www.jianshu.com/p/1b3b6b003…本文出自 TigerChain 简书 人人都会设计模式 教程简介 1、阅读对象本篇教程适合新手阅读,老手直接略过 2、教程难度初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢 正文 一、什...

weixin_33923762的博客 530

使用plot函数可视化F分布累积分布函数数据(R语言)

在上面的代码中,我们使用了seq()函数来生成从0到5之间,以0.1为步长的一组F值。然后,我们指定了分子自由度为3,分母自由度为8,并使用pf()函数计算了对应的F分布CDF值。在本文中,我们将介绍如何使用R语言的plot函数来可视化F分布的累积分布函数(CDF)数据。通过使用plot函数,我们可以直观地了解F分布的累积分布函数,并对F值和对应的累积概率之间的关系有更深入的认识。运行上述代码后,我们将获得一个由F值和对应的累积概率构成的图形,其中x轴表示F值,y轴表示累积概率。

LogicGuruX的博客 503

AI自动化时代:一人公司的崛起与商业机会

AI技术正在重塑商业格局,催生"一人公司"创业新模式。这种轻资产、高效率的商业模式通过AI自动化运营,让个人创业者能以极低成本挑战传统企业。核心机会包括:1)AI自主公司替代人力密集型业务;2)为企业提供AI转型解决方案;3)利用AI工具实现个人创业。创业者需快速验证创意,聚焦人机协作,把握当前AI发展窗口期。未来,AI赋能的"一人公司"将成为商业新常态,带来更高的专业化和灵活性。抓住AI红利,学会人机协作,是个人创业成功的关键。

这里汇聚了前沿的技术分享与实用的开发技巧,带你探索从创意到企业的技术创业之路。 520

人人视频android资源比ios多,人人视频

人人视频,国内专业的海外视频社区。人人视频最新版v5.5.1更新说明(2021-05-20)1、首页全新改版,内容更丰富,推荐更精准,你的口味我们知道2、“剧荒”升级为“快看”,告别剧荒,快速找剧,一刷就上瘾!3、支持快看、片单的快速搜索,直达心仪内容4、VIP权益升级,更多优质大片等着你5、字幕全新优化,视频高清画质,尽在人人视频如发现版本老旧,欢迎邮件反馈toususpam#liqucn(do...

weixin_42327217的博客 4万+

可编程逻辑控制器(PLC)系列:Mitsubishi MELSEC-Q_(8).MELSEC-Q系列PLC故障诊断与维护

MELSEC-Q系列PLC的故障诊断与维护是一个系统性的过程,需要工程师具备扎实的理论基础和丰富的实践经验。通过了解故障类型、使用诊断工具、遵循诊断步骤,可以有效地定位和解决故障问题。同时,定期的检查与保养、备份与恢复、优化程序和配置,以及提供良好的运行环境,可以减少故障的发生,提高系统的可靠性。希望本文能够帮助工程师在故障诊断与维护过程中更加得心应手,确保MELSEC-Q系列PLC的稳定运行。

2401_87715305的博客 1247

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

版权声明:本文为博主原创文章,未经博主允许不得转载公众号:TigerChain 更多文章等着你 作者: TigerChain 教程简介 1、阅读对象 本篇教程适合新手阅读,老手直接略过 2、教程难度 初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢 正文 一、什么是代理模式 1、生活中的代理 1、微商代理 代理在生活中就太多...

weixin_33809981的博客 245

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

05-代理模式

代理模式 代理模式的应用场景 ​ 在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(Proxy Pattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的: 保护目标对象 增强...

weixin_39169791的博客 188

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

人人人人人人人人人人人人人人人人人人人

的美国各个各个各个哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈的的点对点的得到的得到的大的地对地导弹ddr人人人人人人人人人人人人人人人很好姐姐姐姐姐姐姐姐姐姐姐姐姐姐姐姐姐姐家基金姐姐姐姐姐姐姐姐姐姐姐姐家基金事实上是收拾收拾是收拾收拾是收拾收拾是收拾收拾是收拾收拾姐姐姐姐姐姐姐姐姐姐姐姐家基金姐姐姐姐姐姐姐姐姐姐姐姐家基金姐姐姐姐姐姐突然人人人人人人人人人人人人人人人人人人人人人人人人人人轻轻轻轻轻轻轻轻

u013053665的专栏 2万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

年底这个项目,人人可干!一人撸几百!

总的来说,年底这个项目为广大人群提供了一个赚取额外收入的机会,无论是学生、上班族还是自由职业者,都可以通过参与各种简单的任务或活动来实现自己的经济目标。然而,需要注意的是,参与任何项目都要谨慎对待,理性选择,避免陷入不良的网络陷阱或欺诈活动中。参与这个项目的人可以选择多种方式来赚取收入。首先,他们可以利用社交媒体平台,如微信、微博、抖音等,发布各种广告或推广信息,赚取推广费用或佣金。其次,他们还可以利用各种在线问卷调查平台,参与各类市场调研或问卷调查活动,完成问卷并提交反馈,从而赚取问卷调查费用。

2301_79616819的博客 2824

最热门的9个超级SEX问题

        无从选择,女人的身体被造物设计成传宗接代的载体,而SEX就是当初那只引诱我们上钩的苹果。不会忘记新生命诞生时,那场撕裂心肺的痛,那是女人完成自己使命的另类礼赞。那个曾经代表忠贞、圣洁的标签,被男人心怀窃喜地撕下,并据为己有,女人从此无法原价跳槽。非洲草原上雄师霸占雌狮的原理也与此相仿。   Q1:一夫一妻制符合人类本性吗?   人是不渴而饮的动物,所以,如果一切交由身体做主,那么性

张彤的专栏 4万+

人人快速开发平台开发文档 纯个人手撸 跟官网的不一样

1、配置 1) 从pom.xml 开始 parent 依赖包(包括springboot的所有依赖及Spring-Mybatis 的整合包,常用工具包); 2)Java环境依赖   Java-version 3) 数据库驱动包 MySQL包; 4) 任务调度依赖 quartz  5) 系统权限框架依赖   shiro 框架集成   6) 配置文件   (1)xml 文件  MySQL 数据库和...

Miccke的博客 1万+

上班假装很忙,下班装逼唬妹子的几个神器,人人都能用

前段时间的热门话题莫过于HM和BCI了,我本来对这种跳梁小丑的什么组织,什么品牌的相关新闻没多大兴趣,但没想到还是被卷进来了。 我叫sabiba,你adidas出事关我什么事? 吃过晚饭,忙完工作,老婆忽然非要让我看一个搞笑的新闻,大致意思是这样的: 很多阿迪达斯赞助的节目都崩溃了,后期根本来不及处理那么多帧的画面,目前唯一的解决办法就是统一镜面反转,“我叫sabiba,你adidas出事关我什么事?” 类似这样的画面: 改成这样的: 然后她又问我:这种镜面翻转是怎么做到的? 我没空理她,因为我在看

xx_nm98的博客 3万+

登陆人人网爬取个人信息

创建scrapy工程 1 2 cd C:\Spider_dev\app\scrapyprojects scrapy startproject renren 创建定向爬虫 1 2 cd renren scrapy genspider Person renren.com...

donghan4637的博客 1541

色不迷人人自迷,情不伤人人自伤

色不迷人人自迷,情不伤人人自伤   从前有两个和尚走到了一条河边,他们准备过河的时候,来了一位女子也要过河去,由于河水比较大,老和尚就背起这个女子一同过河,到了河对岸之后,老和尚放下这个女子,两个和尚继续赶路。小和尚走着路,心里却一直在想着什么,他忍不住问老和尚:师父,你不是说出家人不近女色吗?你为什么要背她过河呢?老和尚回头看了看小和尚:"过了河我就把她放下了,可你为什么到现在还放不下呢?"这个

7万+

D2Admin 人人企业版适配发布

使用 D2Admin 适配 人人企业版(专业版) 的前端工程。实现所有人人企业版前端功能,并保留绝大部分 D2Admin 特性。 (如果你不知道人人企业版是什么,请参考:www.renren.io/。这是一个基于Spring、MyBatis、Shiro框架,开发的一套后台权限系统脚手架,极低门槛,拿来即用。支持分布式部署、Quartz分布式集群调度、部门管理、数据权限、云存储等功能。) 项目地址 ...

weixin_34198797的博客 678

人人都爱A-B 待改

#include #include #include <string.h> #include <math.h> using namespace std; int main(){ int n,m; int judge; int a[2][10000]={0},b[10000]; while(1){ judge=0; scanf("%d%d",&n,&m);...

Decmxj1229的博客 109万+

看一个人是否快乐,不要看笑容

看一个国家的国民教育,要看他的公共厕所。看一个男人的品味,要看他的袜子。看一个女人是否养尊处优,要看她的手。看一个人的气血,要看他的头发。看一个人的心术,要看他的眼神。看一个人的身价,要看他的对手。看一个人的底牌.要看他身边的好友。看一个人的性格,要看他的字写得怎样。看一个人是否快乐,不要看笑容,要看清晨梦醒时的一剎那表情。看一个人的胸襟,要看他如何面对失败

§为艺术而技术§ 2695

读人、看人、做人(图)

读人 一个人就是一本书。读人,比读其他文字的书更难。在生活中,每个人都是书,每人又都是读者。我们认认真真地读,读了大半辈子,至今还没有读懂这本“人之书”。有首歌词是这样的:“明明白白我的心,渴望一份真感情”。每个人都渴望自己被人了解,被人接受,但事实上最难懂的就是人。 有的人,在阳光明媚的日子里愿意把伞借给你,而下雨的时候,他却打着伞悄悄地先走了。——你读他时,千万别埋怨他。因为他自己不愿

szu030606的专栏 1876
上一篇: 04、人人都会设计模式:适配器模式--Adapter
下一篇: 06、人人都会设计模式:观察者模式--Observer
TigerChain
博客等级 码龄14年 15粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值