备注

原发表于2016.05.21,资料已过时,仅作备份,谨慎参考

代理模式是什么

如上图所示,代理代表着另一终端中的某个真实服务对象,Client 调用代理(Client helper)的方法,然后将请求通过网络与真正的服务对象进行沟通。

例如 Windows 的快捷方式就是一种代理,用户点击快捷方式,认为自己在跟实际应用交流,实际上是快捷方式去调用了真正的应用程序。

代理模式在 Java RMI 的应用

RMI 是什么 ?

RMI(Remote Method Invocation)是 J2SE 的一部分,使用它能开发出基于 Java 的分布式应用。一个 RMI 对象可以像调用本地 Java 对象的方法一样调用远程对象的方法,使分布在不同的 JVM 中的对象的外表和行为都像本地对象一样。

RMI 使用 jrmp(Java Remote Messaging Protocol)进行通信,采用 tcp/ip 协议,且只能基于 Java 语言。

RMI 如何实现代理模式?

RMI 提供了客户辅助对象(Stub)和服务辅助对象(Skeleton),为这两者创建了相同的方法。RMI 远程调用的过程图如下所示:

RMI 方法调用过程

  1. 客户对象调用 Stub 的方法
  2. Stub 打包信息,通过网络发送给 Skeleton
  3. Skeleton 解包信息,找出被调用的方法和对象并调用
  4. 服务对象调用方法,将结果返回 Skeleton
  5. Skeleton 打包信息,通过网络发送给 Stub
  6. Stub 解包信息,将结果返回给客户对象

RMI 搭建 RMI 服务

制作远程接口

远程接口会被 Stub,Skeleton 和真实服务对象实现,这样客户对象就能知道服务对象都有哪些方法。

在不同终端都需要创建远程接口类。

远程接口要继承自 Remote 接口,用于标识所包含的方法能被远程调用,如下所示:

public interface MyRemote extends Remote {
public String sayHello() throws RemoteException;
}
创建远程接口实现类

服务对象就是远程接口实现类,是真正执行方法提供服务的类。需要实现远程接口,如下所示:

public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
return "Server says Hello";
}
生成 Stub 和 Skeleton

rmic 是 JDK 内的一个工具,用来服务对象生成 Stub 和 Skeleton,是在服务端代码的基础上生成的,所以需要 MyRemoteImpl.class 文件。

%rmic MyRemoteImpl

这样就 rmic 就会生成两个服务对象,MyRemoteImpl_Stub.class 和 MyRemoteImpl_Skel.class。

启动 RMI 注册服务(RMI Registry)

RMI Registry 类似于电话簿或计算机网络中的 DNS 功能,服务对象在此注册,客户对象则在此寻找需要的 Stub。

启动RMI Registry:

%rmiregistry
注册并启动服务

服务对象必须在 RMI Registry 注册后,才能被客户对象找到。

注册服务:

try {
MyRemote service = new MyRemoteImpl();
Naming.rebind("RemoteHello", service);
} catch (Exception ex) {...}

启动服务:

%java MyRemoteImpl
客户对象获取 Stub 并调用方法

同样的,客户对象从 RMI Registry 中取得 Stub,就可以调用方法了。

try {
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/RemoteHello");
String s = service.sayHello();
} catch (Exception ex) {
ex.printStackTrace();
}

RMI 搭建的注意事项

学习了解 RMI 实现远程代理的方式,有很大的借鉴意义,因为在 Android IPC 通信中,就能够看到许多 RMI 的影子。

同时要注意, 调用方法的『参数』和『返回值』都必须是可序列化的类型,因为这些变量都会被打包通过网络传输。

代理模式在 Android 的应用

Android 源码中有许多关于代理模式的实现,这里主要了解 Android 中的 Binder 机制和 AIDL。这两者都是 Android 跨进程通信机制中十分重要的概念。

Binder 跨进程通信机制

Binder 概述

在计算机中,为使两个不同进程中的对象能够互相访问,需要使用到跨进程通信技术。传统的跨进程通信机制有:Socket,内存共享,消息队列等。

由于传统的跨进程通信机制,开销大安全性低,Android 自己设计了一个新的通信机制:Binder。

Binder 基于 Client-Server 通信模式,为发送方添加 UID/PID 身份,在确保传输性能的同时又提高了安全性。

Binder 的四大模块

Binder 涉及到的四个主要模块分别是 Binder Client、Binder Server、ServerManager 和 Binder Driver。

  • Binder Client 相当于客户端
  • Binder Server 相当于服务器
  • ServerManager 相当于 DNS 服务器
  • BinderDriver 相当于路由器

