Understand the aidl principle and system generated source code

Source: Internet
Author: User
Tags int size static class stub mremote

Tools are easy to use, but one drawback is that you will have to rely on the instructions. For my memory is very poor person, every use of tools to go through the manual is a very painful thing, and there are many pits will forget how to fill. Based on this, I often feel as if I would like to understand the tool manufacturing methods, and then build a good, as if my brain is very impressed with this kind of things, so the next time I have a problem I write a tool directly with the efficiency, than to check the instructions and re-visit the pit to a lot higher. Today, I'm going to tear down this tool for Aidl.

Part1. Principle
Talking about the principle of interprocess communication, we first refer to a COM concept--proxy/stub structure (proxy/stub structure) above:

Say a good example of understanding:
You go to the ATM to withdraw money, you are the customer, the teller machine is your agent; you don't care where the money is placed, you just want to see enough or more of it coming out of the exit (that's COM's transparency). The operation between you and the bank is entirely a cash machine agent implementation. (This is activity)
Your withdrawal request passes through the teller machine, to the other end, the bank's server (here is the remote service), he does not need to know where you collect the money, he is concerned about your identity, and how much you withdraw. When he confirms your permission, the corresponding operation, return the results of the operation to the teller machine, the teller machine according to the server returned results, from the safe to take the corresponding amount of money to you. After you remove the card, the operation is completed.
Cash machines are not directly connected to the server, they also have a "stub" between the Teller machine and the stub communication, the Server and the stub communication. In a sense, a stub is a proxy for a server. This stub of English will stub, we generated the Aidl class, is the stub + Proxy, will see later.
Android is the traditional C/s architecture to add a layer to achieve the IPC.

Part2. SOURCE Analysis
First look at a standard through the tool generated by the Aidl class and it inside the JavaBean, we all know that the bean must inherit parcelable interface, as for the Aidl class how to generate, how to write the file, if you do not know I think this article you do not have to look down:

/* * This file is auto-generated.
 Do not MODIFY. * Original FILE:D:\\DEVELOP\\WORKSPACEFORJOB\\ANDROIDTEST\\SRC\\COM\\AMURO\\AIDL\\IPERSONMANAGER.AIDL * * Package

Com.amuro.utils;
     Public interface Ipersonmanager extends Android.os.IInterface {/** * Local-side IPC implementation stub class.
        */public static abstract class Stub extends Android.os.Binder implements Com.amuro.aidl.IPersonManager {

        Private static final String descriptor = "Com.amuro.aidl.IPersonManager";
         /** * Construct The stub at attach it to the interface.
        */Public Stub () {this.attachinterface (this, descriptor); }/** * Cast an IBinder object to an Com.amuro.aidl.IPersonManager interface, * generating a
         Proxy if needed. */public static Com.amuro.aidl.IPersonManager asinterface (Android.os.IBinder obj) {if ((obj = = null)) {Return null;
            } android.os.IInterface iin = Obj.querylocalinterface (descriptor); if ((iin! = null) && (iin instanceof Com.amuro.aidl.IPersonManager)) {return (COM.
            Amuro.aidl.IPersonManager) iin);
        } return new Com.amuro.aidl.IPersonManager.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 flag s) throws android.os.RemoteException {switch (code) {Case Interface_tran
                    Saction: {reply.writestring (descriptor);
                return true;
                    } case Transaction_save: {data.enforceinterface (descriptor); Com.amuro.entity.Person _arg0; if ((0! = Data.readint ())) {_arg0 = Com.amuro.entity.Person.CREATOR.createFro
                    Mparcel (data);
                    } else {_arg0 = null;
                    } this.save (_ARG0);
                    Reply.writenoexception ();
                return true;
        }} return Super.ontransact (code, data, reply, flags); } private static Class Proxy implements Com.amuro.aidl.IPersonManager {private Android.os.IB

            Inder Mremote;
            Proxy (Android.os.IBinder remote) {mremote = remote;
            } @Override Public Android.os.IBinder Asbinder () {return mremote;
            } public String Getinterfacedescriptor () {return descriptor; }

            @Override public void Save (Com.amuro.entity.Person person) 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 ((person! = null)) {_data.writeint (1);
                    Person.writetoparcel (_data, 0);
                    } else {_data.writeint (0);
                    } mremote.transact (Stub.transaction_save, _data, _reply, 0);
                _reply.readexception ();
                    } finally {_reply.recycle ();
                _data.recycle (); }}} static final int TransaCtion_save = (Android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
} public void Save (Com.amuro.entity.Person person) throws android.os.RemoteException;
 }
