服务间进程通信大部分使用AIDL,其其次原理是Binder ,先写一个Demo, 可以参考 这里 。
一 、AIDL 使用教程
1.1 创建要操作的实体类,实现 Parcelable
接口,以便序列化/反序列化 (Serizable没试过,不知道行不行)
public class Book implements Parcelable {
private int bookId;
private String bookName;
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 String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Creator<Book> CREATOR = new Creator<Book>(){
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}
}
1.2 新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
注意,利用as IDE创建AIDL文件,鼠标放在main 文件夹上,然后右键,再new—— AIDL 文件即可,这样就会得到上图的文件结构,注意这里aidl文件会区别java文件,同时后面的文件夹名称一致(不然会出错),接着创建实体类对应的映射的AIDL文件,如下:
可以看到这里什么都没有写,只有一个定义
parcelable Book;
先不管为啥,就这样写吧先。接着在写接口文件:
这里写了两个方法,一个是获取列表,一个是添加书的方法,basicTypes方法是系统生成的,可以去掉;
一般来说, AIDL 文件支持以下类型
- Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
- String
- List 只支持ArrayList ,其中所有元素都必须是AIDL支持的类型
- Map 只支持HashMap,其中所有元素都必须是AIDL支持的类型
- 实现 Parceable 的自定义类型
注意事项
- 在 aidl 文件中,除了 Java 编程语言中的所有原语类型、String、CharSequence、List、Map,其他在 AIDL 文件中用到的类,你必须使用 import 语句导入,否则会报错。比如上面的 Person,需要导入它的全路径
- 当你使用实现Parceable 的自定义类型的时候,当其作为参数的时候,你必须为其制定是输入或者是输出参数。
in 表示输入参数,即服务端可以修改该类型
out 表示输出参数,即客户端可以修改该类型
inout 表示客户端和服务端都可以修改该类型
接着就是reBuild ,重新编译一下工程,这样工程就会在build——generated——source——aidl 目录下生成模板代码(一定要把上面的所有步骤做完了才能重新编译,否则会出错)。
1.3 编写服务端代码
这里是绑定服务,和启动服务不一样,要实现onBind()方法,代码如下:
public class MyService extends Service {
private final String TAG = this.getClass().getSimpleName();
private ArrayList<Book> mBooks;
private IBinder mIBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBooks;
}
@Override
public void addBook(Book book) throws RemoteException {
mBooks.add(book);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
mBooks = new ArrayList<>();
Log.d(TAG, "onBind: ");
return mIBinder;
}
}
别忘记在 Manifest 文件中声明:
<service
android:name=".MyService"
android:process=":aidl"/>
1.4 客户端代码
逻辑:在客户端调用aidl调用服务端的相关方法,并从服务端获取数据,客户端和服务端不在同一进程,但实现了通信。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IBookManager mIBookManager;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理
mIBookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void clickView(View view){
//startActivity(new Intent(this, AnotherProcessActivity.class));
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
public void ToIndenpentProcess(View view){
//startActivity(new Intent(this, AnotherProcessIndepentActivity.class));
// 跨进程通信 在service进程中添加数据,在Activity进程获取数据 中间通过aidl实现
Random random = new Random();
Book book = new Book(random.nextInt(10), "bookName");
try {
mIBookManager.addBook(book);
List<Book> personList = mIBookManager.getBookList();
for (int i = 0; i < personList.size(); i++) {
Log.d(TAG, "ToIndenpentProcess: " + personList.get(i));
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
这样就完成了整个AIDL的使用了,大致有三步骤:
- 创建AIDL接口文件定义相关方法;
- 创建服务代码,实现相关逻辑(这里以service为例,实现onBind方法)
- 创建客户端,实现相关逻辑;
二、IBookManger 的类代码
分析as把AIDL文件转换的代码贴出来:
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.a41082.multiprocess.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.a41082.multiprocess.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.a41082.multiprocess.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.a41082.multiprocess.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.a41082.multiprocess.IBookManager))) {
return ((com.example.a41082.multiprocess.IBookManager) iin);
}
return new com.example.a41082.multiprocess.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.a41082.multiprocess.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.a41082.multiprocess.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.a41082.multiprocess.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.a41082.multiprocess.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public java.util.List<com.example.a41082.multiprocess.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.a41082.multiprocess.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.a41082.multiprocess.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.a41082.multiprocess.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();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public java.util.List<com.example.a41082.multiprocess.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.a41082.multiprocess.Book book) throws android.os.RemoteException;
}
这里主要是 内部类 Stub ,这是一个Binder, 然后内部有onTransact()方法,主要是 用这个方法和服务端进行交互,然后会通过reply 进行返回数据给Binder,binder再给客户端。
Binder原理参考 这里