今天上午,看票圈有朋友分享爱奇艺的跨进程通讯框架——Andromeda,觉的仍是有点意思的。 之前项目中用到跨进程这种解决方案比较少,今天看了下Andromeda,发现调用方式很简单。git
刚好最近一年都是在作后端工做,想到了Json RPC的方案,其实Android跨进程接也是一种rpc调用方式,那么参考json rpc协议,经过aidl通道也能够很简单一种跨进程通讯方式,并且使用方式也很简单。程序员
说干就干,可是做为了高级程序员,确定要给项目起个名字高大上的名字——Bifrost(彩虹桥),参考复联电影雷神上面的彩虹桥,寓意能够传送到各地,也表达Android跨进程通讯能够畅通无阻。github
在Android系统的跨进程调用须要用到AIDL方式,可是呢,操做起来很是麻烦,能够传递基本类型,若是须要自定义类,那么还须要实现Parcelable接口,同时也要写很多代码,操做起来繁琐。json
像日常同样,先定义一个接口和实现类就好了。后端
public interface INumberApi {
int add(int a, int b);
}
复制代码
public class NumberApiImpl implements INumberApi {
@Override
public int add(int a, int b) {
return a + b;
}
}
复制代码
注册下接口和实现类,由于暂时没有用到依赖注入工具,同时我也不想把功能作的很复杂,暂时手动注册吧,作注册前,先作好初始化工做。api
Bifrost.getInstance().init(this);
Bifrost.getInstance().register(IUserApi.class, new UserApiImpl());
Bifrost.getInstance().register(INumberApi.class, NumberApiImpl.class);
复制代码
Bifrost暂时支持2个注册方式,kv都是class类型,还有就是k是class,v是接口实现类的一个对象。bash
调用方式也很简单。微信
IUserApi userApi = Bifrost.getInstance().getRemoteInstance(IUserApi.class);
User user = userApi.login("admin", "123456");
Timber.i("user = %s", user);
INumberApi numberApi = Bifrost.getInstance().getRemoteInstance(INumberApi.class);
int ret = numberApi.add(1, 2);
Toast.makeText(getApplicationContext(), "1 + 2 = " + ret, Toast.LENGTH_LONG).show();
复制代码
原理很简单,见下图所示。并发
当在原始的进程中,定义一个接口,而后获取该对象的时候,其实返回值是一个用Java动态代理实现的一个值,当有使用方调用接口中的方法时候,会构形成一个RpcRequest对象,这个对象很简单,就是标识这个调用的必要信息。框架
public class RpcRequest {
@SerializedName("jsonRpc")
public String jsonRpc = "1.0";
@SerializedName("id")
public String id = UUID.randomUUID().toString();
@SerializedName("clazz")
public String clazz;
@SerializedName("method")
public String method;
@SerializedName("params")
public String params;
@Override
public String toString() {
return "RpcRequest{" +
"jsonRpc='" + jsonRpc + '\'' + ", id='" + id + '\'' + ", clazz='" + clazz + '\'' +
", method='" + method + '\'' + ", params='" + params + '\'' + '}'; } } 复制代码
好比上面的接口方法INumberApi.add,那么生成的最终的json信息以下。
{
"clazz": "cn.mycommons.bifrost.demo.api.INumberApi",
"id": "0af23e0d-03ab-4cb9-8f52-2c7f7e094023",
"jsonRpc": "1.0",
"method": "add",
"params": "[1,2]"
}
复制代码
而后这个对象又会转化成Req对象,这个对象是实现Parcelable接口的,用于2个进程之间通讯。
public class Req implements Parcelable {
private String uuid;
private String payload;
public Req() {
uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
public static Creator<Req> getCREATOR() {
return CREATOR;
}
@Override
public String toString() {
return "Req{" +
"uuid='" + uuid + '\'' + ", payload='" + payload + '\'' + '}'; } protected Req(Parcel in) { uuid = in.readString(); payload = in.readString(); } public static final Creator<Req> CREATOR = new Creator<Req>() { @Override public Req createFromParcel(Parcel in) { return new Req(in); } @Override public Req[] newArray(int size) { return new Req[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(uuid); dest.writeString(payload); } } 复制代码
上面的请求最终的信息变成了这样,这个不是json,是Java的toString方法返回的。
Req{uuid='f6a8028a-3cba-4abf-912b-ee7979923fb5', payload='{"clazz":"cn.mycommons.bifrost.demo.api.INumberApi","id":"0af23e0d-03ab-4cb9-8f52-2c7f7e094023","jsonRpc":"1.0","method":"add","params":"[1,2]"}'}
复制代码
当另一个进程获取到这些数据后,那么会作对应的反序列化,再次转化成Req,而后又能够获得RpcRequest。
当取到RpcRequest时候,能够根据里面的信息,获取当前调用接口的实现类,而后利用反射完成调用操做,获得结果后再次把结果转成json。
public class BifrostAidlImpl extends BifrostAidl.Stub {
private Gson gson = new Gson();
@Override
public Resp exec(Req req) throws RemoteException {
Timber.i("%s-->exec", this);
Timber.i("req = %s", req);
String data = req.getPayload();
RpcRequest rpcRequest = gson.fromJson(data, RpcRequest.class);
Timber.i("rpcRequest = %s", rpcRequest);
try {
Class<?> clazz = Class.forName(rpcRequest.clazz);
Method method = null;
for (Method m : clazz.getMethods()) {
if (m.getName().equals(rpcRequest.method)) {
method = m;
break;
}
}
if (method != null) {
Class<?>[] types = method.getParameterTypes();
List<Object> args = new ArrayList<>();
if (!TextUtils.isEmpty(rpcRequest.params)) {
JSONArray array = new JSONArray(rpcRequest.params);
for (int i = 0; i < array.length(); i++) {
String o = array.getString(i);
args.add(gson.fromJson(o, types[i]));
}
}
Object instance = Bifrost.getInstance().getInstance(clazz);
Timber.i("instance = %s", instance);
Timber.i("method = %s", method);
Timber.i("types = %s", Arrays.toString(types));
Timber.i("params = %s", args);
Object result = method.invoke(instance, args.toArray(new Object[0]));
Timber.i("result = %s", result);
return RespUtil.success(req.getUuid(), rpcRequest.id, result);
}
throw new RuntimeException("method " + rpcRequest.method + " cant not find");
} catch (Exception e) {
Timber.e(e);
// e.printStackTrace();
return RespUtil.fail(req.getUuid(), rpcRequest.id, e);
}
}
}
复制代码
json也会转成Resp,返回到原始的进程。而后解析数据,当作函数返回值。
整体来讲,这个流程仍是蛮清晰的,就是利用一个aidl通道,而后本身定义调用协议,我这边参考了JSON RPC协议。固然了也能够参考其余的,这里再也不表述。
优势
使用和调用简单,无上手压力
无需实现Parcelable接口,代码简洁
缺点
由于涉及到json转换,因此须要依赖gson
调用过程当中含有屡次json序列化与反序列化,有反射操做,可能会有性能影响
接口方法中的参数和返回值必需要是基本的类型,支持josn序列化和反序列化,但原始的AIDL
方式基本上也是同样,因此这条能够接受
暂时只是实现简单的Demo,只是验证这个思路是否可行,后续会作些优化操做,若有朋友有兴趣,能够一块儿参与,本人联系方式 xiaqiulei@126.com。
支持异步操做,支持回调函数,可参考Retroft调用方式,可支持RxJava操做
被调用进程支持线程池,增长并发量
单独的日志操做,不依赖Timber
支持同进程和夸进程调用
支持事件的通知、发送,可参考BroadcastReceiver,EventBus等。
若是你以为此文对您有所帮助,欢迎随时撩我 。微信公众号:终端研发部