一、什么是AIDL
AIDL,全称Android Interface definition language,是Android中IPC(Inter-Process Communication)进程通信方式的一种。
说到进程通信,就要区分一下什么是进程什么是线程:
进程一般指的是一个执行单元,它拥有独立的地址空间,也就是一个应用或者一个程序。
线程是CPU调度的最小单元,是进程中的一个执行部分或者说是执行体,两者之间是包含与被包含的关系。
AIDL的作用:生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数,来完成进程间通信。
二、使用和原理
AIDL的实现可分为三部分:
1、客户端,调用远程服务。
2、服务端,提供服务。
3、AIDL接口,用来传递的参数,提供进程间通信。
Demo:
创建自定义类Book,实现Parcelable接口
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}
protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = in.readString();
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
新建一个IBookManager.aidl文件
import com.kwmax.aidldemo.Book;
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void addBook(in Book book);
List<Book> getBookList();
}
默认格式是basicType方法中的几种,addBook,getBook为我们添加的方法。
注意在AIDL中,Book需要import导入:import com.kwmax.aidldemo.Book;
此外,自定义的Parcelable对象,必须创建一个和它同名的AIDL文件,并在其中声明它为parcelable类型。
// Book.aidl
package com.kwmax.aidldemo;
parcelable Book;
以上完成之后,make project,AS会为我们自动为我们生成对应的Binder类:IBookManager,
路径在build->generated->source->aidl文件夹下
该接口中有个重要的内部类Stub ,继承了Binder 类,同时实现了IBookManager接口
public static abstract class Stub extends android.os.Binder implements com.kwmax.aidldemo.IBookManager
在内部类Stub中,有几个常量和方法需要注意一下:
TRANSACTION_basicTypes、TRANSACTION_addBook、TRANSACTION_getBookList是几个方法的标识id
DESCRIPTOR常量是 Binder的唯一标识,一般用当前Binder的类名表示
asInterface 方法:用于将服务端的Binder对象转换为客户端所需要的接口对象,该过程区分进程,如果进程一样,就返回服务端Stub对象本身,否则呢就返回封装后的Stub.Proxy对象。
onTransact 方法:运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层封装后会交由此方法来处理。通过code来区分客户端请求的方法,注意一点的是,如果该方法返回false,客户端的请求就会失败。一般可以用来做权限控制。
Proxy:代理类。在代理类中,有两个方法,我们定义的addBook和getBookList方法。
@Override public void addBook(com.lvr.aidldemo.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
两个方法都是运行在客户端,当客户端发起远程请求时,_data会写入参数,然后调用transact方法发起RPC(远程过程调用)请求,同时挂起当前线程。服务端的onTransact方法就会被 调起,直到RPC过程返回后,当前线程继续执行,并从_reply取出返回值(如果有的话),并返回结果。
接下来,让客户端和服务端实现调用
实现进程间通信可以在同个应用中也可以不在一个应用中。这里我们在同个应用中,创建clientActivity在默认进程中,创建service在remote进程中。
在AndroidManifest注册service:
<service
android:name=".ServerService"
android:process=":remote">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.kwmax.aidldemo.ServerService"/>
</intent-filter>
</service>
客户端:绑定service -> 将服务端返回的Binder对象转换成AIDL接口所属的类型 -> 调用AIDL中的方法
public class ClientActivity extends AppCompatActivity implements View.OnClickListener{
private Button mBinderButton;
private Button mAddButton;
private IBookManager mIBookManager;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//通过服务端onBind方法返回的binder对象得到IBookManager的实例,得到实例就可以调用它的方法了
mIBookManager = IBookManager.Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBinderButton = (Button) findViewById(R.id.bindService);
mAddButton = (Button) findViewById(R.id.addBook);
mBinderButton.setOnClickListener(this);
mAddButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bindService:{
Intent intentService = new Intent();
intentService.setAction("com.kwmax.aidldemo.ServerService");
intentService.setPackage(getPackageName());
intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ClientActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
Toast.makeText(getApplicationContext(),"绑定服务!!",Toast.LENGTH_SHORT).show();
break;
}
case R.id.addBook:{
if(mIBookManager!=null){
try {
mIBookManager.addBook(new Book(18,"新添加的书"));
//获取执行结果
Toast.makeText(getApplicationContext(),mIBookManager.getBookList().size()+"",Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mIBookManager!=null){
unbindService(mServiceConnection);
}
}
}
服务端:实现Stub 类,并定义接口中方法的具体实现
public class ServerService extends Service {
private List<Book> mBookList = new ArrayList<>();
//实现了AIDL的抽象函数
private IBookManager.Stub mbinder = new IBookManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
//什么也不做
}
@Override
public void addBook(Book book) throws RemoteException {
//添加书本
if(!mBookList.contains(book)){
mBookList.add(book);
}
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mbinder;
}
@Override
public void onCreate() {
super.onCreate();
for (int i = 1; i < 6; i++) {
Book book = new Book();
book.bookName = "book#" + i;
book.bookId = i;
mBookList.add(book);
}
}
}
当客户端连接服务端,服务端就会调用onBind方法,把Stub实现对象返回给客户端,该对象是个Binder对象,可以实现进程间通信。
至此,AIDL部分基本弄明白了,至于内部Binder机制是怎么实现通信的,请听下回分解吧~