In depth interpretation of Android's internal process communication interface AIDL

  • 2021-07-09 09:21:49
  • OfStack

Meaning:

Because each application process has its own independent process space, on android platform, one process usually cannot access the memory space of another process, and we often need to boast that the process passes objects, so we need to decompose the objects into basic units that can be understood by the operation objects, and pass through the process boundaries orderly.

Definition:

AIDL (Android Interface Definition Language) is an IDL language used to generate code for interprocess communication (interprocess communication, IPC) between two processes on an Android device. If you are in one process (for example, Activity) calling an operation on an object in another process (for example, Service), you can use AIDL to generate serializable parameters.

Description and implementation process:

There is no difference between AIDL interface and ordinary java interface, except that the extension is. aidl, which is saved in src directory. If other applications need IPC, they also need to create the same AIDL file in src directory. After creation, through ADT tool, the corresponding. java file will be generated in gen directory of the project.

1. To realize the communication between two processes, you need to implement the following steps

(1) Create a file with aidl extension under android project directory of Eclipse. The syntax is similar to that of java definition interface, but it is necessary to manually define the package name corresponding to import. (For example, if the list set is needed, import java. util. List;)

(2) If the aidl file conforms to the specification, the ADT tool helps the compiler generate the corresponding. java file in the gen directory.

(3) It is necessary to inherit and implement a service class, which is the basis of cross-process invocation.

(4) Implement AIDL interface on service, and implement AIDL interface of callback on client if there is callback.

(5) Register service at AndroidManifest. xml.

Note:

To implement AIDL, we need to pay attention to the following five points

(1) AIDL only supports interface methods and cannot expose static variables.

(2) If the AIDL interface method has parameters, it is necessary to pay attention to the usage rules of in, out and inout. For basic data types, the default is in, and no declaration is needed. For non-basic mutable objects, method types need to be added before variable names

in represents an input parameter, and the caller passes the value to the consumer for use.

out represents the output parameter, and the caller passes the container to the consumer to populate, and then uses the processing himself.

inout tenders input and output parameters, transmit corresponding values and receive returns.

Give an example of using out:
The server sends parameters to the client, the client fills in, after the server calls, you can read the contents filled in by the client, and the specific example will be given later.

(3) The interface name defined by AIDL must match the file name 1.

(4) oneway means that users can call back directly without waiting for a response when requesting corresponding functions, which has a non-blocking effect. This keyword can be used to declare interfaces or methods. If oneway keyword is used in interface declaration, all methods declared by this interface adopt oneway mode.

(5) AIDL passes non-basic variable length variables (non-final objects) and requires the implementation of the parcelable interface.
parcel1 is generally used in Binder communication, through read and write methods for data transfer (communication) between client and server.
For example, Binder communication between frameworks layer server and hardware client


reply->writeInt32(getCardReaderSize());
int mid = data.readInt32();

Memory (RAM) is used to store parcel data, not permanent media (Nand, etc.).

parcelable defines the interface for writing data to parcel and reading data from parcel. If an instance of a class needs to be encapsulated in a message, it must implement this interface. If this interface is implemented, the instance of the class can be "packaged".
The implementation of Parcelabel requires adding a static member variable CREATOR to the class, which inherits the Parcelable. Creator interface.


package com.zlc.provider;
 
import android.os.Parcel;
import android.os.Parcelable;
 
public class Students implements Parcelable{
  private int stu_id;
  private String stu_name;
  public Students(Parcel source){
    stu_id = source.readInt();
    stu_name = source.readString();
  }
  public int getStu_id() {
    return stu_id;
  }
  public void setStu_id(int stu_id) {
    this.stu_id = stu_id;
  }
  public String getStu_name() {
    return stu_name;
  }
  public void setStu_name(String stu_name) {
    this.stu_name = stu_name;
  }
  @Override
  public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
  }
  @Override
  public void writeToParcel(Parcel dest, int flags) {
    // TODO Auto-generated method stub
    dest.writeInt(stu_id);
    dest.writeString(stu_name);
  }
  //Interface that must be implemented and provided as a public CREATOR field that generates instances of your Parcelable class from a Parcel. 
  public final static Parcelable.Creator<Students> CREATOR = new Parcelable.Creator<Students>() {
 
    @Override
    public Students createFromParcel(Parcel source) {
      // TODO Auto-generated method stub
      return new Students(source);
    }
 
    @Override
    public Students[] newArray(int size) {
      // TODO Auto-generated method stub
      return new Students[size];
    }
  };
}


Example:

Here is an example, Mainly realize that the client calls the server and then calls back, The specific realization function changes the text and picture display of the client, The temporary effect of this example is that the picture changes directly use the picture prepared by the client. The next few blogs will be based on this function. Arriving at the server can send text, picture, file handle (I/O stream), and directly by the server directly call the client method through the method name, the client only needs to register the corresponding view and provide the corresponding method for the server to use, the improvement of the following two parts mainly uses reflection and rewriting MemoryFile (achieving parcelable serialization effect) to achieve.

(1) First of all, we need to create aidl files according to our above steps, and create aidl files for calls and callbacks respectively. For more details, this site adds parcelable objects as well, just as a test.
IMyAidlService. aidl is mainly invoked by the server implementation client


