IPC之AIDL入门用法
2018-09-11 本文已影响15人
一线游骑兵
目的:通过AIDL实现简单的IPC [该文例子参考自《Android开发艺术探索》一书]
Step1
首先创建跨进程通信的实体【须实现 Parcelable
接口】。
package com.zhu.aidldemo;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<com.zhu.aidldemo.Book> CREATOR = new Creator<com.zhu.aidldemo.Book>() {
@Override
public com.zhu.aidldemo.Book createFromParcel(Parcel in) {
return new com.zhu.aidldemo.Book(in);
}
@Override
public com.zhu.aidldemo.Book[] newArray(int size) {
return new com.zhu.aidldemo.Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
Step2
创建AIDL文件,并在其中定义供客户端进程调用的方法。
// IBookManager.aidl
package com.zhu.aidldemo;
import com.zhu.aidldemo.Book;
interface IBookManager {
void addBook(in Book book);
List<Book> getBookList();
}
注意事项
- 自定义的Parcelable对象和AIDL对象必须要显示import进来,不管是否在同一包下
- AIDL文件中除了基本类型,其它类型的参数必须标上方向:in(输入型参数)、out(输出型参数)、inout(输入输出型参数)
- AIDL文件中不支持声明静态常量(区别于接口)
AIDL文件中支持的数据类型有:
- 基本数据类型(int、long、char、boolean、double等)
- String和CharSequence
- List:只支持ArrayList,且里边的每个元素都必须能被AIDL支持。
- Map:只支持HashMap,且里边的每个元素(包括key,value)都必须被AIDL支持
- Parcelable:所有实现了Parcelable接口的对象
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
如果AIDL文件中用到了自定义的Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。
在上边的IBookManager.aidl文件中用到了实现了Parcelable的Book对象,因此需要新建一个Book.aidl:
// Book.aidl
package com.zhu.aidldemo;
parcelable Book;
声明之后点击Make Project生成对应的java文件。
Step3
实现服务端的Service,对aidl文件中定义的方法进行实现。
public class BookManagerService extends Service {
private final static String TAG = "server";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "人类简史"));
mBookList.add(new Book(2, "三体"));
}
private Binder mBinder = new IBookManager.Stub() {
@Override
public void addBook(Book book) throws RemoteException {
Log.d(TAG, "receive a request from client, add book to list...");
mBookList.add(book);
}
@Override
public List<Book> getBookList() throws RemoteException {
Log.d(TAG, "receive a request from client, query book list...");
return mBookList;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
代码说明:
- 首先初始化了bookList,并在onCreate中添加了两本书。然后实现了IBookManager中的方法,用来向客户端进程提供服务。之后返回了该binder对象供客户端调用。
- 由于AIDL方法在在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以此处使用CopyOnWriteArrayList解决线程同步问题。
- 前边说了AIDL只支持List,但List只是一个借口,CopyOnWriteArrayList实现了List,因此Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端进程。类似的还有ConcurrentHashMap。
Step4
在清单文件中声明该服务。
<service
android:name=".BookManagerService"
android:process=":romate" />
Step5
客户端的实现
class MainActivity : AppCompatActivity() {
val TAG = "client"
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
var bookManager = IBookManager.Stub.asInterface(service)
try {
var bookList = bookManager.bookList
Log.d(TAG, "list type: " + bookList::class.java.canonicalName)
Log.d(TAG, "book list on server: " + bookList.toString())
} catch (e: Exception) {
println(e.message)
}
}
override fun onServiceDisconnected(name: ComponentName) {
Log.d(TAG, "onServiceDisconnected...")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var intent = Intent(this, BookManagerService::class.java)
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
super.onDestroy()
unbindService(serviceConnection)
}
}
绑定服务并建立连接。实现与服务端跨进程通信。
需要注意的是:服务端的方法有可能是一些耗时操作,此时容易造成客户端ANR,此处不做相关处理
结果:
客户端进程:
image.png
服务端进程:
image.png
本章完。
点击查看 AIDL进阶用法