通过aidl,我们可以实现client(后称客户端)和server(服务端)的双向通信,有时候server和client处于不同的进程当中,如果client意外退出,server再向client发送消息的话,就有可能导致server端也退出。
安卓提供了 RemoteCallbackList 来为我们隐式解决了这种问题。
下面来看一个示范。
首先我们定义用于客户端向服务端通信的一个 aidl
// ICallBackTestInterface.aidl
package com.callback;
// Declare any non-default types here with import statements
import com.callback.ICallbackTestCallback;
interface ICallBackTestInterface {
// 向服务端注册客户端回调
void register(ICallbackTestCallback callback);
// 向服务端注销客户端回调
void unregister(ICallbackTestCallback callback);
// 向服务端发送消息
void callServer(String msg);
}
其内的 ICallbackTestCallback 也是一个 aidl,其内容如下
// ICallbackTestCallback.aidl
package com.callback;
// Declare any non-default types here with import statements
interface ICallbackTestCallback {
/**
* 服务端调用客户端的回调
**/
void onReceived(String msg);
}
编译之后我们可以看到,实际上 aidl 编译之后就会变成类似这样的内容
public interface ICallbackTestCallback extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.callback.ICallbackTestCallback{
...
这个也是aidl的核心,实际上这个Stub是一个 Binder 的实现,并且还实现了我们定义在 aidl 里面的接口
服务端
服务端比较简单,直接贴代码
//callbackserver.java
package com.callback.server;
import androidx.annotation.Nullable;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.callback.ICallBackTestInterface;
import com.callback.ICallbackTestCallback;
public class callbackserver extends Service {
private final String TAG = "testcallback";
// 这里的 clients 就是 RemoteCallbackList 的用法了,我们用它来存储注册了的客户端,然后在某些时候向注册了的客户端发送消息。
private final RemoteCallbackList<ICallbackTestCallback> clients = new RemoteCallbackList<>();
CallBackServer server = new CallBackServer();
private boolean serverRunning = false;
@Override
public void onCreate() {
Log.d(TAG,"callback test server create");
// service 创建的时候,开一个线程去向注册了的客户端发送消息
serverRunning = true;
new Thread(serverRunnable).start();
super.onCreate();
}
@Override
public void onDestroy() {
Log.d(TAG,"callback test server destroy");
serverRunning = false;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// 将一个实现了 ICallBackTestInterface.Stub 的Binder对象返回
// 客户端调用 bind service 时会拿到一个返回的 Binder 对象,就是这里的 server,也就是一个
// ICallBackTestInterface.Stub 实例
return server;
}
/**
* 返回给客户端的 Binder 对象的实现
*/
private class CallBackServer extends ICallBackTestInterface.Stub {
@Override
public void register(ICallbackTestCallback callback) throws RemoteException {
Log.d(TAG,"register callback from pid=" + Binder.getCallingPid());
clients.register(callback);
}
@Override
public void unregister(ICallbackTestCallback callback) throws RemoteException {
Log.d(TAG,"unregister callback from pid=" + Binder.getCallingPid());
clients.unregister(callback);
}
@Override
public void callServer(String msg) throws RemoteException {
Log.d(TAG,"received msg: " + msg + " . from pid=" + Binder.getCallingPid());
}
}
// 向客户端发送消息的具体实现
// 简单的做一个自增运算,然后发送回客户端
private Runnable serverRunnable = () ->{
int count = 0;
while(serverRunning){
try {
Thread.sleep(500);
noteClients(Integer.toString(count++));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
/**
* 这里就是 RemoteCallbackList 的关键用法了
* beginBroadcast 和 finishBroadcast 需要配套使用
* beginBroadcast 会返回注册了的客户端数量,然后开一个循环依次取出客户端注册的回调,并调用回调内的
* onReceived 函数。这个函数需要客户端实现 ICallbackTestCallback.Stub 之后,注册给服务端
* @param msg
*/
private void noteClients(String msg){
int cb = clients.beginBroadcast();
for(int i=0;i<cb;i++){
try {
clients.getBroadcastItem(i).onReceived(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
clients.finishBroadcast();
}
}
服务端对应的 AndroidMenifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.callback.server">
<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.Server">
<service android:name=".callbackserver"
android:exported="true">
</service>
</application>
</manifest>
简单的表示有一个service就好,其他的内容和一般的app一样。
p.s: 这里我是将服务端和客户端分为两个app来实现了。
服务端的文件的结构如下
C:.
├─aidl
│ └─com
│ └─callback
│ ICallbackTestCallback.aidl
│ ICallBackTestInterface.aidl
├─java
│ └─com
│ └─callback
│ └─server
│ callbackserver.java
└─res
├─drawable
├─drawable-v24
├─layout
├─mipmap-anydpi-v26
├─mipmap-hdpi
├─mipmap-mdpi
├─mipmap-xhdpi
├─mipmap-xxhdpi
├─mipmap-xxxhdpi
├─values
└─values-night
服务端的全部内容如上面所示。
客户端
客户端我们使用两个独立的进程(当时在同一个app里面)
客户端的 AndroidMenifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.callback.client">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Client"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Client">
<activity android:name=".ActivityB"
android:launchMode="singleInstance"
android:process="com.callback.client.ActivityB"
android:label="Activityb">
</activity>
<activity
android:name=".ActivityA"
android:launchMode="singleInstance"
android:process="com.callback.client.ActivityA"
android:label="ActivityA">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
我定义了两个Activity,一个ActivityA,一个ActivityB。
ActivityA的实现如下
package com.callback.client;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.callback.ICallbackTestCallback;
import com.callback.ICallBackTestInterface;
public class ActivityA extends AppCompatActivity implements View.OnClickListener{
private final String TAG = "testcallback";
private boolean bound = false;
ICallBackTestInterface remoteServer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button activityb = findViewById(R.id.startactivity);
activityb.setText("open activity b");
initClickListener();
}
@Override
protected void onResume() {
if(bound){
registerCallback();
}
super.onResume();
}
@Override
protected void onPause() {
if(bound){
unregisterCallback();
}
super.onPause();
}
private final ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
remoteServer = ICallBackTestInterface.Stub.asInterface(service);
registerCallback();
bound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private ICallbackTestCallback callback = new ICallbackTestCallback.Stub(){
@Override
public void onReceived(String msg) throws RemoteException {
Log.d(TAG,"received msg: " + msg + " . from server pid=" + Binder.getCallingPid());
}
};
private void initClickListener(){
Button button = findViewById(R.id.registerBotton);
button.setOnClickListener(this);
button=findViewById(R.id.unregisterBotton);
button.setOnClickListener(this);
button=findViewById(R.id.bindServer);
button.setOnClickListener(this);
button=findViewById(R.id.startactivity);
button.setOnClickListener(this);
}
private void unregisterCallback(){
if(remoteServer != null){
try {
remoteServer.unregister(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.e(TAG," null remoteServer");
}
}
private void callServer(String msg){
if(remoteServer != null){
try {
remoteServer.callServer(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.e(TAG," null remoteServer");
}
}
public void bindServer(){
Intent serverIntent = new Intent();
// 这里通过包名和class名来Bindservice。
serverIntent.setComponent(new ComponentName("com.callback.server","com.callback.server.callbackserver"));
bindService(serverIntent,serviceConnection,Context.BIND_AUTO_CREATE);
}
private void startActivity(){
Intent intent = new Intent(ActivityA.this,ActivityB.class);
startActivity(intent);
}
public void registerCallback() {
if(remoteServer != null){
try {
remoteServer.register(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.e(TAG," null remoteServer");
}
}
// Activity 实现了 View.OnClickListener 的话,需要实现对应的接口 onClick
@Override
public void onClick(View view) {
int id = view.getId();
switch (id){
case R.id.registerBotton:
registerCallback();
break;
case R.id.unregisterBotton:
unregisterCallback();
break;
case R.id.bindServer:
bindServer();
break;
case R.id.startactivity:
startActivity();
break;
}
}
}
ActivityA对应的资源文件 activity_main.xml
<?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=".ActivityA">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/registerBotton"
android:textAllCaps="false"
android:layout_marginLeft="5dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:text="register"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/unregisterBotton"
android:textAllCaps="false"
android:layout_marginLeft="5dp"
app:layout_constraintTop_toBottomOf="@id/registerBotton"
app:layout_constraintLeft_toLeftOf="parent"
android:text="unregister"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bindServer"
android:textAllCaps="false"
android:layout_marginLeft="5dp"
app:layout_constraintTop_toBottomOf="@id/unregisterBotton"
app:layout_constraintLeft_toLeftOf="parent"
android:text="Bind server"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/startactivity"
android:textAllCaps="false"
android:layout_marginLeft="5dp"
app:layout_constraintTop_toBottomOf="@id/bindServer"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
ActivityB 的实现和 ActivityA基本一致。
客户端的文件结构如下
C:.
├─aidl
│ └─com
│ └─callback
│ ICallbackTestCallback.aidl
│ ICallBackTestInterface.aidl
├─java
│ └─com
│ └─callback
│ └─client
│ ActivityA.java
│ ActivityB.java
└─res
├─drawable
├─drawable-v24
├─layout
│ activity_main.xml
├─mipmap-anydpi-v26
├─mipmap-hdpi
├─mipmap-mdpi
├─mipmap-xhdpi
├─mipmap-xxhdpi
├─mipmap-xxxhdpi
├─values
└─values-night
然后我们可以得到这样一个应用
上面有4个按钮,分别用来向服务端注册和注销自己,绑定服务端,和打开另外一个Activity。
使用
在分屏模式下可以同时打开两个Activity,分别点击两个Activity内的bind server和register。就可以在logcat内看到对应的内容了
05-08 12:48:45.938 6671 6671 D testcallback: callback test server create
05-08 12:48:45.940 6671 6687 D testcallback: register callback from pid=6631
05-08 12:48:46.439 6631 6653 D testcallback: received msg: 0 . from server pid=6671
05-08 12:48:46.940 6631 6653 D testcallback: received msg: 1 . from server pid=6671
05-08 12:48:47.441 6631 6653 D testcallback: received msg: 2 . from server pid=6671
05-08 12:48:47.944 6631 6653 D testcallback: received msg: 3 . from server pid=6671
05-08 12:48:48.409 6671 6687 D testcallback: register callback from pid=6597
05-08 12:48:48.445 6597 6614 D testcallback: received msg: 4 . from server pid=6671
05-08 12:48:48.446 6631 6648 D testcallback: received msg: 4 . from server pid=6671
05-08 12:48:48.948 6597 6614 D testcallback: received msg: 5 . from server pid=6671
05-08 12:48:48.949 6631 6648 D testcallback: received msg: 5 . from server pid=6671
05-08 12:48:49.451 6597 6614 D testcallback: received msg: 6 . from server pid=6671
05-08 12:48:49.451 6631 6648 D testcallback: received msg: 6 . from server pid=6671
05-08 12:48:49.953 6597 6614 D testcallback: received msg: 7 . from server pid=6671
05-08 12:48:49.954 6631 6648 D testcallback: received msg: 7 . from server pid=6671
05-08 12:48:50.456 6597 6614 D testcallback: received msg: 8 . from server pid=6671
05-08 12:48:50.457 6631 6648 D testcallback: received msg: 8 . from server pid=6671
05-08 12:48:50.958 6597 6614 D testcallback: received msg: 9 . from server pid=6671
05-08 12:48:50.959 6631 6648 D testcallback: received msg: 9 . from server pid=6671
05-08 12:48:51.461 6597 6614 D testcallback: received msg: 10 . from server pid=6671
05-08 12:48:51.461 6631 6648 D testcallback: received msg: 10 . from server pid=6671
05-08 12:48:51.964 6597 6614 D testcallback: received msg: 11 . from server pid=6671
05-08 12:48:51.964 6631 6648 D testcallback: received msg: 11 . from server pid=6671
05-08 12:48:52.465 6597 6614 D testcallback: received msg: 12 . from server pid=6671
05-08 12:48:52.466 6631 6648 D testcallback: received msg: 12 . from server pid=6671
05-08 12:48:52.967 6597 6614 D testcallback: received msg: 13 . from server pid=6671
05-08 12:48:52.967 6631 6648 D testcallback: received msg: 13 . from server pid=6671
05-08 12:48:53.469 6597 6614 D testcallback: received msg: 14 . from server pid=6671
05-08 12:48:53.470 6631 6648 D testcallback: received msg: 14 . from server pid=6671
05-08 12:48:53.972 6597 6614 D testcallback: received msg: 15 . from server pid=6671
05-08 12:48:53.972 6631 6648 D testcallback: received msg: 15 . from server pid=6671
05-08 12:48:54.474 6597 6614 D testcallback: received msg: 16 . from server pid=6671
05-08 12:48:54.475 6631 6648 D testcallback: received msg: 16 . from server pid=6671
05-08 12:48:54.977 6597 6614 D testcallback: received msg: 17 . from server pid=6671
05-08 12:48:54.977 6631 6648 D testcallback: received msg: 17 . from server pid=6671
05-08 12:48:55.478 6597 6614 D testcallback: received msg: 18 . from server pid=6671
05-08 12:48:55.479 6631 6648 D testcallback: received msg: 18 . from server pid=6671
05-08 12:48:55.981 6597 6614 D testcallback: received msg: 19 . from server pid=6671
05-08 12:48:55.982 6631 6648 D testcallback: received msg: 19 . from server pid=6671
05-08 12:48:56.484 6597 6614 D testcallback: received msg: 20 . from server pid=6671
05-08 12:48:56.484 6631 6648 D testcallback: received msg: 20 . from server pid=6671
05-08 12:48:56.987 6597 6614 D testcallback: received msg: 21 . from server pid=6671
05-08 12:48:56.987 6631 6648 D testcallback: received msg: 21 . from server pid=6671
05-08 12:48:57.489 6597 6614 D testcallback: received msg: 22 . from server pid=6671
05-08 12:48:57.489 6631 6648 D testcallback: received msg: 22 . from server pid=6671
05-08 12:48:57.992 6597 6614 D testcallback: received msg: 23 . from server pid=6671
05-08 12:48:57.992 6631 6648 D testcallback: received msg: 23 . from server pid=6671
05-08 12:48:58.494 6597 6614 D testcallback: received msg: 24 . from server pid=6671
05-08 12:48:58.495 6631 6648 D testcallback: received msg: 24 . from server pid=6671
05-08 12:48:58.997 6597 6614 D testcallback: received msg: 25 . from server pid=6671
05-08 12:48:58.998 6631 6648 D testcallback: received msg: 25 . from server pid=6671
05-08 12:48:59.501 6597 6614 D testcallback: received msg: 26 . from server pid=6671
05-08 12:48:59.501 6631 6648 D testcallback: received msg: 26 . from server pid=6671
05-08 12:49:00.003 6597 6614 D testcallback: received msg: 27 . from server pid=6671
05-08 12:49:00.004 6631 6648 D testcallback: received msg: 27 . from server pid=6671
05-08 12:49:00.506 6597 6614 D testcallback: received msg: 28 . from server pid=6671
05-08 12:49:00.506 6631 6648 D testcallback: received msg: 28 . from server pid=6671
05-08 12:49:01.009 6597 6614 D testcallback: received msg: 29 . from server pid=6671
05-08 12:49:01.010 6631 6648 D testcallback: received msg: 29 . from server pid=6671
05-08 12:49:01.511 6597 6614 D testcallback: received msg: 30 . from server pid=6671
05-08 12:49:01.512 6631 6648 D testcallback: received msg: 30 . from server pid=6671
05-08 12:49:02.014 6597 6614 D testcallback: received msg: 31 . from server pid=6671
05-08 12:49:02.014 6631 6648 D testcallback: received msg: 31 . from server pid=6671
05-08 12:49:02.517 6597 6614 D testcallback: received msg: 32 . from server pid=6671
05-08 12:49:02.517 6631 6648 D testcallback: received msg: 32 . from server pid=6671
05-08 12:49:03.019 6597 6614 D testcallback: received msg: 33 . from server pid=6671
05-08 12:49:03.019 6631 6648 D testcallback: received msg: 33 . from server pid=6671
05-08 12:49:03.521 6597 6614 D testcallback: received msg: 34 . from server pid=6671
05-08 12:49:03.522 6631 6648 D testcallback: received msg: 34 . from server pid=6671
05-08 12:49:04.024 6597 6614 D testcallback: received msg: 35 . from server pid=6671
05-08 12:49:04.024 6631 6648 D testcallback: received msg: 35 . from server pid=6671
05-08 12:49:04.527 6597 6614 D testcallback: received msg: 36 . from server pid=6671
05-08 12:49:04.527 6631 6648 D testcallback: received msg: 36 . from server pid=6671
05-08 12:49:05.030 6597 6614 D testcallback: received msg: 37 . from server pid=6671
05-08 12:49:05.030 6631 6648 D testcallback: received msg: 37 . from server pid=6671
05-08 12:49:05.329 6671 6687 D testcallback: unregister callback from pid=6597
05-08 12:49:05.533 6631 6648 D testcallback: received msg: 38 . from server pid=6671
05-08 12:49:06.034 6631 6648 D testcallback: received msg: 39 . from server pid=6671
05-08 12:49:06.537 6631 6648 D testcallback: received msg: 40 . from server pid=6671
05-08 12:49:07.039 6631 6648 D testcallback: received msg: 41 . from server pid=6671
05-08 12:49:07.542 6631 6648 D testcallback: received msg: 42 . from server pid=6671
05-08 12:49:08.043 6631 6648 D testcallback: received msg: 43 . from server pid=6671
05-08 12:49:08.545 6631 6648 D testcallback: received msg: 44 . from server pid=6671
05-08 12:49:09.046 6631 6648 D testcallback: received msg: 45 . from server pid=6671
05-08 12:49:09.548 6631 6648 D testcallback: received msg: 46 . from server pid=6671
05-08 12:49:10.051 6631 6648 D testcallback: received msg: 47 . from server pid=6671
05-08 12:49:10.513 6671 6687 D testcallback: unregister callback from pid=6631
05-08 12:49:14.176 6671 6687 D testcallback: unregister callback from pid=6631
05-08 12:49:14.896 6671 6687 D testcallback: unregister callback from pid=6597
服务端的功能是自增一个整数,并不断的返回给客户端。客户端通过bindservice拿到了服务端的Binder对象之后,通过调用服务端Binder对象的register和unregister来注册和注销实现的 ICallbackTestCallback 。
服务单通过 RemoteCallbackList 取到客户端注册的 ICallbackTestCallback 之后,调用对应的 onReceived() 函数来把服务端的数据返回给客户端。
以此完成客户端和服务端的双向通信。
下一篇,我们来动手将服务端预置在系统内。