Some information about AIDL can be summarized and reviewed

What is the AIDL?

AIDL(Android Interface Definition Language) uses it to define programming interfaces that both clients and services agree on so that they can communicate with each other using inter-process communication (IPC). What actually works is not the AIDL file, but the instance code generated from AIDL, which is a template that Android designed for us to generate Interface code from.

ADIL is designed to communicate between processes. Through AIDL, we can communicate data and interact logic between different processes.

Generally, there are two ends: client and server

Supported parameter types

  • Basic data types: Boolean, byte, char, float, double, int, long
  • String 、CharSequence
  • List type (contains data that must be an AIDL-supported type or other declared AIDL object)
  • Map type (contains data that must be an AIDL-supported type or other declared AIDL object)
  • Data types that implement the Parcelable interface (e.g., Bundle)
  • AIDL interface itself

(Short is not supported online because Parcel doesn’t serialize short, but I found that writeValue () handles short’s type.) Process ‘command ‘D:\Android\ SDK \build-tools\28.0.3\ AIDL. Exe ‘finished’ Process ‘command ‘D:\Android\ SDK \build-tools\28.0.3\ AIDL Arguments with non-zero exit value 1 with non-zero exit value 1

The use of an AIDL

  • Define a unified package name and interface AIDL file at both ends

Ioneaidlinterface. aidl: Exposes the interface class used by the client.

package testview.zhen.com.myapplication;
import testview.zhen.com.myapplication.PersonBean;
import testview.zhen.com.myapplication.IPersonBeanCallBack;

interface IOneAidlInterface {
    void basis(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
     PersonBean getPerson();
    void setPerson(in PersonBean a);
    void registerCallback(IPersonBeanCallBack callback);
    void unregisterCallback(IPersonBeanCallBack callback);

}

Copy the code

Ipersonbeancallback. aidl: The callback object used in ioneaidlInterface. aidl declares the class

package testview.zhen.com.myapplication;

interface IPersonBeanCallBack {
   void getName(String name);
   void getAge(int age);
}

Copy the code

Personbean.aidl: a declaration file of the entity class used in ioneaidlInterface.aidl. To be used in AIDL, the entity class must also implement the parcelable interface

package testview.zhen.com.myapplication; Parcelable PersonBean; parcelable PersonBean;Copy the code

The files above are in AIDL format, and the personBean.kt entity class files are implemented below

package testview.zhen.com.myapplication

import android.os.Parcel
import android.os.Parcelable

/**
 * Create by ldr
 * on 2019/11/5 16:14.
 */
class PersonBean() :Parcelable{
     var name: String=""
    var age: Int? = null

    constructor(parcel: Parcel) : this() {
        name = parcel.readString()
        age = parcel.readValue(Int::class.java.classLoader) as? Int
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeValue(age)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<PersonBean> {
        override fun createFromParcel(parcel: Parcel): PersonBean {
            returnPersonBean(parcel) } override fun newArray(size: Int): Array<PersonBean? > {return arrayOfNulls(size)
        }
    }
}
Copy the code

There are several files defined above, both client and server must have the same package name

  • Creating a Server