package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import com.zlc.aidl.AIDLCallback;
interface IMyAidlService{
  void registerClient(AIDLCallback cb);// Register callback 
  void saveDemoInfo(in DemoParcelable demo);// Actual calling method 
}

AIDLCallback. aidl is mainly implemented by the client, and the server calls


package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import java.util.List;
interface AIDLCallback {
  int returnResult(out List<DemoParcelable> list,int a);// Callback to client 
  void testMethod(out Bundle params);// Used to test parameters in/out Use of 
}

DemoParcelable. aidl declarations pass objects:


package com.zlc.aidl;
parcelable DemoParcelable;

Add 1 point: The difference between out and in parameters is obvious. We can see the difference by directly looking at the corresponding java file generated by adt in gen directory: when it is out parameter, it reads the value from parcel object after execution, while when it is in parameter, it is written to parcel object.
Let's look at the files generated when testMethod is modified by out and in, respectively
At that time, out read data from parcel object


mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
params.readFromParcel(_reply);
}

At that time, in took data from parcel object


if ((params!=null)) {
_data.writeInt(1);
params.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();

(2) Realize a service class to achieve communication between processes MyAidlService. java, post part of the code, detailed code will be uploaded later.


@Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    Log.d(TAG, "MyAidlService onBind");
    return mBinder;
  }
 
  private final IMyAidlService.Stub mBinder = new IMyAidlService.Stub() {
    private AIDLCallback cb;
 
    @Override
    public void saveDemoInfo(DemoParcelable demo) throws RemoteException {
      if (demo != null) {
        if ("meinv1".equals(demo.getDemo_name())) {
          demo.setDemo_name("meinv2");
        }
        list.add(demo);
        Log.d(TAG, "saveDemoInfo list.size = " + list.size() + " list = " + list);
        cb.returnResult(list, 5);
        Bundle params = new Bundle();
        cb.testMethod(params);
        int width = params.getInt("width", 0);
        int height = params.getInt("height", 0);
        Log.d(TAG, "width = " + width + " height = "+height);
      }
    }
 
    @Override
    public void registerClient(AIDLCallback cb) throws RemoteException {
      cb.asBinder().linkToDeath(new DeathRecipient() {
        @Override
        public void binderDied() {
          try {
            Log.i(TAG, "[ServiceAIDLImpl]binderDied.");
          } catch (Throwable e) {
          }
        }
      }, 0);
    }
  };


(3) Implement client connection and implement callback method


private ServiceConnection mRemoteConnection = new ServiceConnection() {
 
    @Override
    public void onServiceDisconnected(ComponentName name) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceDisconnected");
    }
 
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceConnected");
      mRemoteService = (IMyAidlService) IMyAidlService.Stub
          .asInterface(service);
      if(mRemoteService != null)
        Log.d(TAG, "onServiceConnected success");
    }
  };
 ... 
btn.setOnClickListener(new OnClickListener() {
 
      @Override
      public void onClick(View v) {
        // TODO Auto-generated method stub
        String actionName = "com.zlc.aidl.server.MyAidlService";
        Intent intent = new Intent(actionName);
        boolean ret = bindService(intent, mRemoteConnection,
            Context.BIND_AUTO_CREATE);
        Log.d(TAG, " ret ?=" + ret);
        if (ret) {
          new Thread(new Runnable() {
 
            @Override
            public void run() {
              // TODO Auto-generated method stub
              try {
                DemoParcelable demo = new DemoParcelable();
                List<String> list = new ArrayList<String>();
                list.add("like dance");
                demo.setDemo_id((Integer) img.getTag());
                demo.setDemo_name("meinv1");
                demo.setDemo_list(list);
                mRemoteService.registerClient(callback);
                mRemoteService.saveDemoInfo(demo);
              } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
            }
          }).start();
 
        }
      }
    });
  }
 ... 
private final AIDLCallback callback = new AIDLCallback.Stub() {
     
 
    @Override
    public int returnResult(List<DemoParcelable> list, int a)
        throws RemoteException {
      if (list != null)
        Log.d(TAG, "list.size = " + list.size()+"  a="+a);
      for (DemoParcelable demoParcelable : list) {
        doFresh(demoParcelable);
      }
      return 0;
    }
 
    @Override
    public void testMethod(Bundle outParams) throws RemoteException {
      // TODO Auto-generated method stub
      if (outParams != null) {
        outParams.putInt("width", 11);
        outParams.putInt("height", 12);
      }
 
    }
  };


(4) Register the service service in androidManifest. xml.
Note 1: android: process = ": remote", which means that when the service is needed in the application, a new process will be created automatically. If it is android: process= "remote" and there is no ":" semicolon, a global process is created, which is shared by different applications.
It can be seen by looking directly at the pid process number through ps.
Let the application components run in a separate process. If there is a colon:, create a process that is specific to the current process. If there is no colon, you need to use standard naming conventions to name the process name, such as com. xxx. xxx. xxx, and this process is a globally shared process, that is, components of different applications can run in this process.
This breaks the 24M (or 16M) memory limit of your application.
In a word, the process id pid with the attribute: remote is different, and the parent process ID PPID is one. Using remote without colons creates two completely independent processes.


Related articles: