A detailed example of AIDL for Android programming

  • 2020-06-07 05:14:29
  • OfStack

In general, AIDL is not used very often in our application development process. Although sina weibo provides SSO login, the principle is to use AIDL. This paper describes the principle and implementation of AIDL in the form of a complete example.

AIDL(AndRoid Interface Description Language) is an excuse description language. Compiler can through aidl 1 code files are generated, through a predefined interface to achieve the goal of internal communication two process. If you need in a Activity, access a particular object, in the other one Service need to turning objects into AIDL identifiable parameters (probably more parameters), and then use the AIDL to deliver these parameters, in the message receiver, assemble themselves need to use these parameters of the object.

To put it bluntly, AIDL is to define an interface, the client (call) with the remote server by bindService resume a connection, the connection is established when returns 1 IBinder object, that object is the service side Binder BinderProxy, when the connection is established, the client by asInterface function will be the packaging cost to Proxy BinderProxy object, and will be assigned to the remote server BinderProxy object Proxy mRemote fields of a class, is through mRemote perform remote method invocation. For a deeper understanding of the Binder mechanism, please refer to the source code analysis of THE Java interface in the application framework layer of The Android system interprocess communication Binder mechanism. Let's look at an example of AIDL.

AIDL interface declaration

In src directory. Create a com example. advanceandroid. aidl package, and then under the package. Create a ILogin aidl file, pay attention to is to create the file instead of a class or interface type. Declare the interface in ES45en.aidl with the following example:


package com.example.advanceandroid.aidl;
interface ILogin {
    String login();
}

Note that neither the interface nor the method declaration USES public, and adding public to the method prompts an error. After writing if eclipse opens the automatic compilation in gen/com example. advanceandroid. aidl next generation 1 ILogin. java classes, content is roughly as follows:


package com.example.advanceandroid.aidl; 
public interface ILogin extends android.os.IInterface 
{ 
  /** Local-side IPC implementation stub class. */ 
  public static abstract class Stub extends android.os.Binder implements 
      com.example.advanceandroid.aidl.ILogin 
  { 
    private static final java.lang.String DESCRIPTOR = "com.example.advanceandroid.aidl.ILogin"; 
 
    /** Construct the stub at attach it to the interface. */ 
    public Stub() 
    { 
      this.attachInterface(this, DESCRIPTOR); 
    } 
 
    /** 
     * Cast an IBinder object into an com.example.advanceandroid.aidl.ILogin 
     * interface, generating a proxy if needed. 
     */ 
    public static com.example.advanceandroid.aidl.ILogin asInterface(android.os.IBinder obj) 
    { 
      if ((obj == null)) { 
        return null; 
      } 
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 
      if (((iin != null) && (iin instanceof com.example.advanceandroid.aidl.ILogin))) { 
        return ((com.example.advanceandroid.aidl.ILogin) iin); 
      } 
      return new com.example.advanceandroid.aidl.ILogin.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 flags) throws android.os.RemoteException 
    { 
      switch (code) 
      { 
        case INTERFACE_TRANSACTION: { 
          reply.writeString(DESCRIPTOR); 
          return true; 
        } 
        case TRANSACTION_login: {              // 1 , login request, execute is this.login(); 
          data.enforceInterface(DESCRIPTOR); 
          java.lang.String _result = this.login(); 
          reply.writeNoException(); 
          reply.writeString(_result); 
          return true; 
        } 
      } 
      return super.onTransact(code, data, reply, flags); 
    } 
 
    private static class Proxy implements com.example.advanceandroid.aidl.ILogin 
    { 
      private android.os.IBinder mRemote; 
 
      Proxy(android.os.IBinder remote) 
      { 
        mRemote = remote; 
      } 
 
      @Override 
      public android.os.IBinder asBinder() 
      { 
        return mRemote; 
      } 
 
      public java.lang.String getInterfaceDescriptor() 
      { 
        return DESCRIPTOR; 
      } 
 
      @Override 
      public java.lang.String login() throws android.os.RemoteException        // 2 , Proxy In the login Through the Binder Mechanisms to achieve IPC 
      { 
        android.os.Parcel _data = android.os.Parcel.obtain(); 
        android.os.Parcel _reply = android.os.Parcel.obtain(); 
        java.lang.String _result; 
        try { 
          _data.writeInterfaceToken(DESCRIPTOR); 
          mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0); 
          _reply.readException(); 
          _result = _reply.readString(); 
        } finally { 
          _reply.recycle(); 
          _data.recycle(); 
        } 
        return _result; 
      } 
    } 
 
    static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 
  } 
  public java.lang.String login() throws android.os.RemoteException; 
} 

As you can see, the ILogin interface is automatically generated in this class, with another login() function in the interface. But the most important thing is that it generates an Stub class that integrates with the Binder child and implements the ILogin interface. The most important function in Stub is asInterface(). In this function, the type of obj parameter will be judged. If the obj is similar to the local interface, it will be considered not IPC. Otherwise, it wraps obj by automatically generating another inner class, Proxy, assigning it to the mRemote attribute in Proxy. The Proxy class also implements the ILogin interface. In the login() function, Proxy passes requests and data to the server through the Binder mechanism, such as comment 2 in the code above. This is the client's job done.

