preface

This article focuses on the functions of AIDL and how to get started with AIDL project quickly

Introduction to the

A [Android] I [Interface] D [Definition] L [Language] Android Interface Definition Language. What it does: It allows the system to generate code for us to communicate across processes, nothing more. In other words, AIDL is just a tool for fast cross-process communication.

Quick learning

This article is intended to quickly implement the AIDL project, but more details will be covered in the next article.

  • Create an AIDL file on the server side to declare the Java beans and the interface to transport the call. [Declaration document]
  • Create the Service on the server side and implement the interface. [Create service]
  • The client binds the Service. [Binding service]
  • The client invokes the server interface. [Cross-process call]

The service side

To create the server project, start by creating the AIDL folder in app/ SRC /main.




Create a carrierMessageBean

First we create a Java Bean object for transport in this AIDL folder (the package name doesn’t matter) and implement the Parcelable interface (the Parcelable plugin is recommended), since interprocess communication requires converting this object into a sequence of bytes for transport or storage. (Carrier of delivery)

public class MessageBean implements Parcelable {
    private String content;// Request content
    private int level;// Important level`````` Get set method ``````@Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.content);
        dest.writeInt(this.level);
    }

    // If you want to support directional tag out,inout, override this method
    public void readFromParcel(Parcel dest) {
        // Note that the order of values read here should be the same as in the writeToParcel() method
        this.content = dest.readString();
        this.level = dest.readInt();
    }
    protected MessageBean(Parcel in) {
        this.content = in.readString();
        this.level = in.readInt();
    }
    public MessageBean() {

    public static final Creator<MessageBean> CREATOR = new Creator<MessageBean>() {
        @Override
        public MessageBean createFromParcel(Parcel source) {
            return new MessageBean(source);
        }

        @Override
        public MessageBean[] newArray(int size) {
            return newMessageBean[size]; }}; }Copy the code
Create an AIDL fileMessageBean.AIDL

Since AIDL is the language specification for AIDL files, we must convert MessageBeans into AIDL files for other AIDL calls and interactions. That’s it. Two lines of code in one file. (This file should be placed in the same package as the Java Bean.)

package qdx.aidlserver;// Manually guide packets
parcelable MessageBean;/ / parcelable is lowercaseCopy the code

Differences between AIDL files and Java files

  • File type: Files written in AIDL have an.aidl suffix, not.java.
  • Data types: AIDL default support some data type, in the use of these data types is not to need to guide package, but in addition to these types of data types, before use must guide package, even if the target file and is currently being written. The AIDL file under the same package, in Java, this kind of situation is not to need to guide package. For example, we have now written two files, one called idemandManager.java and the other idemandManager.aidl, under the qdx.aidlServer package, Now, we need the aidl file using MessageBean objects, so we must in the aidl file import write QDX. Aidlserver. MessageBean; Even though.java files and.aidl files are in the same package.

  • Default supported data types Eight basic data types in Java (byte, short, int, Long, float, double, Boolean, char) String and CharSequence List: Map: All elements in a Map must be of a type supported by AIDL, including key and value Parcelabel: All objects that implement the Parcelabel interface AILD: All AIDL interfaces themselves can also be used in AIDL files

Create an AIDL fileIDemandManager.AIDL

We created the IDemandManager interface to implement the passing method. In addition, if there is a transport carrier in the method, we must specify the orientation tag(in,out,inout).

  • In: The client data object flows to the server, and the modification of the data object by the server does not affect the client.
  • Out: The data object flows from the server to the client. (At this time, the data object received by the server is empty. The server can modify the data object and send it to the client.)
  • Inout: A combination of these two data streams. (However, this tag is not recommended because it will increase overhead)
package qdx.aidlserver;

import qdx.aidlserver.MessageBean;
import qdx.aidlserver.IDemandListener;

interface IDemandManager {
     MessageBean getDemand();

     void setDemandIn(in MessageBean msg);// Client -> server

     // Both out and inout need to override the readFromParcel method of MessageBean
     void setDemandOut(out MessageBean msg);// Server -> client

     void setDemandInOut(inout MessageBean msg);// Client <-> server
}Copy the code
Pit and perfect

The article isn’t much, but it’s full of gems, especially the following three tips to avoid a disaster. 1. Methods with different parameters of the same method name cannot exist in xxx.aidl. 2. The entity class in xxx.aidl must have the specified tag. 3. After writing the aiDL file in Android Studio, you need to add the aiDL path to the Android {} method in the build.gradle file.

sourceSets {
    main {
        java.srcDirs = ['src/main/java'.'src/main/aidl']}}Copy the code
Create a Service

Finally, we need to create a service that can handle requests from clients or push messages to clients on a regular basis.

public class AIDLService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return demandManager;
    }

    //Stub inherits Binder internally with cross-process transport capabilities
    IDemandManager.Stub demandManager = new IDemandManager.Stub() {
        @Override
        public MessageBean getDemand(a)throws RemoteException {
            MessageBean demand = new MessageBean("First, salute when you see me.".1);
            return demand;
        }

        @Override
        public void setDemandIn(MessageBean msg) throws RemoteException {// The client data flows to the server
            Log.i(TAG, "Programmer :" + msg.toString());
        }

        @Override
        public void setDemandOut(MessageBean msg) throws RemoteException {// The server data flows to the client
            Log.i(TAG, "Programmer :" + msg.toString());// the MSG content must be empty

            msg.setContent("I don't want explanations. Get all your work done before you leave work!");
            msg.setLevel(5);
        }

        @Override
        public void setDemandInOut(MessageBean msg) throws RemoteException {// Data interchange
            Log.i(TAG, "Programmer :" + msg.toString());

            msg.setContent("Change the user interaction color to pink.");
            msg.setLevel(3); }}; }Copy the code