其中 Binder Driver 实现于内核空间中,其余三者实现于用户空间中。

Binder Driver

Binder Driver 主要负责 Binder 通信的建立,以及其在进程间的传递和 Binder 引用计数管理/数据包的传输等。Binder Client 和 Binder Server 之间的跨进程通信统一通过 Binder Driver 处理转发。

Binder Client

Binder Client 只需要知道自己要使用的 Binder 的名字及其在 ServerManager 中的引用即可获取该 Binder 的引用,得到引用后就可以像普通方法调用一样调用 Binder 实体的方法。

Binder Server

Binder Server 在生成一个 Binder 实体时会为其绑定一个名称并传递给 Binder Driver,Binder Driver 会在内核空间中创建相应的 Binder 实体节点和节点引用,并将引用传递给 ServerManager。

ServerManager 会将该 Binder 的名字和引用插入一张数据表中,这样 Binder Client 就能够获取该 Binder实体 的引用,并调用上面的方法。

ServerManager

ServerManager 相当于 DNS服务器,负责映射 Binder 名称及其引用。其本质同样是一个标准的 Binder Server。

ServerManager 和在 Binder Client 端的 ServiceManagerNative、ServiceManagerProxy 都实现了 IServiceManager 接口,该接口定义了 ServerManager 对外公布的方法。这样 Client 就能通过这些方法获得 Binder 引用。

Binder 的总结

从 Binder 的四个模块可以看出其与 Java RMI 有着许多相似之处,但其实 Android 的 Binder 机制是一个庞大的体系模块,其实现要复杂多很多。

当我们想使用 Binder 进行进程间通信时,Android 已将 Binder Driver 和 ServerManager 封装得很完美了,我们只需实现自己的 Binder Client 和 Binder Server 即可。

Android 提供了 AIDL 这种简便的方式来快速实现 Binder。

AIDL

Android 接口定义语言,使用它能够快速实现 Binder 来进行进程间通信。

AIDL 的使用流程

使用 AIDL 来进行进程间通信的流程,分为服务端和客户端两个方面

  1. 服务端:服务端要创建 Service 来监听客户端的请求,创建一个 AIDL 文件声明公开的方法。在这里 AIDL 文件相当于『远程接口』,Service 相当于『服务对象』。

  2. 客户端:客户端需要绑定服务端的 Service,并将服务端返回的 Binder 引用转成 AIDL 接口所属的类型。

AIDL 的具体实现

创建 AIDL 接口
// IHelloManager.aidl
package com.melon.aidl; interface IHelloManager {
String sayHello();
}

需要注意,AIDL 的包结构在服务端和客户端要保持一致,因为客户端需要反序列化服务端中和 AIDL 接口相关的所有类。

项目 Build 之后系统会根据 IHelloManager.aidl 生成一个 Binder 类。

Binder 类内部包含一个 Stub 类和 Proxy 类。

值得注意的是,在这里生成的 Stub 相当于 『远程接口实现类的抽象类』,在 Service 中将实现 Stub 中的抽象方法。

而 Proxy 则是提供给客户对象的代理类。

实现远程服务端 Service

先创建一个 Service,创建一个 Binder 对象实现 Stub 中的内部方法并在 onBind() 中返回它。

