对AIDL以及Binder机制还不太了解的建议先看这篇博客
https://blog.csdn.net/lj402159806/article/details/85038382
首先创建一个数据类文件和四个aidl文件
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 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];
}
};
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}
该数据类文件表示图书信息类,实现了parcelable接口
// Book.aidl
package com.gavinandre.aidl;
parcelable Book;
由于Book类是自定义parcelable对象,所以必须新建一个和它同名的AIDL文件,并在其中声明为parcelable类型
// IOnNewBookArrivedListener.aidl
package com.gavinandre.aidl;
import com.gavinandre.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
AIDL中无法使用普通接口,因此创建一个AIDL接口,用于服务端主动回调客户端,接口中用到了自定义parcelable对象,必须显式import进来,不管是否在同一个包内
// IBookManager.aidl
package com.gavinandre.aidl;
import com.gavinandre.aidl.Book;
import com.gavinandre.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
图书管理接口,有四个方法,前两个分别是客户端从服务端获取图书列表和增加图书功能,后两个用于客户端向服务端注册和注销aidl接口,由于用到了自定义parcelable对象和AIDL对象,因此必须显式将两者import进来
然后rebuild一下,系统就会在build/generated/source/aidl/debug/${packageName}/目录下生成同名的java文件
创建一个Service,名为BookManagerService,并注册这个Service
<service
android:name="com.gavinandre.aidldemo.BookManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
service>
然后声明自定义权限
<permission
android:name="com.gavinandre.aidldemo.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" />
<uses-permission android:name="com.gavinandre.aidldemo.permission.ACCESS_BOOK_SERVICE" />
BookManagerService完整代码如下:
class BookManagerService : Service() {
private val TAG = BookManagerService::class.java.simpleName
//使用CopyOnWriteArrayList来支持并发读写
private val mBookList = CopyOnWriteArrayList<Book>()
//使用RemoteCallbackList来支持aidl接口管理
private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>()
override fun onCreate() {
super.onCreate()
}
override fun onBind(intent: Intent): IBinder? {
//验证权限方式1
//在onBind函数中做验证,没有声明该权限则不返回binder
checkCallingOrSelfPermission(ACCESS_BOOK_SERVICE_PERMISSION)
.takeIf { it == PackageManager.PERMISSION_DENIED }
?.let {
Log.e(TAG, "onBind: permission denied")
return null
}
?: return mBinder
}
override fun onDestroy() {
super.onDestroy()
}
private val mBinder = object : IBookManager.Stub() {
override fun addBook(book: Book) {
//模拟耗时操作
SystemClock.sleep(3000)
mBookList.add(book)
//通知所有注册监听接口的客户端有新书到达
onNewBookArrived(book)
}
override fun getBookList(): List<Book> {
//模拟耗时操作
SystemClock.sleep(3000)
return mBookList
}
override fun registerListener(listener: IOnNewBookArrivedListener?) {
//添加aidl接口
mListenerList.register(listener)
//获取aidl接口数量
val N = mListenerList.beginBroadcast()
mListenerList.finishBroadcast()
Log.i(TAG, "registerListener, current size:$N")
}
override fun unregisterListener(listener: IOnNewBookArrivedListener?) {
//移除aidl接口
val success = mListenerList.unregister(listener)
if (success) {
Log.i(TAG, "unregister success.")
} else {
Log.i(TAG, "not found, can not unregister.")
}
//获取aidl接口数量
val N = mListenerList.beginBroadcast()
mListenerList.finishBroadcast()
Log.i(TAG, "unregisterListener, current size:$N")
}
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
//验证权限方式2
//在onTransact函数中做验证,没有声明该权限则返回false
checkCallingOrSelfPermission(ACCESS_BOOK_SERVICE_PERMISSION)
.takeIf { it == PackageManager.PERMISSION_DENIED }
?.let {
Log.e(TAG, "onTransact: permission denied")
return false
}
//验证权限方式3
//验证包名前缀,不相等则返回false
packageManager.getPackagesForUid(getCallingUid())
?.takeIf { it.isNotEmpty() }
?.takeUnless { it[0].startsWith(PACKAGE_NAME_PREFIX) }
?.let {
Log.e(TAG, "onTransact: package name invalid")
return false
}
return super.onTransact(code, data, reply, flags)
}
}
fun onNewBookArrived(book: Book) {
val N = mListenerList.beginBroadcast()
for (i in 0 until N) {
try {
//通知所有注册监听接口的客户端有新书到达
mListenerList
.getBroadcastItem(i)
?.onNewBookArrived(book)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
mListenerList.finishBroadcast()
}
companion object {
const val ACCESS_BOOK_SERVICE_PERMISSION = "com.gavinandre.aidldemo.permission.ACCESS_BOOK_SERVICE"
const val PACKAGE_NAME_PREFIX = "com.gavinandre"
}
}
上面的Service代码,首先创建了一个Binder对象,然后在onBind方法中返回,onBind方法中做了权限验证的功能,验证不通过直接返回null,这个Binder对象继承自IBookManager.Stub并实现了它内部的四个AIDL方法和onTransact方法,四个AIDL方法分别是获取图书列表,添加图书,注册监听和注销监听,onTransact方法用来做权限验证,这里实现了两种方式,分别是用权限和包名前缀来进行验证,不通过直接返回false
需要注意的点:
private val mBookList = CopyOnWriteArrayList<Book>()
由于AIDL方法是在服务端的Binder线程池中执行的,多个客户端同时连接就会存在并发的情况,因此这里要处理线程同步就可以使用CopyOnWriteArrayList来自动处理线程同步
override fun getBookList(): List<Book> {
//模拟耗时操作
SystemClock.sleep(3000)
return mBookList
}
虽然AIDL中List只能使用ArrayList,但是这里却能用CopyOnWriteArrayList类型的mBookList作为返回值是因为AIDL中所支持的是抽象List,在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端,ConcurrentHashMap同理
服务端AIDL方法是运行在Binder线程池中执行,能做耗时操作,但不能做ui操作
private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>()
AIDL对象在通过Binder传递的过程中会被重新转化并生成一个新对象,这也是为什么AIDL中的自定义对象都必须实现Parcelable接口的原因,因此AIDL接口的管理要使用RemoteCallbackList
创建Activity,在manifest中注册,完整代码如下:
class BookManagerActivity : AppCompatActivity() {
private val TAG = BookManagerActivity::class.java.simpleName
private var mRemoteBookManager: IBookManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_book_manager)
bindService()
initListener()
}
//绑定远程服务端
private fun bindService() {
val intent = Intent(this, BookManagerService::class.java)
intent.setPackage(packageName)
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
//注册按钮监听,调用远程耗时方法需要开启线程
private fun initListener() {
btnGetBookList.setOnClickListener {
Toast.makeText(this, "GetBookList", Toast.LENGTH_SHORT).show()
Thread {
try {
mRemoteBookManager?.let { mRemoteBookManager ->
val list = mRemoteBookManager.bookList
Log.i(TAG, "query book list: $list")
}
} catch (e: RemoteException) {
e.printStackTrace()
}
}.start()
}
btnAddBook.setOnClickListener {
Toast.makeText(this, "AddBook", Toast.LENGTH_SHORT).show()
Thread {
try {
mRemoteBookManager?.apply {
it.tag = it.tag?.run {
this as Int + 1
} ?: 1
val id = it.tag as Int
val newBook = Book(id, "Book$id")
Log.i(TAG, "add book: $newBook")
addBook(newBook)
}
} catch (e: RemoteException) {
e.printStackTrace()
}
}.start()
}
}
override fun onDestroy() {
unBindService()
//防止内存泄露
mHandler.removeCallbacksAndMessages(null)
super.onDestroy()
}
private fun unBindService() {
try {
Log.i(TAG, "onDestroy: $mOnNewBookArrivedListener")
mRemoteBookManager
?.takeIf { it.asBinder().isBinderAlive }
?.unregisterListener(mOnNewBookArrivedListener)
} catch (e: RemoteException) {
e.printStackTrace()
}
unbindService(mConnection)
}
//服务端连接状态回调
private var mConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.e(TAG, "onServiceConnected: $mRemoteBookManager")
//将服务端返回的Binder对象转换为aidl对象
mRemoteBookManager = IBookManager.Stub.asInterface(service)
Log.e(TAG, "onServiceConnected: $mRemoteBookManager")
try {
//注册AIDL回调接口
mRemoteBookManager?.registerListener(mOnNewBookArrivedListener)
//设置死亡代理
mRemoteBookManager?.asBinder()?.linkToDeath(mDeathRecipient, 0)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.i(TAG, "onServiceDisconnected ThreadName: ${Thread.currentThread().name}")
mRemoteBookManager = null
}
}
//aidl接口回调
private val mOnNewBookArrivedListener = object : IOnNewBookArrivedListener.Stub() {
override fun onNewBookArrived(newBook: Book?) {
//obtainMessage可以复用Message,减少开销
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget()
}
}
//使用lambda简化创建handler
private val mHandler = Handler { msg ->
when (msg.what) {
MESSAGE_NEW_BOOK_ARRIVED -> Log.i(TAG, "receive new book: ${msg.obj}")
}
false
}
//死亡代理回调
private val mDeathRecipient = object : IBinder.DeathRecipient {
override fun binderDied() {
Log.d(TAG, "binder died. ThreadName: ${Thread.currentThread().name}")
//移除死亡代理
mRemoteBookManager?.asBinder()?.unlinkToDeath(this, 0)
mRemoteBookManager = null
// TODO:这里重新绑定远程Service
//bindService()
}
}
companion object {
const val MESSAGE_NEW_BOOK_ARRIVED = 1
}
}
客户端首先绑定远程服务端,绑定成功后将服务端返回的Binder对象转换成AIDL客户端对象,接着向远程服务端注册IOnNewBookArrivedListener回调接口,设置DeathRecipient死亡代理,然后就可以通过aidl客户端对象调用服务端的远程方法了
需要注意的点:
btnGetBookList.setOnClickListener {
Toast.makeText(this, "GetBookList", Toast.LENGTH_SHORT).show()
Thread {
try {
mRemoteBookManager?.let { mRemoteBookManager ->
val list = mRemoteBookManager.bookList
Log.i(TAG, "query book list: $list")
}
} catch (e: RemoteException) {
e.printStackTrace()
}
}.start()
}
客户端如果调用远程方法是耗时方法则需要在子线程内操作
完整demo:
https://github.com/GavinAndre/AIDLDemo