这一篇记录 AIDL(Android Interface Definition Language,安卓接口定义语言)。
AIDL 默认支持的数据类型包括:
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
- String 类型。
- CharSequence类型。
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。List可以使用泛型。
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
AIDL接口的创建
两个aidl文件// Book.aidl
package com.kevin.testaidl;
parcelable Book;
// IBookManager.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;//这里一定要手动导入
interface IBookManager {
void addBook(in Book b);
List<Book> getBookList();
}
代码中使用了自定义的对象Book,一定要在aidl文件中手动导入进来。同时一定要新建一个同名的aild文件,Book.aidl.
AIDL已经定义好了,找到Android Studio的make project进行编译,会在generatedJava里面
生成IBookManager。
服务端Service
/**
* Created by Kevin on 2019/3/21<br/>
* Blog:https://blog.csdn.net/student9128<br/>
* Describe:<br/>
* 服务端
*/
public class AIDLService extends Service {
private final String TAG = this.getClass().getSimpleName();
// private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步
private IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public void addBook(Book b) throws RemoteException {
synchronized (this) {
if (mBooks == null) {
// mBooks = new ArrayList<Book>();
mBooks = new CopyOnWriteArrayList<>();
}
if (b == null) {
Log.e(TAG, "Book is null in In");
b = new Book();
}
b.setPrice(123);//在服务端修改了book的价格
if (!mBooks.contains(b)) {
mBooks.add(b);
}
//打印mBooks列表,观察客户端传过来的值
StringBuilder sb = new StringBuilder();
for (Book boo : mBooks) {
sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
}
Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
}
}
@Override
public List<Book> getBookList() throws RemoteException {
// SystemClock.sleep(10000);
synchronized (this) {
if (mBooks != null) {
return mBooks;
}
}
return new CopyOnWriteArrayList<>();
}
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android ADIL 测试");
book.setPrice(33);
mBooks.add(book);
Log.w(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
return mBookManager;
}
}
在onCrate中初始化添加了一本图书信息,然后创建了一个Binder对象mBookManager并在onBind中返回它,这个对象实现了IBookManager.Stub内部的ADIL方法——addBook和getBookList方法。 可以看到addBook方法就是将添加的Book修改了price,添加到mBooks集合中,mBooks是CopyOnWriteArrayList对象,CopyOnWriteArrayList支持并发读写,保证多个客户端连接是,线程同步。
清单文件配置:
<service
android:name=".AIDLService"
android:exported="true"
android:permission="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE"
android:process=":remote">
<intent-filter>
<action android:name="com.kevin.aidl" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
客户端
客户端绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后通过这个接口去调用服务端的远程方法。
public class MainActivity extends AppCompatActivity {
private IBookManager mBookManager = null;
private boolean mBound = false;
private List<Book> mBooks;
private final String TAG = getClass().getSimpleName();
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, AIDLService.class);
bindService(intent, mSC, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mSC);
super.onDestroy();
}
private ServiceConnection mSC = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(getLocalClassName(), "service connected");
mBookManager = IBookManager.Stub.asInterface(service);
if (mBookManager != null) {
try {
Book book = new Book();
book.setPrice(99);
book.setName("Test");
mBookManager.addBook(book);
mBooks = mBookManager.getBookList();
for (Book b : mBooks) {
Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(getLocalClassName(), "service disconnected");
}
};
}
可以看到,总共添加了两本书,第一本是在服务端添加的,第二本是在客户端添加的。
现在增加一种场景,用户不希望每次都手动去获取服务端增加的新书,而是希望,每次一有新书到来,用户可以收到相关提醒。
添加一个aidl接口,aidl无法使用普通接口
// IOnNewBookArrivedListener.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;
// Declare any non-default types here with import statements
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
// IBookManager.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;
import com.kevin.testaidl.IOnNewBookArrivedListener;
// Declare any non-default types here with import statements
interface IBookManager {
void addBook(in Book b);
List<Book> getBookList();
void registerListener(IOnNewBookArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedListener listener);
}
服务端代码修改:
public class AIDLService extends Service {
private final String TAG = this.getClass().getSimpleName();
// private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步
//使用RemoteCallbackList存储 listener,这样可以解注册
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public void addBook(Book b) throws RemoteException {
synchronized (this) {
if (mBooks == null) {
// mBooks = new ArrayList<Book>();
mBooks = new CopyOnWriteArrayList<>();
}
if (b == null) {
Log.e(TAG, "Book is null in In");
b = new Book();
}
b.setPrice(123);
if (!mBooks.contains(b)) {
mBooks.add(b);
}
//打印mBooks列表,观察客户端传过来的值
StringBuilder sb = new StringBuilder();
for (Book boo : mBooks) {
sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
}
Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
}
}
@Override
public List<Book> getBookList() throws RemoteException {
// SystemClock.sleep(10000);
synchronized (this) {
if (mBooks != null) {
return mBooks;
}
}
return new CopyOnWriteArrayList<>();
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android ADIL 测试");
book.setPrice(33);
mBooks.add(book);
Log.w(TAG, "onCreate");
new Thread(new ServiceWorker()).start();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
return mBookManager;
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBooks.add(book);
int N = mListenerList.beginBroadcast();
Log.d(TAG, "onNewBookArrived,notify listeners:" + N);
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewBookArrived(book);
}
Log.d(TAG, "onNewBookArrived,notify listener:" + listener);
mListenerList.finishBroadcast();
}
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBooks.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
从代码中可以看到,创建了一个 RemoteCallbackList<>对象用来存储IOnNewBookArrivedListener。(RemoteCallbackList是系统专门提供的专门用于删跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。
public class RemoteCallbackList<E extends IInterface>
从声明中可以看出,所以的ADIL接口都继承自IInterface。)
实现了IBookManager.Stub内部的registerListener,unRegisterListener在这两个方法中去实现listener的注册和解注册。
同时添加了一个onNewBookArrived
方法,当有新书的时候,添加到mBooks集合,并调用IOnNewBookArrivedListener的onNewBookArrived进行通知用户,新书到了。
注意这里需要使用beginBroadcast
和finishBroadcast
。
客户端代码修改:
public class MainActivity extends AppCompatActivity {
private IBookManager mBookManager = null;
private boolean mBound = false;
private List<Book> mBooks;
private final String TAG = getClass().getSimpleName();
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book: " + msg.obj);
break;
default:
super.handleMessage(msg);
break;
}
}
};
private DeathRecipient deathRecipient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开子线程防止服务器端进行耗时操作,app出现ANR
new Thread(new Runnable() {
@Override
public void run() {
try {
if (mBookManager != null) {
List<Book> bookList = mBookManager.getBookList();
for (Book b : bookList) {
Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}
});
Intent intent = new Intent(this, AIDLService.class);
deathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
if (mBookManager == null) {
return;
}
mBookManager.asBinder().unlinkToDeath(deathRecipient, 0);
mBookManager = null;
attemptToBindService();//Binder die后,重新绑定服务
}
};
bindService(intent, mSC, Context.BIND_AUTO_CREATE);
}
public void addBook() {
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "当前与服务的处于未连接状态,正在尝试连", Toast.LENGTH_SHORT).show();
return;
}
if (mBookManager == null)
return;
Book book = new Book();
book.setName("App研发录");
book.setPrice(30);
try {
mBookManager.addBook(book);
Log.d(getLocalClassName(), "添加的课本:name:" + book.getName() + "\tprice:" + book.getPrice());
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.kevin.aidl");
intent.setPackage("com.kevin.testaidl");
bindService(intent, mSC, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (mBookManager != null && mBookManager.asBinder().isBinderAlive()) {
try {
Log.i(TAG, "unregister listner:" + mOnNewBookArrivedListener);
mBookManager.unRegisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mSC);
super.onDestroy();
}
private ServiceConnection mSC = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(getLocalClassName(), "service connected");
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mBookManager = IBookManager.Stub.asInterface(service);
mBound = true;
if (mBookManager != null) {
try {
Book book = new Book();
book.setPrice(99);
book.setName("Test");
mBookManager.addBook(book);
mBooks = mBookManager.getBookList();
for (Book b : mBooks) {
Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
}
mBookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(getLocalClassName(), "service disconnected");
mBound = false;
}
};
//运行中Binder线程池中,不能在它里面访问UI相关的内容,如果要访问,使用Handler切换到UI线程
// Binder可能会意外死亡,使用DeathRecipient监听,当Binder死亡是binderDied方法中重新连接服务
//或者在onServiceDisconnected重新连接服务
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
//
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
}
可以看到代码中为了防止Binder以外死亡,使用了DeathRecipient,当Binder死亡了,可以在binderDied方法中监听到,重新连接服务。
在使用进程间通信的过程中,还有一点是要去验证应用的权限,这里可以为服务自己定义一个权限,在使用的过程中在清单文件去声明下这个权限,服务端可以进行权限验证。
两种方法,
-
在onBind方法中验证权限,如果验证失败,返回null
@Override public IBinder onBind(Intent intent) { //在这里校验权限,如果客户端没有声明权限会一直连接不上,但是不知道为什么,不会有提示 int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE"); if (i == PackageManager.PERMISSION_DENIED) { return null; } Log.i(TAG, String.format("on bind,intent=%s", intent.toString())); return mBookManager; }
-
在服务端的onTransact方法中进行权限验证,如果失败就返回false,这样服务端就不会终止执行AIDL中的方法从而达到保护服务端的效果。
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//验证权限
int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
if (i == PackageManager.PERMISSION_DENIED) {
Log.i(TAG, "权限不被允许");
return false;
}
//验证包名
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (!packageName.startsWith("com.kevin.testaidl")) {
Log.i(TAG, "包名不对");
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
清单文件的配置
服务端:
<permission
android:name="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="signature" />
客户端:
<uses-permission android:name="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE" />
最后贴一下服务端的完整代码:
/**
* Created by Kevin on 2019/3/21<br/>
* Blog:https://blog.csdn.net/student9128<br/>
* Describe:<br/>
* 服务端
*/
public class AIDLService extends Service {
private final String TAG = this.getClass().getSimpleName();
// private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步
//使用RemoteCallbackList存储 listener,这样可以解注册
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public void addBook(Book b) throws RemoteException {
synchronized (this) {
if (mBooks == null) {
// mBooks = new ArrayList<Book>();
mBooks = new CopyOnWriteArrayList<>();
}
if (b == null) {
Log.e(TAG, "Book is null in In");
b = new Book();
}
b.setPrice(123);
if (!mBooks.contains(b)) {
mBooks.add(b);
}
//打印mBooks列表,观察客户端传过来的值
StringBuilder sb = new StringBuilder();
for (Book boo : mBooks) {
sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
}
Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
}
}
@Override
public List<Book> getBookList() throws RemoteException {
// SystemClock.sleep(10000);
synchronized (this) {
if (mBooks != null) {
return mBooks;
}
}
return new CopyOnWriteArrayList<>();
}
@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);
// } else {
// Log.d(TAG, "not found the listener,cannot unregister");
// }
// Log.d(TAG, "unRegisterListener,current size :" + mListenerList.size());
mListenerList.unregister(listener);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//验证权限
int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
if (i == PackageManager.PERMISSION_DENIED) {
Log.i(TAG, "权限不被允许");
return false;
}
//验证包名
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (!packageName.startsWith("com.kevin")) {
Log.i(TAG, "包名不对");
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android ADIL 测试");
book.setPrice(33);
mBooks.add(book);
Log.w(TAG, "onCreate");
new Thread(new ServiceWorker()).start();
}
@Override
public IBinder onBind(Intent intent) {
//在这里校验权限,如果客户端没有声明权限会一直连接不上,但是不知道为什么,不会有提示
// int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
// if (i == PackageManager.PERMISSION_DENIED) {
// return null;
// }
Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
return mBookManager;
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBooks.add(book);
int N = mListenerList.beginBroadcast();
Log.d(TAG, "onNewBookArrived,notify listeners:" + N);
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewBookArrived(book);
}
Log.d(TAG, "onNewBookArrived,notify listener:" + listener);
mListenerList.finishBroadcast();
}
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBooks.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
注:本文章知识点来自学习《Android开发艺术探索》一书