aidl提供了binder调用的封装,有的时候,比如:
-
懒得使用aidl生成的接口文件(确实是懒,Android studio中aidl生成接口文件很方便)
-
服务端的提供者只公开了部分接口出来,只给了调用编号和参数(这个设计就很奇怪,其实可以提供fake的aidl文件,暴露部分方法即可)
-
想看看更直观的binder通信的调用
可以使用transcat调用,以下是直观的一个例子
java
try {
//获取服务,可以使用反射来实现
IBinder aProxyBinder = (IBinder)getSystemService("lam");//atest
if (aProxyBinder != null){
Log.d(TAG, "lam not null");
}
else {
Log.d(TAG, "lam is null");
}
Parcel dataParcel = Parcel.obtain();
Parcel resultParcel = Parcel.obtain();
dataParcel.writeInterfaceToken(DESCRIPTOR);
//发起请求
aProxyBinder.transact(3, dataParcel, resultParcel, 0);
在 Android 中,直接使用 Binder 的 transact
方法来调用 Binder 接口是一种底层的方法,通常用于自定义的 IPC(进程间通信)场景。这种方式绕过了 AIDL 自动生成的代码,允许你手动进行跨进程调用。下面将介绍如何直接使用 transact
方法来调用 Binder 接口。
1. 理解 transact
方法
transact
方法是 IBinder
类中的一个关键方法,它用于执行跨进程请求。其原型如下:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
- code: 请求码,标识要调用的具体方法。
- data : 输入参数,封装在
Parcel
对象中。 - reply : 输出结果,同样封装在
Parcel
对象中。 - flags : 标志位,控制事务的行为(如
FLAG_ONEWAY
表示异步调用)。
2. 定义和实现 Binder 接口
首先,你需要定义一个接口,并提供相应的服务端实现。这个过程可以通过 AIDL 来完成,但为了演示如何直接使用 transact
,我们将不使用 AIDL 自动生成的 stub 类。
假设我们有一个简单的服务接口 IMyService
,它包含一个名为 doSomething
的方法,该方法接受一个字符串作为输入,并返回一个字符串作为输出。
2.1 服务端实现
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
public class MyService extends Binder {
private static final int TRANSACTION_DO_SOMETHING = IBinder.FIRST_CALL_TRANSACTION + 0;
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (code == TRANSACTION_DO_SOMETHING) {
data.enforceInterface("com.example.IMyService");
String input = data.readString();
String output = doSomething(input);
reply.writeInterfaceToken("com.example.IMyService");
reply.writeString(output);
return true;
}
return super.onTransact(code, data, reply, flags);
}
public String doSomething(String input) {
// 实现你的业务逻辑
return "Hello, " + input;
}
}
在这个例子中,我们重写了 onTransact
方法,并根据传入的 code
来处理不同的请求。对于 TRANSACTION_DO_SOMETHING
请求,我们从 Parcel
中读取输入数据,调用 doSomething
方法处理数据,然后将结果写回到 reply
中。
2.2 客户端调用
接下来,我们需要编写客户端代码来使用 transact
方法调用 doSomething
方法。
import android.os.Binder;
import android.os.Parcel;
import android.os.RemoteException;
public class MyClient {
private IBinder binder;
public MyClient(IBinder binder) {
this.binder = binder;
}
public String callDoSomething(String input) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken("com.example.IMyService");
data.writeString(input);
// 调用 transact 方法
boolean result = binder.transact(MyService.TRANSACTION_DO_SOMETHING, data, reply, 0);
if (result) {
// 读取回复数据
reply.readException(); // 检查是否有异常抛出
return reply.readString();
} else {
throw new RemoteException("Transaction failed.");
}
} finally {
data.recycle();
reply.recycle();
}
}
}
在这个例子中,我们创建了两个 Parcel
对象:data
和 reply
。我们使用 writeInterfaceToken
方法为 data
写入接口令牌,然后写入方法参数。接着,我们调用 transact
方法发送请求,并通过 reply
读取响应数据。最后,不要忘记回收 Parcel
对象以释放资源。
3. 注意事项
- 错误处理 :在调用
transact
方法时,务必检查返回值并处理可能的异常。例如,可以使用reply.readException()
来检查服务器是否抛出了异常。