AIDL是什么:
- Android 接口定义语言(Android interface define language)。用于生成可以在Android设备上两个进程通讯(IPC interprocess communication)的代码。其方法不能重载,因为相同的方法底层映射都是相同的ID;
支持的类型:
Java中的八种基本数据类型。 CharSequence / String类型 Parcelable类型 List /Map 类型
AIDL的优点:
- 跨进程,多并发。
具体使用
-
在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端
-
以下是两个进程角色的具体使用步骤:
服务器端(Service)
步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()等)
步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务客户端(Client)
步骤1:拷贝服务端的AIDL文件到目录下
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service
一:服务端
步骤1. 新建一个AIDL文件
步骤2. 在新建AIDL文件里定义Service需要与Activity进行通信的内容(方法),并进行编译(Make Project)
// IMyAidlInterface.aidl
package com.example.androidpratic;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void startApp();
}
步骤3:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()等)
package service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.example.androidpratic.IMyAidlInterface;
public class AidlService extends Service {
private static final String TAG = "AidlService";
public AidlService() {
}
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public void startApp() throws RemoteException {
Log.d(TAG, "startApp.......");
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind..............");
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate............");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand.............");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy.............");
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind..............");
return super.onUnbind(intent);
}
}
步骤4:在AndroidMainfest.xml中注册服务 & 声明为远程服务
<service
android:name="service.AidlService"
android:process=":remote"
android:enabled="true"
android:exported="true">
<!-- 此处Intent的action必须写成“服务器端包名.aidl文件名” -->
<intent-filter>
<action android:name="com.example.androidpratic.IMyAidlInterface"/>
</intent-filter>
</service>
二:客户端
步骤1:将服务端的AIDL文件所在的包复制到客户端目录下(Project/app/src/main),并进行编译
// IMyAidlInterface.aidl
package com.example.androidpratic;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void startApp();
}
步骤2:在主布局文件定义“启动APP”的按钮
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_app"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
步骤3:在MainActivity.java里
- 使用Stub.asInterface接口获取服务器的Binder;
- 通过Intent指定服务端的服务名称和所在包,进行Service绑定;
- 根据需要调用服务提供的接口方法。
package com.example.aidlclient;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
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 android.widget.Button;
import com.example.androidpratic.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IMyAidlInterface mMyAidlInterface;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "onServiceConnected: " + (iBinder == null));
if (iBinder != null) {
mMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
try {
mMyAidlInterface.startApp();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startBtn = findViewById(R.id.start);
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
//参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
Intent intent = new Intent("com.example.androidpratic.IMyAidlInterface");
intent.setPackage("com.example.androidpratic");
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
});
}
}
三:运行结果
如下图所示,在client端点击了三次,server端都打印了对应log,说明通信成功。
四:补充
今天重新打开代码,运行了一下发现as里面打不出log,郁闷了一会(之前怎么有),后来发现之前都是在cygwin里面看的,所以又去试了一下,果然行。
这次准备再服务端的startApp中添加一个操作,当客户端点击按钮时,可以直接启动服务端activity。修改代码如下:
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public void startApp() throws RemoteException {
Log.d(TAG, "startApp.......");
mHandler.post(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
}
};
得要加一个flag,另开一个栈来存放新app的activity。不然会报错:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
后记:IMyAidlInterface.Stub()只是获取binder对象,进程通信本质就是通过传输binder对象,服务端或者客户端只要获取到了对方的binder对象就可以调用对方的方法,不是只有服务端才实现.Stub()方法。