Server AIDL interface

The service side also need to create the same under the same package aidl file, we will direct the client com. example. advanceandroid. aidl ILogin. Under the package aidl copy to the server, if use the custom types, so the custom types also need in the client and the server. After copying aidl, the corresponding ILogin. java file will be generated in the server program in gen with the same contents as the client 1. The important thing to look at here is the onTransact function, note 1 in the above code. You can see that the ES104en.login () function is executed at case TRANSACTION_login. This means that when the TRANSACTION_login request is received from the client, the this.login () function is executed. When we call login(), we actually submit an TRANSACTION_login request to the server through mRemote, so the two ends are connected through Binder mechanism, which we can simply understand as C/S mode.

The server is not finished yet. The most important step is to set up an Service, which is roughly as follows:


/** 
* AIDL Server interface, LoginStubImpl To achieve the ILogin interface . 
* 
* @author mrsimple 
*/ 
public class LoginService extends Service { 
 
  /** 
   * 
   */ 
  IBinder mBinder = new LoginStubImpl(); 
 
  /** 
   * @author mrsimple 
   */ 
  class LoginStubImpl extends Stub { 
    @Override 
    public String login() throws RemoteException { 
      return " This is from  " + this.getClass().getName() + "  String returned "; 
    } 
  } 
 
  /* 
   *  return Binder Instance, that is, the implementation ILogin Of the interface Stub Subclass of, in this case LoginStubImpl 
   * [url=home.php?mod=space&uid=133757]@see[/url] android.app.Service#onBind(android.content.Intent) 
   */ 
  @Override 
  public IBinder onBind(Intent intent) { 
    return mBinder; 
  } 
} 

The Service, which we'll call LoginService here, inherits from Service, then builds an inner class named LoginServiceImpl, which inherits from the automatically generated Stub, and implements the login() method. Declare 1 IBinder field mBinder in LoginService:


IBinder mBinder = new LoginStubImpl();

And return the mBinder object in the onBind function of LoginService. That is, when the client establishes the connection with the server, the onBind method will be called to return the mBinder object. The object IBinder obtained in the onServiceConnected function of the ServiceConnection class of the client is the mBinder object in the LoginService wrapped by BinderProxy. So the this. login() function called in onTransact on the server side is actually the login() function called in LoginStubImpl.

Register LoginService in AndroidManifest. xml of the server program, as follows:


<!-- aidl server service --> 
<service android:name="com.example.advanceandroid.aidl.LoginService" > 
    <intent-filter> 
      <action android:name="com.example.advanceandroid.aidl.LoginService" /> 
    </intent-filter> 
</service> 

The client establishes a connection

Add the following code to Activity:


ServiceConnection mLoginConnection = new ServiceConnection() { 
 
    @Override 
    public void onServiceDisconnected(ComponentName name) { 
      Log.d("", "### aidl disconnected."); 
    } 
 
    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
      Log.d("", "### aidl onServiceConnected.   service : " + service.getClass().getName()); 
 
      ILogin login = Stub.asInterface(service); 
      Log.d("", "### after asInterface : " + login.getClass().getName()); 
      try { 
        Log.d("", "### login : " + login.login()); 
        // Toast.makeText(MainActivity.this, "onServiceConnected : " + 
        // login.login(), 
        // Toast.LENGTH_SHORT).show(); 
      } catch (RemoteException e) { 
        e.printStackTrace(); 
      } 
    } 
  }; 
 
  @Override 
  protected void onResume() { 
    super.onResume(); 
 
    //  server-side action 
    Intent aidlIntent = new Intent("com.example.advanceandroid.aidl.LoginService"); 
    bindService(aidlIntent, mLoginConnection, Context.BIND_AUTO_CREATE); 
  } 
 
  @Override 
  protected void onStop() { 
    super.onStop(); 
    // unbind 
    unbindService(mLoginConnection); 
  } 

run

First run the server program, and then start the client program. You can see the client output as Log:


09-02 10:40:54.662: D/(9589): ### aidl onServiceConnected.   service : android.os.BinderProxy 
09-02 10:40:54.662: D/(9589): ### after asInterface : com.example.advanceandroid.aidl.ILogin$Stub$Proxy 
09-02 10:40:54.662: D/(9589): ### login :  This is from  com.example.advanceandroid.aidl.LoginService$LoginStubImpl  String returned  

The service object in onServiceConnected(ComponentName name, IBinder service) is of BinderProxy type, which is converted by asInterface and wrapped into Proxy type, but when invoked, the login() function in server LoginStubImpl is executed. Therefore,LoginStubImpl instance mBinder is wrapped by the server as BinderProxy type, then wrapped by the client Proxy, and data transmission is carried out through Binder mechanism to realize IPC.

I hope this article has helped you to take the next step in mastering Android programming.


Related articles: