AIDL for Android process communication

        I wrote an article on process communication before, Messenger of Android process communication

        In fact, as mentioned before, the communication of Messenger is based on the upper layer encapsulation of AIDL. The data transmitted by Messenger is encapsulated by Bundle, so the data type of Messenger communication only supports the basic data types provided by Bundle, such as: String, Int, Double, etc. If you want to pass custom data types, you must pass AIDL. In addition, Messenger processes the information sent by the client in a serial manner. If there are a large number of concurrent requests, the efficiency of Messenger is very low and it is not suitable.

        The full name of AIDL is: Android Interface Define Language, Android Interface Definition Language.

        AIDL is divided into two modules, a Client and a Service

        Client starts the target service to obtain the Service proxy, and communicates through the proxy

===================================================

                                         Client side only

===================================================

For the time being, we don't care how the server is implemented. Let's take a scenario. The server has already encapsulated the interface, and the client only needs to start the call. Let me take "SUNMI Cash Box" as an example. Sunmi’s official website says that they need to open their cash drawers and communicate through AIDL. We don’t know the implementation of their server, and we don’t need to know. They only provide the interface. ICallback.aidl and IWoyouService.aidl

The interface they provide is this:

package woyou.aidlservice.jiuiv5;

import woyou.aidlservice.jiuiv5.ICallback;


interface IWoyouService
{	

	/**
	* 打开钱柜
	*/
	void openDrawer(in ICallback callback);
	
	/**
	* 取钱柜累计打开次数
	*/		
	int getOpenDrawerTimes();

}
/**
 * 打印服务执行结果的回调
 */
interface ICallback {

	/**
	* 返回执行结果
	* @param isSuccess:	  true执行成功,false 执行失败
	*/
	oneway void onRunResult(boolean isSuccess, int code, String msg);
	
}

After getting the interface, create a new AIDl file according to their package name. First, in the directory package at the same level as Java, the interface package name they provide is woyou.aidlservice.jiuiv5 , then we must create a new package name directory with the same name

We will create new ICallback and IWoyouService aidl files in the woyou.aidlservice.jiuiv5 directory

After the creation is completed, the aidl directory will appear in the Studio, which corresponds to the aidl file we just created, and the newly created IWoyouService.aidl will have a default interface. We delete it and write the interface provided by the server

In the same way, we create a new ICallback.aidl

Well, the new ones are all done. Create a newsletter now

package com.example.ipcdemo

import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import woyou.aidlservice.jiuiv5.ICallback
import woyou.aidlservice.jiuiv5.IWoyouService

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<View>(R.id.tvStartServer).setOnClickListener {
            // 启动服务
            startMyServer()
        }
        findViewById<View>(R.id.tvAction).setOnClickListener {
            // 调用接口
            woyouService?.openDrawer(callback)
        }
    }

    var callback: ICallback = object : ICallback.Stub() {
        override fun onRunResult(isSuccess: Boolean, code: Int, msg: String?) {
            // 执行回调
        }
    }
    private var woyouService: IWoyouService? = null

    // 继承 ServiceConnection
    private val serviceConnection = object : ServiceConnection {

        override fun onServiceDisconnected(name: ComponentName?) {// 服务断开
            woyouService = null
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            // 服务连接成功 通过 Stub.asInterface 获取服务
            woyouService = IWoyouService.Stub.asInterface(service)
        }
    }

    // 启动绑定服务
    private fun startMyServer() {
        val intent = Intent()
        // 包名 必须是我们新建aidl 的包名,因为这个包名也是服务对应的包名
        intent.setPackage("woyou.aidlservice.jiuiv5")
        // 对应服务
        intent.action = "woyou.aidlservice.jiuiv5.IWoyouService"
        // 绑定服务
        applicationContext.bindService(intent, serviceConnection, 1)
    }
}

OK In this way, the process communication of the client is completed, and the functions of other processes can be directly called.

The whole process is:

1. The client starts to bind the target server, here

Package name: woyou.aidlservice.jiuiv5 ,

Action 为 :woyou.aidlservice.jiuiv5.IWoyouService

2. After the binding is successful, call back a Binder object through ServiceConnect, obtain the Binder object, and convert it into its own aidl interface instance.

3. Call the instance method for communication.

===================================================

                                         Client side + Service side

===================================================

However, the above is only the function of the client, so how to operate the Client + Service ?

Client has already been introduced, let’s start with how to operate Service

In order to show more clearly that it is process communication, I write Client and Service separately in an App,

========= Service ============

OK, let's take a look at how to implement the Service side. In fact, there are three files required.

1: aidl interface

2: Custom Service

3:AndroidManifest.xml

aidl interface

Corresponding to the code, three interfaces are defined here,

// IMyAidlInterface.aidl
package com.example.ipcservice;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    void sayHi(String content);

    String getName();

    int getAge();
}

As for how to create a new aidl file, it has already been said above

Custom Service

