Aidl是android进程通信的一个办法,通过aidl我们可以跨进程调用方法。如果你仔细看完这篇文章,你将会有以下收获:
1、aidl的基本使用 2、aidl传递自定义对象 3、client实现对Server的监听1、在Server端创建一个aidl文件,把服务端想对外提供的服务在这个文件中声明。声明之后对AS进行rebuild,然后系统会为我们自动生成一个java文件
2、在Server端创建一个Service,Service的目的是为了监听client的连接请求,当有连接请求到来的时候,我们把生成java文件的stub返回
3、client绑定远程Service,并把Server端返回的Binder对象转换成AIDL接口的所属类型。
IMyAidl.aidl(这个aidl文件是对外提供服务的)
// IMyAidl.aidl package com.example.xiaojun.learningdemo_jun; import com.example.xiaojun.learningdemo_jun.Person; import com.example.xiaojun.learningdemo_jun.NotificationListener; interface IMyAidl { int addNumbers(int a,int b); Person getPerson(); //client可以通过这样设置或者取消对server内容的监听 void setListener(NotificationListener listener); void removeListener(NotificationListener listener); }NotificationListener.aidl(server 提供给client的监听接口,当有client感兴趣的内容时,server会通知client)
// NotificationListener.aidl package com.example.xiaojun.learningdemo_jun; interface NotificationListener { void notifyMessage(String msg); }Person.aidl(主要是为了演示怎么通过aidl传递自定义对象)
// Person.aidl package com.example.xiaojun.learningdemo_jun; parcelable Person;备注:Person是自定义对象,它实现了Parcelable接口。创建Person.aidl是为了演示怎么通过aidl传递自定义对象。 aidl支持数据类型:
1、基本数据类型(int,char,float...) 2、String 和 CharSequence 3、实现Parcelable的对象 4、Aidl接口本身 5、ArrayList,或者HashMap对象容纳的上述支持对象Person.kt
package com.example.xiaojun.learningdemo_jun import android.os.Parcel import android.os.Parcelable /** * Created by XiaoJun on 2017/9/4. * Version 1.0.0 */ class Person() :Parcelable { var name:String? = null var age:Int? = null constructor(parcel: Parcel) : this() { name = parcel.readString() age = parcel.readInt() } override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(name) parcel.writeInt(age!!) } //只有有文件描述符的时候返回1 override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator<Person> { override fun createFromParcel(parcel: Parcel): Person { return Person(parcel) } override fun newArray(size: Int): Array<Person?> { return arrayOfNulls(size) } } override fun toString(): String { return "Person(name=$name, age=$age)" } }我们每创建一个aidl文件,IDE都会自动生成一个Java文件。事实上,我们通过aidl进行跨进程通信不是依赖aidl文件,而是生成的java文件。我们写aidl文件是为了让IDE帮我们生成对应的Java文件。 IMyAidl.java
package com.example.xiaojun.learningdemo_jun; // Declare any non-default types here with import statements public interface IMyAidl extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.xiaojun.learningdemo_jun.IMyAidl { private static final java.lang.String DESCRIPTOR = "com.example.xiaojun.learningdemo_jun.IMyAidl"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.xiaojun.learningdemo_jun.IMyAidl interface, * generating a proxy if needed. */ public static com.example.xiaojun.learningdemo_jun.IMyAidl asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.xiaojun.learningdemo_jun.IMyAidl))) { return ((com.example.xiaojun.learningdemo_jun.IMyAidl) iin); } return new com.example.xiaojun.learningdemo_jun.IMyAidl.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_addNumbers: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.addNumbers(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_getPerson: { data.enforceInterface(DESCRIPTOR); com.example.xiaojun.learningdemo_jun.Person _result = this.getPerson(); reply.writeNoException(); if ((_result != null)) { reply.writeInt(1); _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_setListener: { data.enforceInterface(DESCRIPTOR); com.example.xiaojun.learningdemo_jun.NotificationListener _arg0; _arg0 = com.example.xiaojun.learningdemo_jun.NotificationListener.Stub.asInterface(data.readStrongBinder()); this.setListener(_arg0); reply.writeNoException(); return true; } case TRANSACTION_removeListener: { data.enforceInterface(DESCRIPTOR); com.example.xiaojun.learningdemo_jun.NotificationListener _arg0; _arg0 = com.example.xiaojun.learningdemo_jun.NotificationListener.Stub.asInterface(data.readStrongBinder()); this.removeListener(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.xiaojun.learningdemo_jun.IMyAidl { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public int addNumbers(int a, int b) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_addNumbers, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public com.example.xiaojun.learningdemo_jun.Person getPerson() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); com.example.xiaojun.learningdemo_jun.Person _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { _result = com.example.xiaojun.learningdemo_jun.Person.CREATOR.createFromParcel(_reply); } else { _result = null; } } finally { _reply.recycle(); _data.recycle(); } return _result; } //client可以通过这样设置或者取消对server内容的监听 @Override public void setListener(com.example.xiaojun.learningdemo_jun.NotificationListener listener) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null))); mRemote.transact(Stub.TRANSACTION_setListener, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void removeListener(com.example.xiaojun.learningdemo_jun.NotificationListener listener) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null))); mRemote.transact(Stub.TRANSACTION_removeListener, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_addNumbers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_setListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_removeListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); } public int addNumbers(int a, int b) throws android.os.RemoteException; public com.example.xiaojun.learningdemo_jun.Person getPerson() throws android.os.RemoteException; //client可以通过这样设置或者取消对server内容的监听 public void setListener(com.example.xiaojun.learningdemo_jun.NotificationListener listener) throws android.os.RemoteException; public void removeListener(com.example.xiaojun.learningdemo_jun.NotificationListener listener) throws android.os.RemoteException; }1、DESCRIPTOR:Binder的唯一标识,一般使用的是当前Binder的类名表示。 2、asInterface(android.os.IBinder obj):将服务端返回的Binder对象转换成客户端所需要的AIDL接口类型对象。 3、asBinder返回当前Binder对象本身 4、onTransact这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求的时候,远程请求会由系统底层封装后交由此方法处理。服务端通过code可以确定client请求的目标方法是什么。接着从data中取出目标方法所需要的参数。当目标方法执行完之后,就像reply写入返回值。如果onTransact返回false的话,那么客户端会请求失败,我们可以根据这个特性做权限认证。 5、Proxy.addNumbers:这个方法运行在客户端,当客户端远程调用addNumbers方法时,它会进行如下操作:
1)、创建该方法所需要的输入型Parcel对象_data,输出型Parcel对象_reply和返回值对象; 2)、把该方法所需要的参数信息都放入data里面; 3)、调用transact方法来发送远程过程调用请求,并挂起当前线程。 4)、服务端的onTransact方法被调用; 5)、远程过程调用返回后,当前线程继续执行,并从_reply中获取远程的返回结果。
AidlService的主要工作是当客户端请求连接的时候返回aidl的stub。在stub里面我们实现了定义的功能接口。除此之外,service开启了一个线程,每隔5s更新一次消息,如果有client设置了监听,那么client能够接收到我们设置的消息。 aidl是跨进程之间的通信,所以我们演示aidl功能的时候要让客户端和服务端运行在不同进程。除此之外,我们使用aidl的客户端和服务端要包名完全相同才可以。 本次演示是通过设置应用多进程的方式演示的。我们在manifest文件里面把AidlService设置为运行在不同于Client的进程即可。如下:
<!-- 注意:process属性是指定service的运行进程,这样指定之后,service运行的进程就是“xiaojun.test” --> <service android:name=".demo_aidl.AidlService" android:process="xiaojun.test" />AidlService.kt
/** * 定义Aidl服务,在manifest声明service并设置运行进程,详情见manifest */ class AidlService : Service() { val listenerList = RemoteCallbackList<NotificationListener>() var msg = 0 override fun onCreate() { super.onCreate() Log.e("AidlService", "Created!") //200ms后每隔5000ms发送一次更新信息 val scheduledThreadPool = Executors.newScheduledThreadPool(1) scheduledThreadPool.scheduleAtFixedRate(object : Runnable { override fun run() { val N = listenerList.beginBroadcast(); for (i in 0 until N) { val li = listenerList.getBroadcastItem(i) if (li != null) { try { li.notifyMessage(msg.toString()) msg++ } catch (e: RemoteException) { Log.e("AidlService", "Exception: " + e.toString()) } } listenerList.finishBroadcast() } } }, 200, 5000, TimeUnit.MILLISECONDS) } override fun onBind(p0: Intent?): IBinder { return object : IMyAidl.Stub() { override fun addNumbers(a: Int, b: Int): Int { return a + b } override fun getPerson(): Person { val person = Person() person.age = 22 person.name = "XiaoJun" return person } override fun setListener(listener: NotificationListener?) { listenerList.register(listener) } override fun removeListener(listener: NotificationListener?) { listenerList.unregister(listener) } } } }下面代码很多,不过主要是做了以下几个工作: 1、连接服务端并把服务端返回的binder转换成aidl接口 2、设置服务端的通知监听,并Toast通知的内容 3、点击getPerson进行getPerson的远程调用 3、点击addNumber进程addNumber的远程调用 ClientAidlActivity.kt
class ClientAidlActivity : AppCompatActivity(), View.OnClickListener { private var myAdil:IMyAidl? = null private var conn:MServiceConnection? = null private var handler:Handler? = null private var listener : MListener? = null private val NOTIFICATION_COMES = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_aidl) init() } fun init() { val intent = Intent(this,AidlService::class.java) conn = MServiceConnection() bindService(intent,conn, Context.BIND_AUTO_CREATE) handler = @SuppressLint("HandlerLeak") object : Handler() { override fun handleMessage(msg: Message?) { if (msg != null && msg.what == NOTIFICATION_COMES){ Toast.makeText(this@ClientAidlActivity,"SeverNotification "+msg.obj.toString(),Toast.LENGTH_SHORT).show() } super.handleMessage(msg) } } getPerson.setOnClickListener(this) addNumber.setOnClickListener(this) } @SuppressLint("SetTextI18n") override fun onClick(p0: View?) { p0 ?: return if (myAdil == null){ Toast.makeText(this,"not connected!",Toast.LENGTH_SHORT).show() return } when (p0.id) { R.id.getPerson -> { showArea.text = myAdil?.person.toString() } R.id.addNumber -> { showArea.text = "4 + 3 = \n "+ myAdil?.addNumbers(4,3).toString() } else -> { return } } } //取消监听,并断开连接 override fun onDestroy() { super.onDestroy() myAdil?.removeListener(listener) unbindService(conn) } inner class MServiceConnection : ServiceConnection { override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) { myAdil = IMyAidl.Stub.asInterface(p1) listener = MListener() myAdil?.setListener(listener) Toast.makeText(this@ClientAidlActivity,"connected!",Toast.LENGTH_SHORT).show() } override fun onServiceDisconnected(p0: ComponentName?) { } } inner class MListener : NotificationListener.Stub() { // 这个函数是工作在binder线程池的,我们需要一个handler将它切换到UI线程 override fun notifyMessage(msg: String?) { handler?.obtainMessage(NOTIFICATION_COMES,msg)?.sendToTarget() } } }效果图
源码链接:https://github.com/KingLanding94/LearningDemo_Jun
参考:
Android开发艺术探索