Log.e(“哈利迪”, “服务端收到:” + data.readString());
String str = “777”;
Log.e(“哈利迪”, “服务端返回:” + str);
//2. 从reply向客户端写返回值
reply.writeString(str);
//3. 处理完成
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
运行如下,7行日志:
由于我们的flags传入的是0
同步调用,可以试着在服务端onTransact里sleep几秒,会发现客户端需要几秒后才能打印出返回值。所以如果服务端需要进行耗时操作,客户端则需要在子线程里进行binder调用。
延伸:从
IT互联网大叔
的「android获取进程名函数,如何优化到极致」一文可见,在使用系统API时,如果有更好的方案,还是建议将跨进程方案getSystemService放到最后作为兜底,因为他需要的binder调用本身有开销,而且作为应用层开发者也很少会去关注远方进程的内部实现,万一对方有潜在的耗时操作呢?
通过这个例子,我们可以看出,Binder机制使用了Parcel来序列化数据,客户端在主线程调用了transact来请求(Parcel data传参),服务端在Binder线程调用onTransact来响应(Parcel reply回传结果)。
Binder的调用流程大致如下,native层BpBinder的Bp
指的是Binder proxy
,
可见,需要经过如下调用才能完成一次通信:
-
请求:客户端Java层->客户端native层->Binder驱动层->服务端native层->服务端Java层
-
响应:服务端Java层->服务端native层->Binder驱动层->客户端native层->客户端Java层
即Binder驱动层充当着一个中转站的作用,有点像网络分层模型。
客户端与驱动交互
先来看客户端与驱动的交互。因为是跨进程调用(指定了:remote
),示例里onServiceConnected回调回来的service对象是个BinderProxy代理实例(不跨进程的话会发生远程转本地
,后面讲),我们以service.transact(1, data, reply, 0)这行调用作为入口跟进。
BinderProxy类写在Binder类文件里面:
//BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags){
//调用了native方法
return transactNative(code, data, reply, flags);
}
这个native方法在android_util_Binder.cpp里注册,
//android_util_Binder.cpp
//JNI注册<