上一篇我们在使用Messenger来进行进程间通信的方法,可以发现,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务断仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。具体步骤如下:
服务端:
1.服务端创建一个Service用来监听客户端的连接请求
2.创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明
3.在Service中实现这个AIDL接口
客户端:
1.绑定服务端的Service
2.绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型
3.调用AIDL中的方法
项目的目录如下:
首先我们先在com.example.bookmanager包下创建aidl目录,并在这个目录下创建Book.class类,Book类实现Parcelable接口
public class Book implements Parcelable {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}
AIDL接口的创建
接着我们在com.example.bookmanager.aidl目录下,使用Android Studio来创建IBookManager.aidl的AIDL文件
Android Studio会自动帮我们在main目录下创建aidl接口文件
// IOnNewBookArrivedListener.aidl
package com.example.bookmanager.aidl;
import com.example.bookmanager.aidl.Book;
// Declare any non-default types here with import statements
interface IOnNewBookArrivedListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onNewBookArrived(in Book book);
}
// IBookManager.aidl
package com.example.bookmanager.aidl;
import com.example.bookmanager.aidl.Book;
import com.example.bookmanager.aidl.IOnNewBookArrivedListener;
// Declare any non-default types here with import statements
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
List<Book> getBookList();
void addBook(in Book book);//in 表示输入型参数,out表示输出型参数,inout表示输入输出型参数
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
注意:Book是自定义的Parcelable对象,IOnNewBookArrivedListener是AIDL对象,虽然它们和当前IBookManager.aidl在同一个包内,我们仍然需要显示地import进来。上面代码还使用观察者模式,每个感兴趣的用户都观察新书,当新书到的时候,BookManager就通知每一个对这本书感兴趣的用户。
// Book.aidl
package com.example.bookmanager.aidl;
parcelable Book;
注意:Book是自定义的Parcelable对象,还必须创建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型
远程服务端Service的实现
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
//CopyOnWriteArrayList支持并发读/写
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
//当客户端进程终止后,RemoteCallbackList能够自动移除客户端所注册的listener。
//RemoteCallbackList内部自动实现了线程同步的功能
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if (!mListenerList.contains(listener)) {
// mListenerList.add(listener);
// } else {
// Log.d(TAG, "listener already exists.");
// }
// Log.d(TAG, "registerListener, size:" + mListenerList.size());
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if (mListenerList.contains(listener)) {
// mListenerList.remove(listener);
// Log.d(TAG, "unregister listener succeed.");
// } else {
// Log.d(TAG, "not found, can not unregister.");
// }
//
// Log.d(TAG, "unregisterListener, current size:" + mListenerList.size());
mListenerList.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//1.这里可以做权限验证,验证不通过返回null,绑定失败
//2.使用permission验证
//还有一种验证方式是在服务端的onTransact方法中进行权限验证,验证失败就直接返回false。
// int check = checkCallingOrSelfPermission("com.example.bookmanager.permission.ACCESS_BOOK_SERVICE");
// if (check == PackageManager.PERMISSION_DENIED) {
// return null;
// }
// String packageName = null;
// String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
// if (packages != null && packages.length > 0) {
// packageName = packages[0];
// }
// if (!packageName.startsWith("com.example")) {
// return null;
// }
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
// Log.d(TAG, "onNewBookArrived, notify listeners size:" + mListenerList.size());
// for (IOnNewBookArrivedListener listener : mListenerList) {
// Log.d(TAG, "onNewBookArrived, notify listener:" + listener);
// listener.onNewBookArrived(book);
// }
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book book = new Book(bookId, "new book # " + bookId);
try {
onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
注意:AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以要在AIDL方法中处理线程同步,我们这里直接使用CopyOnWriteArrayList来进行自动的线程同步
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bookmanager">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".aidl.BookManagerService"
android:process=":remote" />
</application>
</manifest>
客户端的实现
首先绑定远程服务,绑定成功后就将服务端返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务端的远程犯法了。这里我们客户端和服务端在同一个工程中,不同工程除了需要复制AIDL接口所相关的包到客户端,其他完全一样。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private TextView textView;
private IBookManager mRemoteBookManager;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
textView.setText("receive new book:" + msg.obj);
Log.d(TAG, "receive new book:" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
mRemoteBookManager = bookManager;
List<Book> bookList = bookManager.getBookList();
Log.i(TAG, "query book list, list type:" + bookList.getClass().getCanonicalName());
Log.i(TAG, "query book list:" + bookList.toString());
bookManager.addBook(new Book(3, "Python"));
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query new book list:" + newList.toString());
bookManager.registerListener(onNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mRemoteBookManager = null;
Log.e(TAG, "binder died.");
}
};
private IOnNewBookArrivedListener onNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
//这个方法是在客户端的Binder线程池中执行的,因此,更新UI,需要一个Handler
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textview);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
Log.i(TAG, "unregister listener:" + onNewBookArrivedListener);
try {
mRemoteBookManager.unregisterListener(onNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
注意:服务端的方法有可能需要很久才能执行完毕,这个时候上面的代码就会导致ANR,这里这样写是为了更好的了解AIDL的实现步骤。
最后效果如下图:
参考:《Android开发艺术探索》