Finally, we register the service Action as the service name in the manifest file, from which the client can implicitly start the service (com.tengxun.aidl). On Meizu’s mobile phone, the system prohibits the permission to start the service implicitly, so be sure to enable the permission to start the project automatically in the mobile phone Manager/Permission Management /.

        <service
            android:name=".AIDLService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.tengxun.aidl" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

        </service>Copy the code

The client

Create a client project

Copy the AIDL folder

Copy the aiDL folder created on the server to the app/ SRC /main directory on the client and associate the AIDL path with gradle.build.

    sourceSets {
        main {
            java.srcDirs = ['src/main/java'.'src/main/aidl']}}Copy the code
Open the service
        Intent intent = new Intent();
        intent.setAction("com.tengxun.aidl");/ / service action
        intent.setPackage("qdx.aidlserver");// the package name of the aiDL file in the aidl folder
        bindService(intent, connection, Context.BIND_AUTO_CREATE);Copy the code
Associate the object and call the method

After the service binding is successful, we convert the idemandManager.stub object on the server side into the AIDL interface type object required by our client side through asInterface, and this transformation process is process-specific (detailed in the next chapter).


    private IDemandManager demandManager;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            demandManager = IDemandManager.Stub.asInterface(service);// Once we have this object, we can use it for inter-process method calls and transfers.
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };Copy the code

At this point we can communicate across processes through AIDL.

Additional skills (timed push messages)

The above steps have already achieved normal cross-process communication. If we want to push messages to the client project periodically, how do we do this in the cross-process? —————— uses the observer pattern (a collection of multiple callback methods)——————

Server project (push message)

And callback method is the same as the usually, first of all, we create a callback interface in the AIDL folder IDemandListener. AIDL but the directional tag should pay attention, there may be some children’s shoes think we send a message to the client in the server, it should not be set out tag? In fact, the so-called “Server” and “Client” are relative in Binder communication, because we copy aiDL files in the Client project, the Client project can not only send messages as “Client”, but also receive messages pushed by the Server project and upgrade to “Server”. This functionality relies on AIDL’s stubs and proxies… (See more in the next article.) So the process of onDemandReceiver pushing messages is actually equivalent to a “Client.”

package qdx.aidlserver;

import qdx.aidlserver.MessageBean;

interface IDemandListener {

     void onDemandReceiver(in MessageBean msg);// Client -> server

}Copy the code

We also add bind/unbind listening methods to the idemandManager.aidl file

     void registerListener(IDemandListener listener);

     void unregisterListener(IDemandListener listener);Copy the code

Finally, Clean Project, bind/unbind two methods in multiple places in idemandManager.stub.

    IDemandManager.Stub demandManager = new IDemandManager.Stub() {
        ` `` `` `
        setDemandIn/outMethods such as` `` `` `

        @Override
        public void registerListener(IDemandListener listener) throws RemoteException {
        }

        @Override
        public void unregisterListener(IDemandListener listener) throws RemoteException {

        }
    };
Copy the code




So, the key step comes up that we usually useList<IDemandListener>To store a collection of listening interfaces

But!

Since transferring the same object from the client across processes will generate different objects on the server!

In the process of cross-process communication, the object carrier is not sent from the client to the server like a Courier. But through the middleman civet cat for prince some means of transmission.

Binder objects are all the same, so Android provides RemoteCallbackList with this feature to store a collection of listener interfaces.

This RemoteCallbackList automatically implements thread synchronization internally, and it is essentially aArrayMap, so we don’t need to do additional thread synchronization when we use it to bind/unbind.

        private RemoteCallbackList<IDemandListener> demandList = new RemoteCallbackList<>();

        @Override
        public void registerListener(IDemandListener listener) throws RemoteException {
            demandList.register(listener);//
        }

        @Override
        public void unregisterListener(IDemandListener listener) throws RemoteException {
            demandList.unregister(listener);
        }Copy the code

Finally, we use a Handler to send messages on time. BeginBroadcast () is used to get the number of elements in the RemoteCallbackList. Both beginBroadcast() and finishBroadcast() must be paired, if only to get the number of elements in the set.

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if(demandList ! =null) {
                int nums = demandList.beginBroadcast();
                for (int i = 0; i < nums; i++) {
                    MessageBean messageBean = new MessageBean("I threw the", count);
                    count++;
                    try {
                        demandList.getBroadcastItem(i).onDemandReceiver(messageBean);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                demandList.finishBroadcast();
            }

            mHandler.sendEmptyMessageDelayed(WHAT_MSG, 3000);// Push the message every 3s}};Copy the code

Client project (receiving timing push)

The idemandListener.stub is created (our client project is “Server” in terms of the push process) and added to demandManager.

    IDemandListener.Stub listener = new IDemandListener.Stub() {
        @Override
        public void onDemandReceiver(final MessageBean msg) throws RemoteException {
            // This method runs in a Binder thread pool, non-UI threads}}; demandManager.registerListener(listener);Copy the code

conclusion

A simple general-purpose AIDL project has been created. According to my personal habit, before learning and understanding a project knowledge, if the project does not RUN successfully, then the learning process will become a little boring, so we first go through the process, and then study with various problems, so that we can understand and view the knowledge more deeply. Of course, it’s also a bit of a rush… AIDL (2)

The project download

Download.csdn.net/download/qi…