https://github.com/Xiaofei-it/Hermes/blob/master/README-ZH-CN.md
AIDL是Android跨进程通信的重要手段,其中核心思想就是“动态代理”。
动态代理:利用Java的反射技术,在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类(Abstract Class)。
1、代理模式
(1)静态代理
假设有一个对象, 定义为:
public interface SpeakInterface {
// 说英语
void speakEnglist();
// 说中文
void speakChinese();
}
public class Speak implements SpeakInterface {
@Override
public void speakEnglist() {
System.out.println("说英语");
}
@Override
public void speakChinese() {
System.out.println("说中文");
}
}
Speak
对象的给出的能力就是speakEnglist
和speakChinese
,我们可以直接使用如下代码直接执行该能力
// 上转型对象,使用多态特性
SpeakInterface speak = new Speak();
speak.speakEnglist();
speak.speakChinese();
在某些特定的场景下,需要一个其它类代为执行speakEnglist
和speakChinese
,代码如下:
public class SpeakProxy implements SpeakInterface {
private SpeakInterface speak = new Speak();
@Override
public void speakEnglist() {
speak.speakEnglist();
}
@Override
public void speakChinese() {
speak.speakChinese();
}
}
SpeakProxy
就是代理对象,使用代理对象实现Speak
的能力
// 上转型对象,使用多态特性
SpeakInterface speak = new SpeakProxy();
speak.speakEnglist();
speak.speakChinese();
这就是静态代理
。
静态代理
的特性:
- 委托对象(Speak)和代理对象(SpeakProxy)必须实现相同的接口(SpeakInterface);
- 代理对象(SpeakProxy)需要拿到委托对象(Speak),由委托对象(Speak)来实现原有的能力;
(2)动态代理
静态代理
需要手动定义代理类(SpeakProxy),然而动态代理
是通过
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
来创建的,其底层是通过反射机制来获取代理对象, 代码实现如下:
SpeakInterface speakInterface = new Speak();
SpeakHandler handler = new SpeakHandler(speakInterface);
SpeakInterface speak = (SpeakInterface) Proxy.newProxyInstance(speakInterface.getClass().getClassLoader(), speakInterface.getClass().getInterfaces(), handler);
speak.speakChinese();
private static class SpeakHandler implements InvocationHandler {
private SpeakInterface speakInterface;
public SpeakHandler(SpeakInterface speakInterface) {
this.speakInterface = speakInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(speakInterface, args);
}
}
Proxy.newProxyInstance
的核心代码是利用反射机制,获取类、接口、方法,并通过invoke方法执行对应的方法。
既然是反射,那么可以尽量使用反射机制完善下代码:
// 从远程传递过来的className字符串
String className = Speak.class.getName();
Class<SpeakInterface> clazz = null;
Object object = null;
try {
clazz = (Class<SpeakInterface>) Class.forName(className);
Constructor constructor = clazz.getDeclaredConstructor();
object = (SpeakInterface) constructor.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if (clazz == null) {
return;
}
if (object == null) {
return;
}
SpeakHandler handler = new SpeakHandler(object);
SpeakInterface speak = (SpeakInterface) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
speak.speakChinese();
private static class SpeakHandler implements InvocationHandler {
private Object object;
public SpeakHandler (Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object, args);
}
}
2、封装AIDL
AIDL是Android自带的跨进程通信架构,为了实现更加优雅的跨进程通信实现方式,需要对AIDL进行重新封装。
【第一步】
依赖
通信双方需要添加依赖
在项目根目录的build.gradle中引入maven
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
在对应模块的build.gradle中引入依赖
implementation 'com.github.NoBugException:IPCBusDemo:1.0.1'
【第二步】
初始化上下文
// 初始化
IPCBus.getDefault().init(getApplicationContext(), "com.ipcbus.ipcbusservice");
IPCBus.getDefault().init(getApplicationContext(), "com.nobug.ipcbusservice2");
IPCBus.getDefault().init(getApplicationContext(), getPackageName());
初始化的操作最好放在自定义Application中的onCreate中。
除了指定上下文之外,还需要指定通信目标的包名
。
/**
* 初始化
* @param mContext mContext
*/
public void init(Context mContext, String servicePackageName) {
this.mContext = mContext;
// 连接
connect(null, servicePackageName);
}
初始化的时候传递包名是为了连接远程服务
,保证通信的通道是连通的。
【第三步】
发送和响应
Request request = new Request();
request.setRequestName("subtraction");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("parameter1", 5);
jsonObject.put("parameter2", 4);
} catch (JSONException e) {
e.printStackTrace();
}
request.setParamJson(jsonObject.toString());
// 发送消息
Responce responce = IPCBus.getDefault().send(request);
if (responce != null) {
Toast.makeText(MainActivity2.this, "减法结果:" + responce.getData(), Toast.LENGTH_SHORT).show();
}
对远程服务发送相关数据,远程服务根据这些数据响应结果。
【第四步】
可以在适当的释放资源
// 断开连接
IPCBus.getDefault().unConnected(getPackageName());
【第五步】
远程进程需要注册Service
<!--定义服务-->
<service android:name="com.noexception.ipcbus.service.IPCBusService" />
需要注意的是,name="com.noexception.ipcbus.service.IPCBusService"
是固定写法,它被封装在依赖中。
【第六步】
远程进程还需要注册接收类
// 注册接收类
IPCBus.getDefault().regist(MyReceive.class);
这个操作最好在自定义Application的onCreate中,用于接收请求消息。
/**
* 服务端侧申明接口的地方
*/
public class MyReceive implements ICPReceive {
@Override
public Responce receive(Request request) {
Responce responce = null;
if ("addition".equals(request.getRequestName())) { // 加法运算
responce = new Responce();
responce.setResultCode(ResultCode.SUCCESS_CODE);
try {
JSONObject jsonObject = new JSONObject(request.getParamJson());
int parameter1 = jsonObject.optInt("parameter1");
int parameter2 = jsonObject.optInt("parameter2");
responce.setData(String.valueOf(parameter1 + parameter2));
} catch (JSONException e) {
responce.setData("数据异常");
}
} else if ("subtraction".equals(request.getRequestName())) { // 减法运算
responce = new Responce();
responce.setResultCode(ResultCode.SUCCESS_CODE);
try {
JSONObject jsonObject = new JSONObject(request.getParamJson());
int parameter1 = jsonObject.optInt("parameter1");
int parameter2 = jsonObject.optInt("parameter2");
responce.setData(String.valueOf(parameter1 - parameter2));
} catch (JSONException e) {
responce.setData("数据异常");
}
} else {
// 其它
}
return responce;
}
}
MyReceive的receive方法给发送法响应数据。
代码的具体细节可以查看项目:
https://github.com/NoBugException/IPCBusDemo
[本章完...]
网友评论