AIDL是 Android Interface definition language的缩写,一看就明白,它是一种Android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。
client app可通过aidl调用直接唤醒server app,类似于intent启动另一个app的activity,相比而言,在5.0以后,broadcast已经无法唤醒另一个未启动过的app。
AIDL的使用
最终效果
client调用aidl接口向server打招呼,server回调client的callback实现回应招呼。
一般是这样用的,client远程调用server端提供的service接口执行任务,server端回调,异步通知client端任务执行的状态。
客户端:
MainActivity
public class MainActivity extends Activity {
private Button bindBtn;
private Button greetBtn;
private Button unbindBtn;
private int count = 0;
private IGreetBinder iGreetBinder;
private Handler handler = new Handler();
private IGreetCallback mCallback = new IGreetCallback.Stub() {
@Override
public void greetBack(final Person someone) throws RemoteException {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"greet back"+someone,Toast.LENGTH_LONG).show();
}
});
}
};
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
iGreetBinder = IGreetBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//This is called when the connection with the service has been unexpectedly disconnected,
//that is, its process crashed. Because it is running in our same process, we should never see this happen.
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindBtn = (Button) findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.AIDLService");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
bindBtn.setEnabled(false);
greetBtn.setEnabled(true);
unbindBtn.setEnabled(true);
}
});
greetBtn = (Button) findViewById(R.id.greetBtn);
greetBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
count++;
String retVal = iGreetBinder.greet(new Person(count,"person"+count),mCallback);
Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
}
});
unbindBtn = (Button) findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
bindBtn.setEnabled(true);
greetBtn.setEnabled(false);
unbindBtn.setEnabled(false);
}
});
}
}
build.gradle
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
服务端:
AIDLService
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IGreetBinder.Stub stub = new IGreetBinder.Stub() {
@Override
public String greet(final Person someone, final IGreetCallback iGreetCallback) throws RemoteException {
Log.i(TAG, "greet() called");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
iGreetCallback.greetBack(someone);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return "hello, " + someone;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
}
AndroidManifest.xml
// 需定义action,提供给客户端调起;
build.gradle
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
AIDL部分
服务端和客户端共用aidl部分。
Person.java
public class Person implements Parcelable {
public int age;
public String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
public void readFromParcel(Parcel in){
name = in.readString();
age = in.readInt();
}
protected Person(Parcel in) {
age = in.readInt();
name = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public String toString() {
return age + ":" + name;
}
}
传递Person需要实现Parcelable接口。Aidl支持基本数据类型(int、long、char 等)String 和 CharSequence,List:只支持ArrayList,里面的每个元素都必须被AIDL支持。,Map: 只支持HashMap, 里面的每个元素都必须被AIDL支持。Parcelable: 所有实现了Parcelable接口的对象。
Parcelable 接口
Parcelable 可以是 android 系统将 objectes 分解成可以被进程处理的原始种类必须提供一个名为CREATOR的static final属性 该属性需要实现android.os.Parcelable.Creator接口,writeToParcel 注意写入变量和读取变量的顺序应该一致,不然得不到正确的结果,readFromParcel 注意读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
Person.aidl
package com.test.aidldemo;
parcelable Person;
IGreetBinder.aidl
package com.test.aidldemo;
import com.test.aidldemo.IGreetCallback;
import com.test.aidldemo.Person;
interface IGreetBinder {
String greet(inout Person person,IGreetCallback cb);
}
添加服务端对客户端的回调。
IGreetCallback.aidl
package com.test.aidldemo;
import com.test.aidldemo.Person;
interface IGreetCallback {
void greetBack(inout Person person);
}
使用Person类型参数时,需要提供方向指示符,其包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。如不设置方向指示符,将报错,基本类型int、String等无需设置方向指示符。
源码
client端:https://github.com/woshizmxin/AidlClientDemo
server端:https://github.com/woshizmxin/AidlServerDemo
在该demo运行在5.0以上设备上,会报错
** IllegalArgumentException: Service Intent must be explicit**
解决办法参考: Service Intent must be explicit的解决方法
使用Messager
如果想做app进程间通信,aidl写起来还是比较麻烦的,meassager则相当于是一个简化版本。它基于Message,相信大家都很熟悉,不需要编写aidl文件。
AIDL和Messager区别:
- Messenger本质也是AIDL,只是进行了封装,开发的时候不用再写.aidl文件。
结合我自身的使用,因为不用去写.aidl文件,相比起来,Messenger使用起来十分简单。但前面也说了,Messenger本质上也是AIDL,故在底层进程间通信这一块,两者的效率应该是一样的。 - 在service端,Messenger处理client端的请求是单线程的,而AIDL是多线程的。使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程(非主线程)去执行相应的操作。而Messenger,service收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。
当然是有办法解决的,在server端,Messenger的Handler可以只当作一个转发器,不处理请求,只转发请求到相应的处理线程(多是相应的HandlerThread),这样也可以达到异步的效果。 - client的方法,使用AIDL获取返回值是同步的,而Messenger是异步的。Messenger只提供了一个方法进行进程间通信,就是send(Message msg)方法,发送的是一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的,而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。
具体使用请移步:
Android 基于Message的进程间通信 Messenger完全解析
参考文章
Android中使用AIDL时的跨进程回调—Server回调Client
Service Intent must be explicit的解决方法
Android 基于Message的进程间通信 Messenger完全解析