Android: Detailed explanation of AIDL actual combat

AIDL:Android Interface Definition Language

AIDL is an Android interface language designed for inter-process communication

There are many ways to communicate between Android processes, and the Binder mechanism is the most common one

The essence of AIDL is based on the use of Binder to achieve inter-process communication

This blog post starts from actual combat and uses a demo that is as streamlined as possible to implement cross-process communication between two apps (also two processes: server-side mainapp and client-side otherapp) through AIDL

Without further ado, let's go!

1. Create two apps

Follow the steps below to create two Apps first:

(1). Create a new development folder (named aidl-test in this Demo)

(2). Use AndroidStudio to create an empty App of the first Empty Activity under the aidl-test folder: mainapp

For the sake of convenience

After the creation is complete, change the name of MainActivity.java created by Studio to MainAppActivty.java

(3). Create an empty App of the second Empty Activity: otherapp

 

Two empty apps are created:

2. Create a Service in mainapp

In the previous section, two new empty apps were created: mainapp and otherapp

Now implement a service in mainapp

Use the convenient and fast studio to create MainAppService.java

 

You can see that the newly created MainAppService will automatically implement an onBind (Intent intent) method. I will implement the code in this method later. It needs to return an object that inherits android.os.Binder when other processes connect to the Service

First add some necessary life cycle function codes in MainAppActivity and MainAppService
and then add in MainAppService  :
onBind (Intent intent): execute when bound by the client
onUnbind (Intent intent): execute when unbound by the client

com/android/mainapp/MainAppActivity.java

