备注

原发表于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. 【reverse】逆向7 堆栈图

    [reverse]逆向7 堆栈图 前言 本章就是开始画堆栈图来打基础拉,堆栈熟悉了之后就可以开始C语言的逆向了. 这一章使用的exe文件,我已经上传到了我的个人网盘中,点击下载 1.准备工作 先看这张 ...

  2. 虚拟化架构与Centos7系统部署

    1.什么是虚拟化(Virtualization) 虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机.在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可 ...

  3. c#重写和多态

    多态是基于重写的 继承:向子类中添加父类没有的成员,子类对父类的横向扩展 重写:纵向扩展,成员没有增加,但成员的版本增加了 引言 Rider JetBrains:Rider.ReSharper.dot ...

  4. Node内部架构图

    1.Node内部架构图 先来看一下Node节点的内部实现架构图. 首先最上层入口是Restful风格和javaTcp风格的API入口,RestFul请求映射到处理器RestControl.JavaAp ...

  5. gin中的多模板和模板继承的用法

    1. 简单用法 package main import ( "github.com/gin-contrib/multitemplate" "github.com/gin- ...

  6. java多态概述特点转型I

    1 package face_09; 2 3 import com.sun.jdi.Method; 4 5 /* 6 * 对象的多态性. 7 * class 动物 8 * {} 9 * 10 * cl ...

  7. 集合框架-工具类-JDK5.0特性-函数可变参数

    1 package cn.itcast.p4.news.demo; 2 3 public class ParamterDemo { 4 5 public static void main(String ...

  8. Power Apps 创建响应式布局

    前言 我们都知道Power Apps作为低代码平台,最大的优势就是各个设备之间的兼容性,尤其是自带的响应式布局,非常好用. 这不,我们就为大家分享一下,如何使用Power Apps画布应用,创建响应式 ...

  9. uni微信小程序优化,多个分包在用的公共代码该放在哪?

    公共的代码包括公用的vue组件和js代码,从维护性的角度来说应该放到主包才对, 但是主包有大小限制,如果把2个分包都在用的代码放到主包里面那2M很快就满了. 所以该放在哪?我的方案是从维护的角度放在主 ...

  10. iBooker AI+财务提升星球 2020.4 热门讨论

    比特币量化套利的心路历程(附python量化招聘)(分享自知- 如何选择一份好的工作? 你知道为什么大家都想去好公司吗? 不- #财务知识# 可转债套利 辉丰转债128012套利之三个知道- #财务知 ...