Detailed explanation of the use of Android advanced Aidl

Aidl (Android interface definition language) can be used to define the programming interface for inter process communication (IPC) between client and server. In Android, memory (user space) cannot be shared between processes, and communication between different processes is generally handled by Aidl.

The main process is to define the Aidl interface in the. Aidl file and add it to the SRC directory of the application project. After creation, rebuild, and the Android SDK tool will automatically generate the ibinder interface based on the. Aidl file. The specific business object implements the interface, and the specific business object is also the ibinder object, When binding a service, a specific communication object (local or proxy) will be returned according to the actual situation. Finally, bind the client to the service, and then call the methods in ibinder for interprocess communication (IPC). The following will learn about the use of Aidl from the following aspects:

In Aidl, the interface can be declared through one or more methods with parameters and return values. The parameters and return values can be of any type. The data types supported in Aidl are as follows:

If the parameter or return value type in the business method is list or map:

// IPersonAidlInterface.aidl
package com.manu.aidldemo;
// Declare any non-default types here with import statements
interface IPersonAidlInterface {
    //具体的业务
    void setName(String name);
    void setAge(int age);
    String getInfo();

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt,long aLong,boolean aBoolean,float aFloat,double aDouble,String aString);
}

Then rebuild the project, and the Android SDK tool will generate the corresponding. Java interface file with the same name as the. Aidl file in the corresponding directory. The specific directory is as follows:

In the previous step, only the basic business operations are defined using Aidl. After rebuild, a. Java file with the same file name as. Aidl will be generated. In the generated interface file, there is a subclass named stub, which is also the abstract implementation of its parent interface. It is mainly used to generate all methods in the. Aidl file. The stub class declaration is as follows:

// Stub
public static abstract class Stub extends android.os.Binder implements com.manu.aidldemo.IPersonAidlInterface

Obviously, the stub implements the local interface and inherits the binder object. With the support of the binder object at the bottom of the system, the stub object has the ability to remotely transmit data. When generating the stub object, the asinterface method will be called, as follows:

// asInterface
public static com.manu.aidldemo.IPersonAidlInterface asInterface(android.os.IBinder obj){

    if ((obj==null)) {
        return null;
    }

    // 检索 Binder 对象是否是本地接口的实现
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

    if (((iin!=null)&&(iin instanceof com.manu.aidldemo.IPersonAidlInterface))) {
        return ((com.manu.aidldemo.IPersonAidlInterface)iin);
    }

    return new com.manu.aidldemo.IPersonAidlInterface.Stub.Proxy(obj);
}

The asinterface method will be called when creating a stub. Its main function is to retrieve whether the binder object is the implementation of the local interface. Judge whether to use a proxy object according to the return value of the querylocalinterface () method. This retrieval process should be supported by the bottom layer of the system. If the return is null, create a proxy object of the stub, Instead, use local objects to transmit data. Let's see why binder has the ability of remote communication, because stub inherits binder class. Binder class is as follows:

// Binder
public class Binder implements IBinder {
    //...
}

The following is the description of ibinder interface on the official website:

Here we know that binder implements the ibinder interface, that is, binder has the ability of remote communication. When communicating between different processes (remote), it obviously uses the stub proxy object. The specific business processing logic in this proxy class is as follows:


//具体的业务
@Override 
public void setName(java.lang.String name) throws android.os.remoteexception{
    // 将数据序列化
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeString(name);
        // 这个方法会最终调用 onTransact 方法
        mRemote.transact(Stub.TRANSACTION_setName,_data,_reply,0);
        _reply.readException();
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
}

Here, the data is serialized, and then the ontransact () method is finally called with the system cross process support. The following is the ontransact () method, as follows:

@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_setName:
        {
            //...
            // 最终调用了 Stub 里面的业务方法
            this.setName(_arg0);
            //...
        }
    }
}

Obviously, when the system calls back to the developer, the code passed back by this method is a constant. In cross process, each specific service (method) will correspond to a number, and then the corresponding service (business) will be executed according to this number. Here is the specific business to be executed finally. Where should this business be reflected, It can be seen from the above that stub is an abstract class, so the specific business it provides must be completed by a specific implementation class. The following implements this specific business class, as follows:

/**
 * Created by jzman
 * Powered by 2018/3/8 0008.
 */
public class IPersonImpl extends IPersonAidlInterface.Stub {
    private String name;
    private int age;

    @Override
    public void setName(String name) throws remoteexception {
        this.name = name;
    }

    @Override
    public void setAge(int age) throws remoteexception {
        this.age = age;
    }

    @Override
    public String getInfo() throws remoteexception {
        return "My name is "+name+",age is "+age+"!";
    }

    @Override
    public void basicTypes(int anInt,String aString) throws remoteexception {

    }
}

This class is a specific business class provided externally, and its instance is also a binder object.

Create a service to provide specific services, as follows:

// Service
public class PersonService extends Service {
    public PersonService() {
    }

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

When the bindservice () method is called externally to bind the service, the onbind () method will be called to return the ibinder object, which is also a specific business object. For example, the onbind () method here also returns a specific business object. The two are unified. In addition, the created service should be declared in the androidmanifest.xml file, as follows:

<service
    android:name=".PersonService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>

Use the process keyword to start an independent process for the service, and remote can be arbitrary, indicating the process name, ":" will add a new name to the main process (the process name is the package name) as the name of the new process, for example, com.manu.study will become com.manu.study: remote.

Through the above steps, the service is built and run in an independent process. The following is the specific call of the client. The specific implementation reference is as follows:

// Client
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private IPersonAidlInterface iPersonAidlInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void bindServiceClick(View view) {
        Log.i(TAG,"绑定服务...");
        Intent intent = new Intent(this,PersonService.class);
        // 绑定服务时自动创建服务
        bindService(intent,conn,Context.BIND_AUTO_CREATE);
    }

    public void unbindServiceClick(View view) {
        Log.i(TAG,"解绑服务...");
        unbindService(conn);
    }

    public void callRemoteClick(View view) {
        Log.i(TAG,"远程调用具体服务...");
        try {
            iPersonAidlInterface.setName("Tom");
            iPersonAidlInterface.setAge(10);
            String info = iPersonAidlInterface.getInfo();
            System.out.println("这是远程调用的服务信息:"+info);
        } catch (remoteexception e) {
            e.printStackTrace();
        }
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name,IBinder service) {
            // 根据实际情况返回 IBinder 的本地对象或其代理对象
            iPersonAidlInterface = IPersonAidlInterface.Stub.asInterface(service);
            System.out.println("具体的业务对象:"+iPersonAidlInterface);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // Service 意外中断时调用
        }
    };
}

The above code is the process of the client calling a specific service.

Through the previous steps, the server and client have been completed. The following is to verify whether specific services can be called. Here are two cases:

When creating a service, do not use process in the androidmanifest.xml file to start an independent process. At this time, the service process belongs to a unified process with the client by default. The results are as follows:

When creating a service, you can use process in the androidmanifest.xml file to start an independent process. This is mentioned above. At this time, the service process and the client process are located in different processes. The results are as follows:

Obviously, if the service and the client are in different processes, that is, the inter process communication is usually completed by the proxy object of the ibinder object. On the contrary, the local object, that is, the local ibinder object, is completed by the object generated by the business class implementing Aidl. The next article describes how to use custom types in Aidl.

If you feel that you have written wrong, you can pay attention to the official account number: jzman-blog, learn and exchange together!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>