Exploring the principle of AIDL directional tag in out in out

Keywords: Android Java less REST

The last article, "Analyzing AIDL Principles from an Example" This paper analyses the process of accomplishing cross-process communication through AIDL, and finally raises a question: what are the meanings of directional tag: in, out and in out in the parameter list of Aidl grammar? (Reading this article requires a certain foundation of aidl, if you see the clouds and mists, you can look at it first. Last article)

Now suppose I don't know (though there are more or less conjectures), the usual way is to find out who wrote it, so I go to the Android website to search for aidl keywords, and lie down in the following paragraph in a long article.

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 cannot be otherwise.
Caution: You should limit the direction to what is truly needed, because marshalling parameters is expensive.

Generally speaking, all parameters of non-basic data types need to specify a direction tag to indicate the direction of data flow when they are passed, which can be in, out or in out. The basic data type is in by default and cannot be modified to other types. There's also a warning that you should limit your direction according to your actual needs, because the cost of parameter choreography is huge (after all, achieving requirements for performance is our goal).

There are also a few words on the official website about the directional tag explanation, it seems that it can not explain clearly. Very confused, what to do? If you are Di Renjie, you can still say: Yuanfang, what do you think? At that time, Yuanfang said with a clear face: when the clue is broken and there is no clue, then return to the essence of the matter and find a breakthrough. Well, recall the process of using aidl.

Create aidl file - > Build - > Generate corresponding java file - > Implement its inner class Stub - > onBind and return an instance of Stub implementation class...

Notice that the role of aidl files is to generate java files, and ultimately participate in compiling the java files, which seems to be a breakthrough: as long as in, out and intout are used in aidl files to define several methods separately, to see the difference between the generated java files, from this point of view, it seems that we can deduce the meaning of them? Very excited, can't stop at all, or use Last article Example:

// IDownloadCenter.aidl
package com.jdqm.downloadcenter.aidl;
import com.jdqm.downloadcenter.aidl.DownloadTask;

interface IDownloadCenter {
    //Add Download Tasks
    void addDownloadTaskIn(in DownloadTask task);
    void addDownloadTaskOut(out DownloadTask task);
    void addDownloadTaskInout(inout DownloadTask task);
}

In the aidl file, three methods are defined. The parameters are modified with in, out and in out tag s. Build will take a look at the generated java files.

1,in

@Override
public void addDownloadTaskIn(com.jdqm.downloadcenter.aidl.DownloadTask task) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        if ((task != null)) {

            //Note that the parameter here is not null written1
            _data.writeInt(1);

            //Serialize the passed arguments to _data
            task.writeToParcel(_data, 0);
        } else {

            //Write with parameter null0
            _data.writeInt(0);
        }
        mRemote.transact(Stub.TRANSACTION_addDownloadTaskIn, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

You can see that when in-directed tag s are used, the parameters we pass are serialized into _data, so onTransact on the server side can receive the parameters we pass when we initiate a remote call.

case TRANSACTION_addDownloadTaskIn: {
    data.enforceInterface(DESCRIPTOR);
    com.jdqm.downloadcenter.aidl.DownloadTask _arg0;

    //Read the int s written in tansact to see if there are parameters
    if ((0 != data.readInt())) {

        //Deserialize parameters
        _arg0 = com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR.createFromParcel(data);
    } else {
        _arg0 = null;
    }

    //Call the target method
    this.addDownloadTaskIn(_arg0);
    reply.writeNoException();
    return true;
}

Here we come to the conclusion that using in-directed tag to modify parameters, we can get an object with the same parameter value in the server-side target method (note that it is not the same object, they are in different virtual machine instances).

2,out

@Override
public void addDownloadTaskOut(com.jdqm.downloadcenter.aidl.DownloadTask task) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);

        //There's no task we passed in here at all written to _data
        mRemote.transact(Stub.TRANSACTION_addDownloadTaskOut, _data, _reply, 0);
        _reply.readException();

        //_reply.readInt()This value is written by the server
        if ((0 != _reply.readInt())) {

            //The server-side onTransact is deserialized from _reply to write the value of reply, so the argument we passed is modified.
            task.readFromParcel(_reply);
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

It can be found that using out directional tag, the parameters we passed will not be passed to the server, and if the value of _reply.readInt() is not zero, the parameters we passed will be deserialized and modified. Questions arise, since parameters are not passed to the server, where do the parameters come from when the server invokes the target method? Look at onTransat:

case TRANSACTION_addDownloadTaskOut: {
    data.enforceInterface(DESCRIPTOR);
    com.jdqm.downloadcenter.aidl.DownloadTask _arg0;

    //Instead of reading parameters from data, we directly new a new object.
    _arg0 = new com.jdqm.downloadcenter.aidl.DownloadTask();

    //Pass the new object directly to the target method
    this.addDownloadTaskOut(_arg0);
    reply.writeNoException();
    if ((_arg0 != null)) {

        //Parametric write-back, markup1
        reply.writeInt(1);
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {

        //Parametric write-back, markup0
        reply.writeInt(0);
    }
    return true;
}

The question just now has been answered. Originally, a new object was created directly through the new keyword on the server, and then the new object was passed to the target method, which returned the parameters serialized into reply.

Some conclusions are drawn: using the directional tag out parameters, the incoming arguments will not be passed to the server when the remote call is initiated, but a new object will be created at the server to the target method, which will be returned to the client when the target method returns. In addition, it is found that the server calls the parameterized constructor to create new objects, which means that the class corresponding to your parameter must have a parameterized constructor.

3,inout

Maybe after reading the in and out directional tag s, you have begun to guess that in out may be a combination of them. Is that right? Then look down.

@Override
public void addDownloadTaskInout(com.jdqm.downloadcenter.aidl.DownloadTask task) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        if ((task != null)) {
            _data.writeInt(1);

            //Write the parameter to _data, which is related to the directional tag inAgreement
            task.writeToParcel(_data, 0);
        } else {
            _data.writeInt(0);
        }
        mRemote.transact(Stub.TRANSACTION_addDownloadTaskInout, _data, _reply, 0);
        _reply.readException();
        if ((0 != _reply.readInt())) {
            //Deserialize the value of the reply written to the server and modify the client-side object
            task.readFromParcel(_reply);
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

As you can see, the client does write the parameters in _data (which is the same as in), call task.readFromParcel(_reply) to modify the parameters we passed (which is the same as out), and then look at the server.

case TRANSACTION_addDownloadTaskInout: {
    data.enforceInterface(DESCRIPTOR);
    com.jdqm.downloadcenter.aidl.DownloadTask _arg0;
    if ((0 != data.readInt())) {

        //Deserializing parameters passed from the client from data
        _arg0 = com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR.createFromParcel(data);
    } else {
        _arg0 = null;
    }
    this.addDownloadTaskInout(_arg0);
    reply.writeNoException();
    if ((_arg0 != null)) {
        reply.writeInt(1);

        //Serialize _arg0 to repley, and the client will deserialize it
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        reply.writeInt(0);
    }
    return true;
}

The server reads the data from the client (no new object, no out feature), and writes back the parameters to the client. So basically you have your own conclusions about these three directional tag s, and the rest is a summary.

  1. The parameters modified by directional tag in are serialized and passed to the server, which deserializes to get a new object with the same value.
  2. The client does not serialize the parameters modified by tag out, but calls the parametric construction method to create a new object. When the target method returns, the parameters are written to reply and returned to the client.
  3. Oriented tag in out is basically a union of in and out. Why is it basically that out will create a new object through the new keyword on the server side, and in out has got a new object through the data from the deserialized client, so there is no need for a new one.

The following is an attempt to validate the above conclusions through some examples:

//aidl interface method
void addDownloadTaskIn(in DownloadTask task);

//Server-side implementation
@Override
public void addDownloadTaskIn(DownloadTask task) throws RemoteException {
    //Print result: {id=1, url='url of directional tag in'}
    //It can be seen that the parameters of the target method are exactly the same as those passed on.
    Log.d(TAG, "addDownloadTaskIn: " + task);

    //Modify the id field of the parameter
    task.setId(110);
}

//Client Call
DownloadTask taskIn = new DownloadTask(1, "url of directional tag in");
downloadCenter.addDownloadTaskIn(taskIn);

//Print result: {id=1, url='url of directional tag in'}
//The server modifies the id to 110, but the client object is not modified
Log.d(TAG, taskIn.toString());
//aidl interface method
void addDownloadTaskOut(out DownloadTask task);

//Server-side implementation
@Override
public void addDownloadTaskOut(DownloadTask task) throws RemoteException {
    //Print result: {id=0, url='null'}
    //As you can see, it's not the value passed by the calling method, it's actually the new object obtained by calling the parametric constructor.
    Log.d(TAG, "addDownloadTaskOut: " + task);

    //Two fields of task
    task.setId(119);
    task.setUrl("change by service");
}

//Client Call
DownloadTask taskOut = new DownloadTask(2, "url of directional tag out");
downloadCenter.addDownloadTaskOut(taskOut);

//Print result: {id=119, url='change by service'}
//The server modifies the parameters and the client modifies the parameters.
Log.d(TAG, taskOut.toString());
//aidl interface method
void addDownloadTaskInout(inout DownloadTask task);

//Server-side implementation
@Override
public void addDownloadTaskInout(DownloadTask task) throws RemoteException {
    //Print result: {id=3, url='url of directional tag inout'}
    //It can be seen that the parameters of the target method are exactly the same as those passed on.
    Log.d(TAG, "addDownloadTaskInout: " + task);

    //Two fields of task
    task.setId(120);
    task.setUrl("change by service");
}

//Client Call
DownloadTask taskInout = new DownloadTask(3, "url of directional tag inout");
downloadCenter.addDownloadTaskInout(taskInout);
//Print result: {id=120, url='change by service'}
//The server modifies the parameters and the client modifies the parameters.
Log.d(TAG,  taskInout.toString());

The experimental results show that the previous analysis is correct. After understanding these three directional tags, you can choose the appropriate tag according to your business needs. In fact, most of the cases are in. But I still have a question. Last article It is mentioned that if the keyword oneway is in front of the method, when the client invokes transact, the thread will not be suspended waiting for the server to return, which seems to contradict the directed tag out and inout. Take a chestnut:

oneway void addDownloadTaskOut(out DownloadTask task);

After Build, the error is reported directly, and inout can no longer use oneway. So one way means in this way. Wait, what if the method has a return value?

oneway int addDownloadTaskIn(in DownloadTask task);

Errors are still reported after Build. Although you're in, it's not good to have a return value, so this oneway has to add a condition: the return value type is void.

Note: The above conclusions or examples are based on the correct implementation of Parcelable interface parameters.

What syntax does AIDl have?

  1. What kinds of aidl files are there?
    One is the Aidl file corresponding to the data type that is not supported by default, such as DownlaodTask.aidl, and the other is the Aidl that defines the interface. For example:
// DownloadTask.aidl
package com.jdqm.downloadcenter.aidl;

parcelable DownloadTask;
// IDownloadCenter.aidl
package com.jdqm.downloadcenter.aidl;

interface IDownloadCenter {
    //Add Download Tasks
    void addDownloadTask(in DowloadTask task);

    //Query all added download tasks
    List<DownloadTask> getDownloadTask();
}

If you use the default supported data type, the first type of aild file is not needed, so you know why it's called "Interface Language".

  1. What data types are supported by default?

(1) All basic data types of Java only support directional tag in;
(2) String only supports directional tag in;
(3) CharSequence, which only supports directional tag in;
(4) List, where the elements must be the data type supported by aidl, including (1) (2) (3) parcelable defined by other Aidl files; in addition, the recipient always gets ArrayList;
(5) Map, where the elements must be data types supported by aidl, including (1) (2) (3) and parcelable defined by other Aidl files; additionally, the recipient always gets HashMap.

  1. It must be written in the Java language. It also provides some keywords, such as parcelable, onway, in, out, in out. The meaning and usage of these keywords have been mentioned before.

  2. When using a custom Parcelable type, import statements must be used, even in the same package.

  3. The definition of method can only be included in the aidl interface, and some static fields can not be exposed through it.

Finally, if you have different opinions, you are welcome to leave your footprints, thank you!

Posted by Dingbats on Tue, 18 Dec 2018 12:33:04 -0800