欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

基于JSON RPC的一种Android跨进程调用解决方案了解一下?

程序员文章站 2022-03-02 21:50:19
基于json rpc的一种android跨进程调用解决方案了解一下? 基于json rpc的一种android跨进程调用解决方案了解一下? 简介 使用方式 实现原理 总结 后续安排 相关链接 简介...

基于json rpc的一种android跨进程调用解决方案了解一下?

基于json rpc的一种android跨进程调用解决方案了解一下?
简介 使用方式 实现原理 总结 后续安排 相关链接

简介

今天上午,看票圈有朋友分享爱奇艺的跨进程通信框架——andromeda,觉的还是有点意思的。
以前项目中用到跨进程这种解决方案比较少,今天看了下andromeda,发现调用方式很简单。

恰好最近一年都是在做后端工作,想到了json rpc的方案,其实android跨进程接也是一种rpc调用方式,那么参考json rpc协议,通过aidl通道也可以很简单一种跨进程通信方式,而且使用方式也很简单。

说干就干,但是作为了高级程序员,肯定要给项目起个名字高大上的名字——bifrost(彩虹桥),参考复联电影雷神上面的彩虹桥,寓意可以传送到各地,也表达android跨进程通信可以畅通无阻。

使用方式

在android的跨进程调用需要用到aidl方式,但是呢,操作起来非常麻烦,可以传递基本类型,如果需要自定义类,那么还需要实现parcelable接口,同时也要写不少代码,操作起来繁琐。

像平常一样,先定义一个接口和实现类就行了。

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;
 }
}

注册下接口和实现类,因为暂时没有用到依赖注入工具,同时我也不想把功能做的很复杂,暂时手动注册吧,做注册前,先做好初始化工作。

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是接口实现类的一个对象。

调用方式也很简单。

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();

实现原理

原理很简单,见下图所示。

基于JSON RPC的一种Android跨进程调用解决方案了解一下?

当在原始的进程中,定义一个接口,然后获取该对象的时候,其实返回值是一个用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 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 creator = new creator() {
  @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 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等。