private Binder mBinder = new IHelloManager.Stub() {
@override
public String sayHello() throws RemoteException {
return "server says hello";
}

可以看到,服务端 Service 内的 mBinder 才是真正提供服务,执行方法的对象。

实现客户端

客户端只要绑定连接 Service,将服务端返回的 Binder 对象转化为 AIDL 接口,就能够调用服务端的方法了。关键代码如下:

IHelloManager HelloManager = IBookManager.Stub.asInterface(service);
String hello = HelloManager.sayHello();

可以看到,连接成功后,asInterface() 会返回 Proxy 代理对象。之后再将 Proxy 转化成 IHelloManager 接口。

这样我们在客户端就能够远程调用在不同进程里的方法了。

全文总结

本文对代理模式,RMI,AIDL 做了简单的介绍,主要是对书本和网络的资料进行整合,并尽量用简洁的方式表达出来。在撰写文章时,对 AIDL 有了更为深入的了解。

对书本内容的认识可能会有出错的地方,希望各位读者能够指出文章中的错误,第一次写技术总结,也希望各位对文章陈述的结构给出指导意见。

参考资料

  1. RMI(Remote Method Invocation)原理浅析

  2. Java RMI详解

  3. 设计模式之代理模式(Proxy Pattern)远程代理解析

  4. 《 Android 开发艺术探索 》

  5. 《 Android 源码设计模式解析与实战 》

[旧][Android] 代理模式的更多相关文章

  1. 理解Android系统的进程间通信原理(一)----RPC中的代理模式

    Android系统中的进程间通信是通过一个轻量级的RPC(Remote Procedure Call远程进程调用)和AIDL(Android Interface Definination Langua ...

  2. 代理模式与Android

    代理模式(Proxy) 一.   什么是代理模式 先来看看官方的说法,代理模式就是为其它对象提供一种代理,以控制对这个对象的訪问. 看来这个官方的说法的确有点官方,看了还是让人感觉不点不知所措,还是不 ...

  3. Android开发中无处不在的设计模式——动态代理模式

    继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...

  4. Android中的代理模式

    代理的概念:为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代.代理类负责请求的预处理.过滤.将请求分派给委托类处 ...

  5. Android设计模式之代理模式

    代理模式: 为其他对象提供一种代理以控制对这个对象的访问 Subject类定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy inte ...

  6. (转 )【Android那些高逼格的写法】InvocationHandler与代理模式

    转自这个公众号: 今天会聊一下InvocationHandler.说到InvocationHandler不得不提到的就是代理模式,什么是代理模式,举个例子,你玩游戏,花钱请个代练,代练其实是登录你的账 ...

  7. 大话设计模式C++版——代理模式

    本篇开始前先发个福利,程杰的<大话设计模式>一书高清电子版(带目录)已上传至CSDN,免积分下载. 下载地址:http://download.csdn.net/detail/gufeng9 ...

  8. 结构型:代理模式 Vs 适配器模式 Vs 门面模式(外观模式)

    先上UML图 代理模式: 适配器模式: 门面模式(外观模式): 打了例子……呃……举个比方 代理模式: 水浒街,西门庆看上潘金莲,想和她嘿咻嘿咻,但是自己有不能去找潘金莲去说,于是他找到了金牌代理人王 ...

  9. Proxy 代理模式

    简介 代理模式是用一个简单的对象来代替一个复杂的或者创建耗时的对象. java.lang.reflect.Proxy RMI 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对 ...

随机推荐

  1. 【vps】如何在vps上安装mirai机器人?

    [vps]如何在vps上安装mirai机器人? 前言 由于某位师傅在群里设置了一个bot,吸引了我,所以我之前找他问了点bot的相关知识,这几天正好服务器搬迁,所以就在新服务器上再装一遍bot 1.安 ...

  2. virtual stuido同时调试多个控制台

    问题 UDP作业需要服务器端和客户端收发信息完成交互,需要同时调试多个窗口. 解决办法 但是缺点依然是无法调试2个,修改另一个测试. 所以多开可能依然是好办法.

  3. Go 变量及基本数据类型2

    #### Go 变量及基本数据类型(二)今天学习一下基本数据类型,包括整数类型,小数类型,字符类型##### 整数类型用来存放整数数值的, 如0,1,-10,2222等; 整数型的类型有: 类型 有无 ...

  4. python函数关键字实参传参

    #!/usr/bin/python #coding=utf-8 #好好学习,天天向上 def describe_pet(type,name): print(f"i have a {type} ...

  5. 鸿蒙轻内核源码分析:文件系统LittleFS

    摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...

  6. 多种方式告诉你如何计算DM同步数据到TiDB的延时时间

    背景 用户在做技术选型的过程中,总是会对一些数据指标比较关心,特别是在和竞品相比较的时候,更加需要一些有说服力的数据.基于MySQL开发的项目在迁移到TiDB的时候,使用DM同步数据是必不可少的一个环 ...

  7. Java.lang.Math类详解

    Math类位于Java.lang包中,包含用于执行基本数学运算的方法!Math类的所有执行方法都是静态方法,可以直接使用类名.方法名调用,如:Math.round() 常用的方法:Math.round ...

  8. Java泛型T与?

    感谢大佬:http://m.mamicode.com/info-detail-2657551.html 一.区别 单独的T 代表一个类型 ,而 Class<T>代表这个类型所对应的类, C ...

  9. EasyExcel小试牛刀

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/12029411.html 某种偶然的机会遇到了这个插件, 听说很牛X, 我之前也不知道, 不过还 ...

  10. Docker consul的容器服务更新与发现

    Docker consul的容器服务更新与发现 目录 Docker consul的容器服务更新与发现 一.Consul简介 1. 服务注册与发现 2. consul概述 3. consul的两种模式 ...