For the corresponding code, rewrite onBind(). When the service is bound, it will call back this method and return the defined Binder instance to the caller.

    Question : Why is the aidl interface instance a binder object?

    answer :

             In fact, after the aidl is created, the compiled file is in the directory: build\generated, click in and you can see that the compiled aidl file inherits from the binder, so what is returned is a Binder object. In fact, process communication depends on this Binder, because this knowledge point does not belong to the knowledge point of this article, so the author will not say more, and interested students can study it by themselves

Well, the following is the custom Service

package com.example.ipcservice

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log

class MyService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d("ServiceLog", "onCreate")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("ServiceLog", "onDestroy")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("ServiceLog", "onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent?): IBinder {
        Log.d("ServiceLog", "onBind")
        return MyBinder()
    }

    class MyBinder : IMyAidlInterface.Stub() {
        override fun getName() = "My Name is Leonardo Dicaprio"

        override fun getAge() = 40

        override fun sayHi(content: String?) {
            Log.d("ServiceLog", "say Hi : $content")
        }

    }

}

AndroidManifest.xml   

To mention, the custom Action must be configured

Ok, Service has been configured

========= Client ============

Then it's the client side, which is actually the top one,

1: Create aidl file

2: Customize connectService to get the server proxy

3: Call the interface for communication

Create aidl file

Customize connectService to get server proxy

package com.example.ipcclient

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.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.ipcservice.IMyAidlInterface

class MainActivity : AppCompatActivity() {

    companion object {
        private const val TAG = "LogClient"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<View>(R.id.tvStartService).setOnClickListener {
            Log.d(TAG, "StartService click")
            startMyServer()
        }
        findViewById<View>(R.id.tvSayHi).setOnClickListener {
            Log.d(TAG, "SayHi click")
            mIMyAidlInterface?.sayHi("Hello my name is Pitt")
        }
        findViewById<View>(R.id.tvGetName).setOnClickListener {
            Log.d(TAG, "GetName click")
            Log.d(TAG, "GetName ${mIMyAidlInterface?.name ?: ""}")

        }
        findViewById<View>(R.id.tvGetAge).setOnClickListener {
            Log.d(TAG, "GetAge click")
            Log.d(TAG, "GetName ${mIMyAidlInterface?.age ?: ""}")
        }
    }

    private var mIMyAidlInterface: IMyAidlInterface? = null

    // 继承 ServiceConnection
    private val serviceConnection = object : ServiceConnection {

        override fun onServiceDisconnected(name: ComponentName?) {// 服务断开
            Log.d(TAG, "onServiceDisconnected")
            mIMyAidlInterface = null
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d(TAG, "onServiceConnected")
            // 服务连接成功 通过 Stub.asInterface 获取服务
            mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
        }
    }

    // 启动绑定服务
    private fun startMyServer() {
        val intent = Intent()
        // 包名 必须是我们新建aidl 的包名,因为这个包名也是服务对应的包名
        intent.setPackage("com.example.ipcservice")
        // 对应服务
        intent.action = "com.example.ipcservice.MyService"
        // 绑定服务
        applicationContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }
}

I wrote this in the Activity

Look at the results of the operation

Client side:

Service side:

OK, the running result is fine

===================================================

                                         Client side + Service side custom data type

===================================================

The above only shows the basic data types, now add custom data types

Before creating a new one, let’s talk a little bit off topic. In fact, there is no need to create any files on the client side. The classes related to communication can be copied on the server side. This can avoid writing errors or leaks when creating new ones. But you must ensure that the package name is consistent.

Well, first create a custom People class on the Service side, which must implement the Parcelable interface

The class code is:

package com.example.ipcservice

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

/**
 * Time:2021/6/17 11:33
 * Author: Leonardo Dicaprio
 * Description:
 */
class People() : Parcelable {

    var age: Int = 0
    var name: String = ""

    constructor(parcel: Parcel) : this() {
        age = parcel.readInt()
        name = parcel.readString().toString()
    }

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

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<People> {
        override fun createFromParcel(parcel: Parcel): People {
            return People(parcel)
        }

        override fun newArray(size: Int): Array<People?> {
            return arrayOfNulls(size)
        }
    }

    override fun toString(): String {
        return "芳名: $name  芳龄: $age"
    }
}

Then create People.aidl in the AIDL directory

The code is:

// People.aidl
package com.example.ipcservice;
import com.example.ipcservice.People;
parcelable People;

Then in the interface, add an interface to get People.

We have Service to return a People

OK, so the Service side is completed

Now go to the client side to update the interface, you can copy the service side aidl directory and the entire directory

Then copy the People class of Service, as follows:

Call the interface and test it:

OK test passed

Similarly, you can also add a List to test

Service side

The client can directly copy the IMyAidlInterface.aidl of the server

test passed

Demo :

https://github.com/LeoLiang23/IpcClient.git

https://github.com/LeoLiang23/IpcService.git

Guess you like

Origin blog.csdn.net/Leo_Liang_jie/article/details/117957917