haohao

Android 设计模式之代理模式

Markdown

生活中的很多烦恼都源于盲目的攀比,而忘了享受自己的生活

本文是 Android 设计模式的第五篇,接下来会陆续推出整个常用的设计模式系列。
本篇主要介绍一下在 Android 开发中无所不在的一种设计模式 – 代理模式。说它无所不在,主要是因为 Android 四大组件的创建和使用均会涉及与 Framework 层进行频繁的 IPC ,而 Binder IPC 模型使用的就是代理模式。

代理模式

代理模式(Proxy Pattern):为访问对象提供一个代理对象来实现对被访问者的访问,其实就是在访问对象与被访问对象之间添加一个中介,用来隔离访问者与被访问者的具体实现细节。它是一种结构型设计模式。

模式结构

  • 抽象对象:声明了目标对象和代理对象的共同接口;
  • 目标对象:被代理或者被访问的对象;
  • 代理对象:代理对象内部持有目标对象的引用,代理对象与目标对象实现相同的接口, Client 访问代理对象相当于间接访问目标对象。

图片来自于网络

模式实现

下面是一个非常简单的例子。

抽象对象

1
2
3
interface CommonInterface {
fun operate(str: String)
}

目标对象

1
2
3
4
5
class SubjectObj : CommonInterface {
override fun operate(str: String) {
println("目标对象 : $str")
}
}

代理对象

1
2
3
4
5
6
class ProxyObj(private val subjectObj: SubjectObj) : CommonInterface {
override fun operate(str: String) {
println("代理对象 : 调用目标对象")
subjectObj.operate(str)
}
}

Client

1
2
3
4
5
6
fun main(args: Array<String>) {
val subjectObj = SubjectObj()
val proxyObj = ProxyObj(subjectObj)
proxyObj.operate("执行操作")
}

Running

1
2
代理对象 : 调用目标对象
目标对象 : 执行操作

Android 中的代理模式

Android Binder 类是实现进程间通信的媒介,由于进程间通信贯穿四大组件使用的始末,所以在 Android 开发过程中进程间通信随处可见,而 Android Binder IPC 模型是基于代理模式。

Android Binder IPC 通信模型:

  • Client : 持有 Server 的本地 Binder 对象的代理对象;
  • Server : 持有本地 Binder 对象,为 Client 端提供功能性服务;
  • ServiceManager : 负责管理 Binder 服务,可以根据 Binder Name 获取 Binder 引用,功能类似于 DNS 服务器;
  • Binder 驱动 : Client 与 Server ,以及 Server 与 ServiceManager 之间的通信都会经过 Binder 驱动,维持 Binder Proxy 与 Binder 实体引用之间的映射,根据 Client 端由 Binder Proxy 打包发送过来数据包,调用 Server 方法,再将返回结果打包由 Binder Proxy 传回 Client 端。

下面实现一个最简单的 Binder IPC 模型:

抽象对象接口

1
2
3
4
public interface RemoteCompute extends android.os.IInterface {
// 实现一个远程加法调用,并将结果返回给 Client 端
public int remoteAdd(int arg1, int arg2) throws android.os.RemoteException;
}

目标对象(Binder 本地对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Stub 就是 Binder 本地对象,它的方法是在 Server 进程中执行。
public static abstract class Stub extends android.os.Binder implements RemoteCompute {
private static final java.lang.String DESCRIPTOR = "cn.haohao.ipc.ICompute";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.haohao.ipc.ICompute interface,
* generating a proxy if needed.
*/
public static RemoteCompute asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof RemoteCompute))) {
return ((RemoteCompute) iin);
}
return new RemoteCompute.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_remoteAdd: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.remoteAdd(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_remoteAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 在 IPC 时 Proxy 对象只是持有 Binder Proxy 对象的引用,严格来说它是间接的代理。
// Proxy 负责打包 Client 端的请求参数,发送给 Binder 驱动,由 Binder 驱动调用 Server 端本地 Binder 对象解析数据并执行对应的操作,同时 Client 端线程挂起,当 Server 端方法执行完毕后,再将返回结果打包,通过 Binder 驱动传回到 Client 端的 Binder Proxy,Binder Proxy 会解析数据包中的内容并将原始结果返回给 Client 端。
private static class Proxy implements RemoteCompute {
private android.os.IBinder mRemote; // IPC 时由 Binder 驱动返回的 Binder Proxy (Binder 代理对象)
Proxy(android.os.IBinder remote) {
mRemote = remote;
Log.d(TAG, "Proxy: remote : " + mRemote);
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int remoteAdd(int arg1, int arg2) throws android.os.RemoteException {
Log.d(TAG, "remoteAdd() called with: arg1 = [" + arg1 + "], arg2 = [" + arg2 + "]");
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(arg1);
_data.writeInt(arg2);
mRemote.transact(Stub.TRANSACTION_remoteAdd, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 在 IPC 时,service 就是 Binder 驱动返回的 Binder Proxy 对象的引用。
remoteCompute = RemoteCompute.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
// 调用远程方法
int result = remoteCompute.remoteAdd(88, 12);

代理模式的优点和缺点

优点

  • 降低访问者与被访问者之间的耦合度
  • 可以控制访问者对被访问者的访问权限
  • 虚拟代理通过使用一个小对象来代理一个大对象,可以减少系统开销

缺点

  • 代理对象会增加逻辑的复杂度
  • 减慢对目标对象的访问速度


联系我

Wechat ID

公众号

生活不止于眼前的苟且, 还有诗和远方的田野