  1. AidlService:Service
class AidlService :Service(){
    companion object{
        val TAG = "AidlService"} override fun onBind(intent: Intent?) : IBinder? {returnVar remoteCallbackList = remoteCallbackList <IPersonBeanCallBack>() private val = object: IOneAidlInterface.Stub() {
        override fun registerCallback(callback: IPersonBeanCallBack?) {
            remoteCallbackList.register(callback)
         Log.i(TAG,"Server registration callback")
        }
        override fun unregisterCallback(callback: IPersonBeanCallBack?) {
            remoteCallbackList.unregister(callback)
       Log.i(TAG,"Server cancel callback")
        }
        override fun basis(anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String?) {
                Log.i(TAG,"Get the data passed in by the client anInt=${anInt},aLong=${aLong}" +
                        "aBoolean=${aBoolean} , aFloat=${aFloat}, aDouble=${aDouble}, aString=${aString}")
        }
        override fun getPerson(): PersonBean {
            return  PersonBean().also {
                    it.name = "Xiao Ming"Age = 18}} // Call back to notify client override fun when setting the namesetPerson(a: PersonBean?) {
           Log.i(TAG,"Get the entity Bean information name = passed by the client${a!! .name}And age =${a!! .age}"Key code 1) / / / / get the callback from remoteCallbackList object and invoke the callback object methods in the var n = remoteCallbackList. BeginBroadcast ()for (i in0 until n){ remoteCallbackList.getBroadcastItem(i).getName(a!! .name) remoteCallbackList.getBroadcastItem(i).getAge(a!! .age!!) }} remoteCallbackList. FinishBroadcast () / 2 / / here/key code is used to capture the inside appear abnormal, to prevent the service side have error exception thrown directly to the client override fun onTransact (code: Int, data: Parcel, reply: Parcel? , flags: Int): Boolean { try{return  super.onTransact(code, data, reply, flags)
            }catch (e:RuntimeException){
                Log.w(TAG, "Unexpected remote exception", e)
              throw e
            }
        }
    }
}
Copy the code

Bind the ioneaidlInterface.stub () object in the onbind() method. The remoteCallbackList is used to register callbacks, and the registration and unregistration of callbacks can be found here. The key code 1: remoteCallbackList getBroadcastItem (I) is used to get a callback object, beginBroadcast and finishBroadcast must be used. Key Code 2: This is invoked when the server side function appear problem, and then let the client throws Java lang. NullPointerException when for his capture in abnormal with print, otherwise the client somehow throw, But your server did not capture the reach abnormal throws really abnormal position (reference CSDN:blog.csdn.net/zxfrdas/art… Check out this article for details.)

  1. inAndroidManifest.xml
        <service android:name=".service.AidlService" android:exported="true">
            <intent-filter>
                <action android:name="testview.zhen.com.myapplication.service.aidservice"></action>
                <category android:name="android.intent.category.DEFAULT"></category>
            </intent-filter>
        </service>
Copy the code

Android: Exported =”true”; / / Add < ACition >; / / Add

; / / Add

  • Creating a Client

class MainActivity : AppCompatActivity() {

    companion object{
        val TAG = "MainActivity"
    }

    var isConnection = falsePrivate var servirveConnection = object: ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { isConnection =false} // Override fun onServiceConnected(name: ComponentName? , service: IBinder?) { iOne = IOneAidlInterface.Stub.asInterface(service) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)
        button1.setOnClickListener {
            if (isConnection) return@setAn OnClickListener iOne. Basis (1100 * 1000,true, f 1.0, 1.00,"hello world"Intent = intent ().also {it.setPackage();"testview.zhen.com.myapplication")
            it.action = "testview.zhen.com.myapplication.service.aidservice"
        }
        bindService(intent,servirveConnection,Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        if (isConnection) unbindService(servirveConnection)
    }

}

Copy the code

Key code 1 creates a servirveConnection object whose onServiceConnected(Name: ComponentName?) is implemented in an anonymous inner class. , service: IBinder?) In iOne = IOneAidlInterface. Stub. AsInterface (service) will iOne instantiation, the last key code 3, sets the intent of the package with the action to the service configuration information from the server, Passing bindService() implements the binding.

The following calls, specific details of the code see the above several classes of methods in the specific logic

  1. Click clientMainActivityThe buttonbutton1
        button1.setOnClickListener {
            if (isConnection) return@setAn OnClickListener iOne. Basis (1100 * 1000,true, f 1.0, 1.00,"hello world")}Copy the code

Aidlservice. kt Specifies the information printed on the server

The 2019-11-06 16:03:30. 277 5195-5210 /testview.zhen.com.myapplication I/AidlService: Get the data from the client anInt=1,aLong=1000000aBoolean=true, convertible =1.0, aDouble=1.0, aString=hello worldCopy the code

2. The client gets getPerson() and returns the Person object