Package com.amuro.entity;
Import Android.os.Parcel;

Import android.os.Parcelable;
    public class Person implements parcelable {private int id;

    private String name;
        Public person () {} public person (int ID, String name) {this.id = ID;
    THIS.name = name;
    } public int getId () {return id;
    } public void setId (int id) {this.id = ID;
    } public String GetName () {return name;
    } public void SetName (String name) {this.name = name;
    } @Override public int describecontents () {return 0; } @Override public void Writetoparcel (Parcel dest, int flags) {//writes data from Javanbean to Parcel Dest.writein
        T (this.id);
    Dest.writestring (this.name);  }//Add a static member named Creator, which implements the Parcelable.creator interface public static final parcelable.creator<person> Creator = New Parcelable.creator<person> () {@OverridE Public person Createfromparcel (Parcel source) {//reads data from Parcel, returns the person object return new person (
        Source.readint (), source.readstring ());
        } @Override Public person[] NewArray (int size) {return new person[size];
}
    }; }

Well, let's take a little bit of analysis:
1. First of all, our interface to inherit iinterface this interface, this interface what to do, look at the note: Base class for Binder interfaces. When defining a new interface, your must derive it from IInterface. In simple terms, this interface must be inherited as long as it is cross-process.

2. This interface in addition to our definition of the method, but also help us to generate two internal classes, one is stub, one is Proxy,stub is the meaning of the stub, how to understand how not to tangle, this kind of thing is good. First of all, the stub is for the service end, proxy is for the client. The fact is, let's look at the code in service and activity.

public class Testservice extends Service
{
    @Nullable
    @Override public
    ibinder onbind (Intent Intent)
    {
        return new ipersonmanager.stub ()
        {
            @Override public
            void Save (person person) throws Android.os.RemoteException
            {
                //do your work
            }
        }
}
/** * Created by Amuro on 2016/12/15.
        */public class Testactivity extends Activity {serviceconnection connection1 = new Serviceconnection () { @Override public void onserviceconnected (componentname name, IBinder service) {Ipersonmanage

            R manager = IPersonManager.Stub.asInterface (service);
            try {manager.save (New person ());
            } catch (RemoteException e) {e.printstacktrace ();

    }} @Override public void onservicedisconnected (componentname name) {}};
        @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);

        Setcontentview (r.layout.activity_test); Findviewbyid (R.ID.BT). Setonclicklistener (New View.onclicklistener () {@Override public voi D OnClick (View v) {Intent intent = new Intent (testactivity.this, testservice.class);
            Bindservice (Intent, Connection1, bind_auto_create);
    }
        });
        } @Override protected void OnDestroy () {Super.ondestroy ();
    Unbindservice (connection);
 }
}

3. Back we continue to look at the source code, first see stub,stub inherited binder, so has a cross-process capability.
1) First look at the constructor, inside called the Attachinterface method, the method passed two parameters, one is iinterface, that is, Ipersonmanager itself, one is descriptor, is a package name, but also play the role of the logo, The call is successful only if the parcel object is consistent in the Transact and Ontransact methods. This is why we require server side and client side AIDL storage directory structure Consistent reason, inconsistent words this automatically generated descriptor will be different;
2) The second method is the system for our own generation, Asinterface, this method is very important, I can see the client is through this method to get a "Ipersonmanager", in the end to get a what, see the Source:

