- 1. 前言
- 2. 远程代理(Remote Proxy)
- 3. 后记
在上篇Android常见设计模式——代理模式(Proxy Pattern)中基本上知道了什么是代理模式,以及对应的应该如何写代理模式的代码,了解了在Retrofit中没有被代理者的代理模式。在这篇中将继续围绕代理模式进行展开。在《Android源码设计模式》一书中提到:静态代理和动态代理是从编码的角度来区分代理模式的方式,同时也可以从其使用范围来区分不同类型的代理模式:
- 远程代理(Remote Proxy):为某个对象在不同的内存地址空间提供局部代理。使系统可以将Server部分的实现隐藏,以便于Client可以不考虑Server的存在。
- 虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建。
- 保护代理(Protection Proxy):使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同的访问权限的情况。
- 智能引用(Smart Reference):在访问原始对象时执行一些自己的附加操作。
在上文中提到过Binder机制中使用了代理模式,具体体现在AIDL中使用了代理机制,下面来看看在Android中AIDL的介绍,可以查询官方文档Android 接口定义语言 (AIDL):
AIDL(Android Interface Definition Language)是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,以便二者使用进程间通信 (IPC,Inter-Process Communication) 进行相互通信。
在 Android 中,一个进程通常无法访问另一个进程的内存(Linux进程隔离机制)。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 为您处理此问题。而这个转换过程无需开发人员自己编写,由Android编译器生成,所以这里也采用了代理模式。
2. 远程代理(Remote Proxy)进程的隔离实现,使用了虚拟地址空间。进程 A 的虚拟地址和进程B的虚拟地址不同,这样就防止进程 A 将数据信息写入进程 B。
在Android系统中,远程代理的设计实现就是AIDL。我们知道在需要进行进程间通信的时候,我们通常需要借助Binder机制,而对应的使用过程就需要使用AIDL,比如当我们使用new->AIDL->AIDL File来创建一个AIDL文件,这里我的文件内容为:
// IMyAidlInterface.aidl
package com.weizu.myapplication;
interface IMyAidlInterface {
int sum(int a, int b);
}
然后Build->Make Project来进行编译下,切换项目视图为Project可以看到生成的对应的接口文件:
打开这个文件,可以看到使用了静态代理模式,不妨简单的折叠部分对应的实现:
从上图可以发现其中①②③都实现了我们定义的接口,并且在外部最外层接口定义处给了原本的接口申明④。突然发现一件事情那就是:接口中可以定义静态的类!
测试一下:
public interface jjj{
class Default{
public int sum(int a, int b) {
return a + b;
}
}
}
public static void main(String[] args) {
int sum = new jjj.Default().sum(23, 4);
System.out.println(sum);
}
结果:
尽管这里我没有实现接口,但是因为接口中默认为public static类型修饰,所以这里可以直接获取到其内部静态类的引用。
继续回到主题。这里查看Porxy这个类:
可以看到分为两步,分别是参数装填、解析表达式然后返回结果。和Retrofit中类似的是这里同样没有出现被代理者,感觉很像是一个简单的实现。那么不妨将这个进程间通讯的案例写完整。
再次创建一个Service服务,我们知道在服务的绑定onBind的方法调用后会返回一个Binder接口,而在前面自动生成的idal文件的接口代码中,在②中看到在生成的IMyAidlInterface的接口的静态内部类Stub继承自android.os.Binder这个类,所以我们可以在自定义服务的onBinder方法中返回我们定义的接口的Stub对象就可以实现真正接口的实现,即RealSubject对象:
public class MySumService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
static class MyBinder extends IMyAidlInterface.Stub{
@Override
public int sum(int a, int b) throws RemoteException {
return a + b;
}
}
}
然后注册MySumService,这里指定Service在不同的进程中:
然后简单的测试使用Binder:
public class TestActivity extends AppCompatActivity implements View.OnClickListener {
private Button button;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
button = findViewById(R.id.button);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent(TestActivity.this, MySumService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将Binder对象转为IMyAidlInterface接口对象
IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
// 调用
try {
int sum = iMyAidlInterface.sum(20, 3);
button.setText("结果为:" + sum);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
}
}
在方法onServiceConnected中,使用IMyAidlInterface.Stub.asInterface(service)来将一个Binder对象转换为接口对象,不妨来看看这个代码是如何做的:
public static com.weizu.myapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DEscriptOR);
if (((iin!=null)&&(iin instanceof com.weizu.myapplication.IMyAidlInterface))) {
return ((com.weizu.myapplication.IMyAidlInterface)iin);
}
return new com.weizu.myapplication.IMyAidlInterface.Stub.Proxy(obj);
}
因为是首次调用,所以会直接走return语句,也就是调用前面分析过的Proxy类,传入这个Binder实例。而我们知道在自定义Service的onBind方法返回的是RealSubject的实际实现,所以这个Binder中其实也就实现了自定义的sum方法。所以在Proxy构造方法中传入的这个Binder其实也就是实际的被代理者。也就构成了我们的代理模式。
3. 后记写到这里越发的发现自己之前对于Binder、AIDL、AMS等语法的知识匮乏,所以在接下来的日子里就来继续看看这些部分的知识,争取能学有所成。
References
- Android 接口定义语言 (AIDL)
- Android常见设计模式——代理模式(Proxy Pattern)
- Binder的入门案例体验
- 《Android源码设计模式》