        button2.setOnClickListener {
            if (isConnection) return@setOnClickListener // Call the Person() method to retrieve the PersonBean object var Person = ione.person log. I (TAG,"Person object information retrieved from the server name =${person.name},age =${person.age}")}Copy the code

Information printed by the client:

2019-11-06 17:59:33. 702, 30666-30666 / com. Mx. Testaidldemo I/MainActivity: returned from the server to get the Person object information name = xiao Ming, age = 18Copy the code
  1. Registering callback returns callback information

3.1 Client Callback Method Logic

private var  istb:IPersonBeanCallBack.Stub =  object : IPersonBeanCallBack.Stub() {
        override fun getName(name: String?) {
                Log.d(TAG,"Get the callback name information name =${name}")
        }
        override fun getAge(age: Int) {
            Log.d(TAG,"Get callback age information age =${age}")}}Copy the code

3.2 Client Call Registration callback calls setPerson

        button3.setOnClickListener {
            if (isConnection) return@setIone.registercallback (istb) log. I (TAG,"Register callback")
        }
        button4.setOnClickListener {
            if (isConnection) return@setOnClickListener
            iOne.person = PersonBean().apply {
                name = "Comrade"
                age = 40
            }
        }
        button5.setOnClickListener {
            if (isConnection) return@setUnregisterCallback (istb) log. I (TAG,"Cancel the callback")}Copy the code

Click button3 to register the callback client Log:

The 2019-11-07 10:20:18. 654, 17960-17960 / com. Mx. Testaidldemo I/MainActivity: register callbackCopy the code

The server Log:

The 2019-11-07 10:20:18. 652 17671-17694 /testview.zhen.com.myapplication I/AidlService: server register callbackCopy the code

Click button4 ion. person to trigger the server callback, client Log:

The 2019-11-07 10:22:28. 017, 17960-17960 / com. Mx. Testaidldemo D/MainActivity: Comrade to the name of the callback information name = 2019-11-07 10:22:28. 018, 17960-17960 / com. Mx. Testaidldemo D/MainActivity: get to callback the old information age = 40Copy the code

The server Log:

The 2019-11-07 10:22:28. 015 17671-17694 /testview.zhen.com.myapplication I/AidlService: get the client incoming information entity Bean name = comrade, age = 40Copy the code

Click button5 to Log out of the callback client:

The 2019-11-07 10:29:27. 351, 20514-20514 / com. Mx. Testaidldemo I/MainActivity: cancel the callbackCopy the code

The server Log:

The 2019-11-07 10:29:27. 350 20291-20306 /testview.zhen.com.myapplication I/AidlService: server cancel callbackCopy the code

AIDL callback

RemoteCallbackList is not a List. You cannot operate on it like a List. Traversing RemoteCallbackList must be done in the same way as using the AidlService class. BeginBroadcast and finishBroadcast must be used together, even if we just want to get the number of elements in RemoteCallbackList,

  • If you don’t usebeginBroadcast“And then go straight to itremoteCallbackList.getBroadcastItem(0)Then it will return nothing to you instead of the object you were expecting,
  • If you don’t usefinishBroadcastAnd then the procedureBind -> Use -> Logout -> Use, will throw exception saidjava.lang.IllegalStateException: beginBroadcast() called while already in a broadcast!!!!!!!!!

Directional TAG

On the Android website

All non-primitive parameters require a directional tag indicating which way the data goes . Either in , out , or inout . Primitives are in by default , and connot be otherwise . All non-basic parameters require an orientation Tag to indicate how data flows. The orientation Tag for basic parameters is in by default and only

  • in: Indicates that data can only be transferred from the client to the server
  • out: Indicates that data can only be transferred from the server to the client
  • inout: indicates that data can flow in both directions between the server and the client. The data flow is for the object passing the method in the client. In indicates that the server will receive the complete data of the object, but the object on the client will not change as the server changes the parameter. Out indicates that the server will receive the empty object of that object, but the client will synchronize the change after the server has made any changes to the received empty object. When inout is tag oriented, the server will receive complete information about the object from the client, and the client will synchronize any changes the server makes to the object.

AIDL authentication

Can have a look at Jane books about authentication related other authors of this article www.jianshu.com/p/69e5782dd…

Matters needing attention (all kinds of pits)

  1. End of file reached when AIDL resolution occurred for programs that could be run before import, error. Solution: AIDL has Chinese annotations. This error occurs when the IDE is upgraded to Android Studio 3.5. Delete all aiDL comments and run.

  2. The client thread will be suspended when the client calls the remote server method. If the remote method has a long time consuming operation, it will cause the ANR phenomenon of our client. In this case, we should avoid accessing the remote method in the client UI thread.

  3. When the server calls back to the client’s methods, it is also time consuming.

  4. Because server-side methods run in the server’s Binder thread pool, the methods themselves can perform time-consuming operations, so be sure not to open threads for asynchronous tasks in server-side methods.

  5. Binders die accidentally (such as when a service is killed or suddenly interrupted), and each Aidl accidental disconnect calls the linkToDeath and onServiceDisconnected methods, Just call the linkToDeath method before onServiceDisconnected (see blog.csdn.net/Small_Lee/a… www.jianshu.com/p/9af02aa66,…).