一、概述
在App开发过程中,有时候为了得到更多的可用内存、App性能优化、或者App保活的时候可能会用到多进程之间通讯。最常用的方式是使用AIDL进行通讯,这也是Android推荐的一种IPC通讯方式。
下面就记录一下用实际的代码来实现IPC通讯双方客户端和服务器端进行通讯的详细步骤。
案例:实现一个IPC通讯,客户端可以向服务端添加书籍,服务端写一个定时器来定时向客户端推送指定的书籍。
步骤如下:
1.新建IBookManager.aidl,用于管理AIDL通讯
// IBookManager.aidl
package com.yw.custommutilimageadapter.aidl;
// Declare any non-default types here with import statements
import com.yw.custommutilimageadapter.aidl.Book;
import com.yw.custommutilimageadapter.aidl.IOnServerCallback;
interface IBookManager {
///从远程服务端获取图书列表
List<Book> getBookList();
//往图书列表中添加一本书
void addBook(in Book book);
void registerListener(IOnServerCallback callback);
void unRegisterListener(IOnServerCallback callback);
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
2.新建Book.aidl用于IPC通讯的实体
// Book.aidl
package com.yw.custommutilimageadapter.aidl;
// Declare any non-default types here with import statements
parcelable Book;
//interface Book {
// /**
// * Demonstrates some basic types that you can use as parameters
// * and return values in AIDL.
// */
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);
//}
3.新建IOnServerCallback.aidl用于在IBookManager.aidl中设置回调函数,从而实现服务端向客户端传递数据。次类在客户端实现
// IOnServerCallback.aidl
package com.yw.custommutilimageadapter.aidl;
// Declare any non-default types here with import statements
import com.yw.custommutilimageadapter.aidl.Book;
interface IOnServerCallback {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void onBookReceived(in Book book);
}
4.新建一个Book.java类,此类必须实现parcelable接口,此类用户跨进程通讯传输,Book.java类的包名必须和aidl的包名保持一致
package com.yw.custommutilimageadapter.aidl;
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;
}
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 dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
}
5.新建后的目录结构
6.新建一个BookService类,其中BookManagerImpl类为Binder服务端实现类
package com.yw.custommutilimageadapter.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteCallbackList
import com.yw.custommutilimageadapter.aidl.Book
import com.yw.custommutilimageadapter.aidl.BookManagerImpl
import com.yw.custommutilimageadapter.aidl.IBookManager
import com.yw.custommutilimageadapter.aidl.IOnServerCallback
import java.lang.Exception
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
/**
* AIDL图书馆测试类
*/
class BookService : Service() {
//RemoteCallbackList是专门用于删除跨进程listener接口,它是一个泛型,支持管理任意的AIDL接口
private var mListener = RemoteCallbackList<IOnServerCallback>()
private var isServiceDestroy = AtomicBoolean(false)//服务是否销毁
private var books = CopyOnWriteArrayList<Book>()
override fun onCreate() {
super.onCreate()
books.add(Book(1,"《深入Java虚拟机》1"))
Thread(serverWork).start()
}
override fun onBind(intent: Intent?): IBinder? {
return BookManagerImpl(mListener)
}
override fun onDestroy() {
isServiceDestroy.set(true)
super.onDestroy()
}
/**
* 发送图书到客户端
*/
private fun sendBookToClient(book: Book){
books.add(book)
var n = mListener.beginBroadcast()
for(index in 0 until n){
var callback = mListener.getBroadcastItem(index)
callback?.onBookReceived(book)
}
mListener.finishBroadcast()
}
/**
* 服务端开始工作
*/
private var serverWork = Runnable {
while(!isServiceDestroy.get()){//如果服务没有销毁就一直运行
try{
Thread.sleep(5000)
var i = books.size+1
sendBookToClient(Book(i,"《深入Java虚拟机》${i}"))
}catch (e:Exception){
e.printStackTrace()
}
}
}
}
**
* AIDL图书馆代理实现类
*/
class BookManagerImpl(var mListener: RemoteCallbackList<IOnServerCallback>) : IBookManager.Stub() {
var list = ArrayList<Book>()
override fun getBookList(): MutableList<Book> {
LogUtils.e("服务端书籍数量:${list.size}")
return list
}
override fun addBook(book: Book?) {
book?.let { list.add(it) }
LogUtils.e("向服务端添加书籍")
}
override fun registerListener(callback: IOnServerCallback?) {
mListener?.register(callback)
}
override fun unRegisterListener(callback: IOnServerCallback?) {
mListener.unregister(callback)
}
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
}
7.注册BookService类到AndroidManifest.xml中
<!--运行在不同进程的Service-->
<service android:name=".service.BookService"
android:enabled="true"
android:exported="true"
android:process=":remote"
/>
8.BookManagerActivity为客户端调用类
package com.yw.custommutilimageadapter.ui
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.util.Log
import com.yw.custommutilimageadapter.R
import com.yw.custommutilimageadapter.aidl.Book
import com.yw.custommutilimageadapter.aidl.BookManagerImpl
import com.yw.custommutilimageadapter.aidl.IBookManager
import com.yw.custommutilimageadapter.aidl.IOnServerCallback
import com.yw.custommutilimageadapter.service.BookService
import kotlinx.android.synthetic.main.activity_book_manager.*
/**
* 测试AIDL,IPC通信
*/
class BookManagerActivity : Activity() {
/**
* 图书管理类
*/
private var iBookmanager: IBookManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_book_manager)
btnBind.setOnClickListener {
bindServiceClick()
}
btnUnBind.setOnClickListener {
unBindServiceClick()
}
btnAddBook.setOnClickListener {
iBookmanager?.addBook(Book(1, "《Android性能优化》"))
}
btnGetBooks.setOnClickListener {
var sb = StringBuffer()
var list = iBookmanager?.bookList
list?.forEachIndexed { index, book ->
sb.append(book.toString())
}
tvBookContent.text = sb.toString()
}
}
/**
* 绑定Service
*/
private fun bindServiceClick() {
var intent = Intent(this@BookManagerActivity, BookService::class.java)
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
/**
* 接触绑定Service
*/
private fun unBindServiceClick() {
unbindService(conn)
}
private var conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iBookmanager = IBookManager.Stub.asInterface(service)
try {
//设置死亡代理
service?.linkToDeath(mDeathRecipient, 0)
iBookmanager?.registerListener(serverListener)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
//Service意外中断时调用
iBookmanager = null
}
}
/**
* 监听Binder是否死亡
*/
private var mDeathRecipient = object : IBinder.DeathRecipient {
override fun binderDied() {
if (iBookmanager == null) {
return
}
iBookmanager?.asBinder()?.unlinkToDeath(this, 0)
iBookmanager = null
bindServiceClick()//重新绑定
}
}
/**
* 监听服务端消息的回调函数
*/
private var serverListener = object : IOnServerCallback.Stub() {
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
override fun onBookReceived(book: Book?) {
Log.e("收到服务端数据:", book.toString())
}
}
/**
* 销毁监听器
*/
private fun destroyService() {
if (conn != null && iBookmanager?.asBinder()?.isBinderAlive!!) {
try {
iBookmanager?.unRegisterListener(serverListener);
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
override fun onDestroy() {
super.onDestroy()
destroyService()
}
}
ps:把所有的代码按照目录结构贴在项目中就可以运行测试
二、重要代码说明
1.类介绍
a.IBookManager.aidl里面定义了四个方法 :
//从远程服务端获取图书列表
List<Book> getBookList();
//往图书列表中添加一本书
void addBook(in Book book);
//注册服务端向客户端发送消息的回调函数
void registerListener(IOnServerCallback callback);
//解除注册客户端向服务端发送消息的回调函数
void unRegisterListener(IOnServerCallback callback);
b.Book.aidl,由于Android的IPC通讯只允许指定的数据类型进行传输,所以此处定义的类用户跨进程通讯,因为其继承了parcelabel
c.IOnServerCallback.aidl,其实一个回调接口,用于向客户端回传消息,由于AIDL通讯不支持一般的interface所以,此接口继承了IInterface接口
//向客户端回传消息的方法
void onBookReceived(in Book book);
d.AIDL文件中支持的数据类型
1.基本数据类型(int、long、char、boolean、double等);
2.String和CharSequence;
3.List:只支持ArrayList,里面每个元素都必须能够被AIDL支持;
4.Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value
5.Percelable:所有实现了Parecelable接口的对象
6.AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
ps:如果AIDL文件中用到了自定义的parcelable对象,必须新建一个和他同名的AIDL文件,并在其中声明他为Parcelable类型,同时在引用的AIDL文件中必须导入这个AIDL,如上述的Book.aidl及IBookManager.aidl
e.RemoteCallbackList
1:RemoteCallbackList是专门用于删除跨进程listener的接口,它是一个泛型,支持管理任意的AIDL接口。
为什么要使用它呢?因为在服务端进行注册客户端发送过来的listener时,Binder会把客户端传递过来的对象重新转化并生成一个新的对象,因为对象是不能进行跨进程直接传输的,对象的跨进程传世都是反序列化的过程,这就是为什么AIDL中自定义对象都必须实现Parcelable的原因
2:使用RemoteCallbackList,我们无法像操作list一样去操作它,它并不是一个list,遍历的时候
int n=mListener.beginBroadcast();
for(int i=0;i<n;i++){
IOnNewPersonArrivedListener l=mListener.getBroadcastItem(i);
if (l!=null){
try {
l.onServerCallback(book);//服务端通过这个向客户端发送消息
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListener.finishBroadcast();
这样去进行,其中beginBroadcast和finishBroadcast必须要配对出现,哪怕是要获取RemoteCallbackList中的元素个数
f.