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的进程即可。如下:
<service
android:name=".demo_aidl.AidlService"
android:process="xiaojun.test" />
AidlService.kt
/**
* 定义Aidl服务,在manifest声明service并设置运行进程,详情见manifest
*/
class AidlService : Service() {
val listenerList = RemoteCallbackList()
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开发艺术探索