package com.android.mainapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class MainAppActivity extends AppCompatActivity {
    private String TAG = "AIDL-MainAppActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v(TAG, "onCreate()");
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

com/android/mainapp/MainAppService.java 

package com.android.mainapp;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MainAppService extends Service {
    private String TAG = "AIDL-MainAppService";

    public MainAppService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.v(TAG, "onStart()");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.v(TAG, "onUnbind()");
        return true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

3. Implement AIDL in mainapp

Still use the convenient and fast AndroidStudio to create an AIDL file named IAppAidlInterface.aidl  in the main directory of the mainapp

 

When AndroidStudio creates IAppAidlInterface.aidl, it will automatically implement a default void basicTypes(...) function that can be used for communication between processes , and its parameters are several basic data types of java.

In addition, AIDL also supports a variety of other data types: byte, short, char, charSequence, List, Map, etc.

In addition to the basicTypes(...) function automatically created by AndroidStudio , I added a setData(..) function in the AIDL file for subsequent cross-process data transmission, although using the basicTypes(...) automatically generated by AndroidStudio It is also possible, but creating a function yourself will be more representative

As stated at the beginning of this blog post, the essence of AIDL is the use of Binder to achieve inter-process communication

So where is Binder now?

After the IAppAidlInterface.aidl file is created, build the project, AndroidStudio will create an aidl_source_output_dir folder in the build directory, and create a folder directory and java file with the same package name and file name as IAppAidlInterface.aidl under this folder

In the IAppAidlInterface.java file , an abstract proxy class stub is created by inheriting android.os.Binder . This stub abstract proxy class mainly does the following things:

  • Intra-process communication is realized through the stub instance itself
  • By implementing an internal proxy class Porxy for cross-process communication
  • Rewrite the onTransact() function in Binder to implement the functions declared in the AIDL interface file for data transmission

The transmitted data must be serialized android.os.Parcel type data 

 

Of course, if the project needs to customize the implementation process of AIDL's Binder to facilitate the customization of the inter-process communication mechanism in the project, then it is completely unnecessary to use the IAppAidlInterface.java automatically generated by AndroidStudio, and only need to implement IAppAidlInterface according to your own needs The implementation process of Binder inter-process communication in .java is fine.

Because in the final analysis, the basis for AIDL to achieve inter-process communication is the Binder mechanism, as long as you use Binder to achieve the purpose of AIDL inter-process communication.

IAppAidlInterface.java will be discussed here first. This blog post is mainly about the use of AIDL, and its implementation and encapsulation of the Binder mechanism will not be discussed in depth here.

A blog post will be dedicated to explain AIDL's internal implementation of the Binder mechanism and how users can customize the package

4. AIDL is also implemented in otherapp

In the previous section, an AIDL file was created in the mainapp as the server, and the otherapp of the client also needs to implement the same AIDL, otherwise the client will not be able to reference the corresponding functions and stubs, etc.

It's very simple, just copy the entire package name directory of the AIDL file in mainapp to otherapp, and then build the project

Next, you need to add some code to realize the connection between the otherapp on the client side and the mainapp on the server side

5. Add code to mainapp

As shown in the previous article, MainAppService will startService() and stopService() along with MainAppActivity's onCreate() and onDestroy() life cycle

The code to be added in mainapp is in MainAppService.java, and the following things need to be done in MainAppService:

  • Use the anonymous inner class to implement the IAppAidlInterface.Stub abstract class, which is used to implement the interface function in IAppAidlInterface.aidl and return the anonymous inner class instance when onBinder()
  • Start a thread in onBinder() , and receive the data sent by the client every 1 second

      Here is a knowledge point outside the topic. In addition to the onBinder() function in Service , there is also an onRebind() function

      If the same client bindService() after each unBindService() and sends the same Intent , then onBind() will only be executed when the server is connected to the client for the first time, and will not be executed when the client reconnects. Execute again.

     And onRebind() will not be executed when the server is connected for the first time, but it will be executed every time it is reconnected, regardless of whether the Intent is the same.

    If you want onBind() to be executed when the same client connects, the client can change the type or other member variables of the sent Intent every time bindService()

  • Define a string variable to receive the string passed by the client, and define a boolean variable to control the thread
  • Add Service tag for MainAppService in AndroidMainfest.xml

6. Add code to otherapp

Otherapp mainly needs to do the following things:

  • Create a new Intent , which is used to connect to the server mainapp. The Component of
    the Intent is set to the package name and class name of the Service
  • Create two new buttons to control bindService() binding and unbindService() unbinding
  • Rewrite the two basic functions of Service connection and disconnection onServiceConnected() and onServiceDisconnected()
  • Add the permission to query the package name in AndroidMainfest.xml, so that otherapp can query the mainapp, or directly specify the package name of the mainapp

7. Operation and verification 

At this point, the most basic demo code that uses AIDL to realize communication between two apps (mainapp on the server side and otherapp on the client side) is completed. Let's verify it below.

Compile and install apk:

After the project builds Apk, two apks will be generated, both of which are installed

 

Run verification:

Note: All log tags of the code in this demo are prefixed with AIDL, which is convenient for log printing and verification

Start the mainapp first, MainAppService will not be started but will be registered to the system, because the Service tag is added to MainAppService in AndroidMainfest.xml of mainapp.

After exiting mainapp, open otherapp:

Now execute "Bind Service" and "Unbind Service" several times, and you will see the following log prints:

Well, through this streamlined Demo, the cross-process communication between two Apps through AIDL has been initially realized. 

8. Source code

Next, the source code of the implementation will be shown one by one.

mainapp source code:

D:\Codes\aidl-test\app\src\main\aidl\com\android\mainapp\IAppAidlInterface.aidl

// IAppAidlInterface.aidl
package com.android.mainapp;

// Declare any non-default types here with import statements

interface IAppAidlInterface {
    /**
     * 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);

    void setStringData(String strData);
}

D:\Codes\aidl-test\app\src\main\java\com\android\mainapp\MainAppActivity.java

package com.android.mainapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class MainAppActivity extends AppCompatActivity {
    private String TAG = "AIDL-MainAppActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v(TAG, "onCreate()");
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

D:\Codes\aidl-test\app\src\main\java\com\android\mainapp\MainAppService.java

package com.android.mainapp;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MainAppService extends Service {
    private String TAG = "AIDL-MainAppService";

    private String mStrData;
    private boolean mSetServiceRunning = true;

    public MainAppService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onStart()");
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.v(TAG, "onStart()");
    }

    IAppAidlInterface.Stub mStub = new IAppAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void setStringData(String strData) {
            mStrData = strData;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        Log.v(TAG, "onBind()");
        mSetServiceRunning = true;

        new Thread() {
            @Override
            public void run() {
                super.run();
                while (mSetServiceRunning) {
                    try {
                        Thread.sleep(1000);
                        Log.v(TAG, "mStrData:"+mStrData);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        return mStub;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.v(TAG, "onUnbind()");
        mSetServiceRunning = false;
        return true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy()");
    }
}

D:\Codes\aidl-test\app\src\main\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mainapp"
        tools:targetApi="31">
        <service
            android:name=".MainAppService"
            android:enabled="true"
            android:exported="true">
        </service>

        <activity
            android:name=".MainAppActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

otherapp source code: 

D:\Codes\aidl-test\otherapp\src\main\aidl\com\android\mainapp\IAppAidlInterface.aidl

// IAppAidlInterface.aidl
package com.android.mainapp;

// Declare any non-default types here with import statements

interface IAppAidlInterface {
    /**
     * 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);

    void setStringData(String strData);
}

D:\Codes\aidl-test\otherapp\src\main\java\com\android\otherapp\OtherAppMainActivity.java

package com.android.otherapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;

import com.android.mainapp.IAppAidlInterface;

public class OtherAppMainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
    private String TAG = "AIDL-OtherAppActivity";

    private int mICount = 0;
    private Intent mServiceIntent;
    private IAppAidlInterface mBinder;

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

        mServiceIntent = new Intent();
        mServiceIntent.setComponent(new ComponentName("com.android.mainapp", "com.android.mainapp.MainAppService"));

        findViewById(R.id.btnBindMainAppService).setOnClickListener(this);
        findViewById(R.id.btnUnBindMainAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btnBindMainAppService: {
                Log.v(TAG, "onClick():btnBindMainAppService");
                bindService(mServiceIntent, this, Context.BIND_AUTO_CREATE);
            }
            break;

            case R.id.btnUnBindMainAppService: {
                Log.v(TAG, "onClick():btnUnBindMainAppService");
                unbindService(this);
                mBinder = null;
            }
            break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        if (mBinder == null) {
            mBinder = IAppAidlInterface.Stub.asInterface(iBinder);
            mICount++;
            Log.v(TAG, "onServiceConnected() 第 " + mICount + " 次");
            try {
                String strData = "第" + mICount + "次连接Service成功!";
                mBinder.setStringData(strData);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.v(TAG, "onServiceDisconnected");
    }
}

D:\Codes\aidl-test\otherapp\src\main\res\layout\activity_other_app_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".OtherAppActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="OtherApp"/>

    <Button
        android:id="@+id/btnBindMainAppService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="Bind  Service" />

    <Button
        android:id="@+id/btnUnBindMainAppService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="UnBind Service" />

</LinearLayout>

D:\Codes\aidl-test\otherapp\src\main\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!--<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>-->
    <queries>
        <package android:name="com.android.mainapp"/>
    </queries>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mainapp">
        <activity
            android:name=".OtherAppMainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

Guess you like

Origin blog.csdn.net/geyichongchujianghu/article/details/130045373