public static Com.amuro.aidl.IPersonManager asinterface (Android.os.IBinder obj)
        {
            if ((obj = = null))
            {
                return null;
            }
            Android.os.IInterface iin = obj.querylocalinterface (descriptor);
            if ((iin! = null) && (iin instanceof Com.amuro.aidl.IPersonManager))
            {
                return ( Com.amuro.aidl.IPersonManager) iin);
            }
            return new Com.amuro.aidl.IPersonManager.Stub.Proxy (obj);
        

You can see the logic here is that asinterface based on the input binder to determine whether the current is a cross-process, if the step is to return a proxy object, if not a step directly back to the current interface implementation class (equivalent to call the local interface), this conclusion I believe many people know that But how does the source run to get this result? Here we can see the source of this method Querylocalinterface:
In Binder:

    Public IInterface Querylocalinterface (String descriptor) 
    {
        if (mdescriptor.equals (descriptor)) 
        {
            return mowner;
        }
        return null;
    }

In Binderproxy:

Public IInterface Querylocalinterface (String descriptor) 
{
        return null;
}

Here also can be determined that the client has to get a proxy, because Serviceconnection returned IBinder is a binderproxy,querylocalinterface after the inevitable return null, The branch must go to the code that returns the proxy.
3) Asbinder is the callback method of IInterface, it is OK to return to yourself here.
4) The following is the most important ontransact method, this method will be called when the client calls the Tansact method, the callback, first put, and so on after the proxy analysis.

4. The following see proxy, this kind of model of children's shoes can be seen at a glance is a proxy mode, which puts the IBinder is the client to get the Binderproxy, mainly see the Save method:

@Override public
void Save (Com.amuro.entity.Person person) 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 ((person! = null))
      {
           _data.writeint (1);
           Person.writetoparcel (_data, 0);
       }
       else
      {
           _data.writeint (0);
      }
     Mremote.transact (Stub.transaction_save, _data, _reply, 0);
     _reply.readexception ();
}
Finally
 {
      _reply.recycle ();
      _data.recycle ();
}
}

First, prepare two parcel objects, one for storing the parameters, and one for storing the results.
First write the descriptor in data, ensure that the object is correct, and then the argument is not NULL, write 1 and call the Writetoparcel method of the parameter to write data to the specific class, and finally call Mremote Transact method, Note that the first parameter of this method is essentially a method flag, because one interface often has multiple methods, so flag is required as a unique identifier, where the flag is used. Yes, that's the Ontransact method in the stub.

5. Go back and look at the Ontransact method in the stub:

@Override public boolean ontransact (int code, ANDROID.OS.PARCEL data, android.os.Parcel reply, int flags) throws a
                ndroid.os.RemoteException {switch (code) {Case Interface_transaction:
                    {reply.writestring (descriptor);
                return true;
                    } case Transaction_save: {data.enforceinterface (descriptor);
                    Com.amuro.entity.Person _arg0; if ((0! = Data.readint ())) {_arg0 = Com.amuro.entity.Person.CREATOR.createFro
                    Mparcel (data);
                    } else {_arg0 = null;
                    } this.save (_ARG0);
                    Reply.writenoexception ();
                return true; }} return Super.ontransact (code, data, reply, flags); }

When you see switch's case branch, the flag of that method just now is on the number. Yes, this is where the callback is, the Transaction_save branch will first determine if the argument is null based on the int value written before the proxy in data, if not, Will call us to write our own parcel class when the creator of the Createfromparcel method, its essence, is to generate a memory is not the same as the internal data of the person object, finally the object passed to our real save method, execute callback.
In fact, the transact of the proxy to the ontransact of the process, the Android system to help us do a lot of very complex things, interested friends can catch up to native layer to see Transactnative method, here will not repeat.

PART3: Know what the principle is for
The most intuitive role is to let us jump out of the tool, to implement a aidl interface, such as the SDK only allow the use of code conditions.
In fact, the most important thing is that in the process of running our app there will be a lot of cross-process calls, regardless of the four components or even window operation, familiar with the Aidl source code can let us better understand the call process of these sources, understand operating system operation mechanism and design ideas, This is a quality that a good programmer must have.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.