service.transact传入了flags为0
,表示同步调用,会阻塞等待服务端的返回值。如果服务端进行了耗时操作,此时用户操作UI则会引起ANR。
flags的另一个值是1
,表示异步调用的one way,不需要等待服务端的返回结果,先忽略。
来看服务端运行的Service,
class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
//返回服务端的IBinder句柄
return new MyBinder();
}
}
注册服务,让服务端Service运行在:remote
进程,来实现跨进程,
<service
android:name=“.binder.no_aidl.MyService”
android:process=“:remote” />
运行在服务端的Binder对象,
class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags){
if (code == 1) {//如果是约定好的行为码1
Log.e(“哈利迪”, "— 我是服务端 MyBinder , pid = "
-
Process.myPid() + ", thread = "
-
Thread.currentThread().getName());
//1. 从data读取客户端参数
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</