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>