Android AIDL客户端与服务端双向通信
一、概述
在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.