AIDL基本用法

前言

Aidl是android进程通信的一个办法,通过aidl我们可以跨进程调用方法。如果你仔细看完这篇文章,你将会有以下收获:

1、aidl的基本使用
2、aidl传递自定义对象
3、client实现对Server的监听

使用Aidl步骤


1、在Server端创建一个aidl文件,把服务端想对外提供的服务在这个文件中声明。声明之后对AS进行rebuild,然后系统会为我们自动生成一个java文件

2、在Server端创建一个Service,Service的目的是为了监听client的连接请求,当有连接请求到来的时候,我们把生成java文件的stub返回

3、client绑定远程Service,并把Server端返回的Binder对象转换成AIDL接口的所属类型。

1、 声明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)"
    }

}

IDE自动生成的java文件

我们每创建一个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;
}

IMyAidl.java文件说明:

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中获取远程的返回结果。


2、 Server端创建Service

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)
            }
        }
    }
}

3、客户端连接服务

下面代码很多,不过主要是做了以下几个工作:
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开发艺术探索

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值