安卓 RemoteCallbackList 的使用
通过aidl,我们可以实现client(后称客户端)和server(服务端)的双向通信,有时候server和client处于不同的进程当中,如果client意外退出,server再向client发送消息的话,就有可能导致server端也退出。
安卓提供了 RemoteCallbackList 来为我们隐式解决了这种问题。
下面来看一个示范。
首先我们定义用于客户端向服务端通信的一个 aidl
// ICallBackTestInterface.aidlpackage com.callback;// Declare any non-default types here with import statementsimport com.callback.ICallbackTestCallback;interface ICallBackTestInterface { // 向服务端注册客户端回调 void register(ICallbackTestCallback callback); // 向服务端注销客户端回调 void unregister(ICallbackTestCallback callback); // 向服务端发送消息 void callServer(String msg); }
其内的 ICallbackTestCallback 也是一个 aidl,其内容如下
// ICallbackTestCallback.aidlpackage com.callback;// Declare any non-default types here with import statementsinterface 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.javapackage 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 create05-08 12:48:45.940 6671 6687 D testcallback: register callback from pid=663105-08 12:48:46.439 6631 6653 D testcallback: received msg: 0 . from server pid=667105-08 12:48:46.940 6631 6653 D testcallback: received msg: 1 . from server pid=667105-08 12:48:47.441 6631 6653 D testcallback: received msg: 2 . from server pid=667105-08 12:48:47.944 6631 6653 D testcallback: received msg: 3 . from server pid=667105-08 12:48:48.409 6671 6687 D testcallback: register callback from pid=659705-08 12:48:48.445 6597 6614 D testcallback: received msg: 4 . from server pid=667105-08 12:48:48.446 6631 6648 D testcallback: received msg: 4 . from server pid=667105-08 12:48:48.948 6597 6614 D testcallback: received msg: 5 . from server pid=667105-08 12:48:48.949 6631 6648 D testcallback: received msg: 5 . from server pid=667105-08 12:48:49.451 6597 6614 D testcallback: received msg: 6 . from server pid=667105-08 12:48:49.451 6631 6648 D testcallback: received msg: 6 . from server pid=667105-08 12:48:49.953 6597 6614 D testcallback: received msg: 7 . from server pid=667105-08 12:48:49.954 6631 6648 D testcallback: received msg: 7 . from server pid=667105-08 12:48:50.456 6597 6614 D testcallback: received msg: 8 . from server pid=667105-08 12:48:50.457 6631 6648 D testcallback: received msg: 8 . from server pid=667105-08 12:48:50.958 6597 6614 D testcallback: received msg: 9 . from server pid=667105-08 12:48:50.959 6631 6648 D testcallback: received msg: 9 . from server pid=667105-08 12:48:51.461 6597 6614 D testcallback: received msg: 10 . from server pid=667105-08 12:48:51.461 6631 6648 D testcallback: received msg: 10 . from server pid=667105-08 12:48:51.964 6597 6614 D testcallback: received msg: 11 . from server pid=667105-08 12:48:51.964 6631 6648 D testcallback: received msg: 11 . from server pid=667105-08 12:48:52.465 6597 6614 D testcallback: received msg: 12 . from server pid=667105-08 12:48:52.466 6631 6648 D testcallback: received msg: 12 . from server pid=667105-08 12:48:52.967 6597 6614 D testcallback: received msg: 13 . from server pid=667105-08 12:48:52.967 6631 6648 D testcallback: received msg: 13 . from server pid=667105-08 12:48:53.469 6597 6614 D testcallback: received msg: 14 . from server pid=667105-08 12:48:53.470 6631 6648 D testcallback: received msg: 14 . from server pid=667105-08 12:48:53.972 6597 6614 D testcallback: received msg: 15 . from server pid=667105-08 12:48:53.972 6631 6648 D testcallback: received msg: 15 . from server pid=667105-08 12:48:54.474 6597 6614 D testcallback: received msg: 16 . from server pid=667105-08 12:48:54.475 6631 6648 D testcallback: received msg: 16 . from server pid=667105-08 12:48:54.977 6597 6614 D testcallback: received msg: 17 . from server pid=667105-08 12:48:54.977 6631 6648 D testcallback: received msg: 17 . from server pid=667105-08 12:48:55.478 6597 6614 D testcallback: received msg: 18 . from server pid=667105-08 12:48:55.479 6631 6648 D testcallback: received msg: 18 . from server pid=667105-08 12:48:55.981 6597 6614 D testcallback: received msg: 19 . from server pid=667105-08 12:48:55.982 6631 6648 D testcallback: received msg: 19 . from server pid=667105-08 12:48:56.484 6597 6614 D testcallback: received msg: 20 . from server pid=667105-08 12:48:56.484 6631 6648 D testcallback: received msg: 20 . from server pid=667105-08 12:48:56.987 6597 6614 D testcallback: received msg: 21 . from server pid=667105-08 12:48:56.987 6631 6648 D testcallback: received msg: 21 . from server pid=667105-08 12:48:57.489 6597 6614 D testcallback: received msg: 22 . from server pid=667105-08 12:48:57.489 6631 6648 D testcallback: received msg: 22 . from server pid=667105-08 12:48:57.992 6597 6614 D testcallback: received msg: 23 . from server pid=667105-08 12:48:57.992 6631 6648 D testcallback: received msg: 23 . from server pid=667105-08 12:48:58.494 6597 6614 D testcallback: received msg: 24 . from server pid=667105-08 12:48:58.495 6631 6648 D testcallback: received msg: 24 . from server pid=667105-08 12:48:58.997 6597 6614 D testcallback: received msg: 25 . from server pid=667105-08 12:48:58.998 6631 6648 D testcallback: received msg: 25 . from server pid=667105-08 12:48:59.501 6597 6614 D testcallback: received msg: 26 . from server pid=667105-08 12:48:59.501 6631 6648 D testcallback: received msg: 26 . from server pid=667105-08 12:49:00.003 6597 6614 D testcallback: received msg: 27 . from server pid=667105-08 12:49:00.004 6631 6648 D testcallback: received msg: 27 . from server pid=667105-08 12:49:00.506 6597 6614 D testcallback: received msg: 28 . from server pid=667105-08 12:49:00.506 6631 6648 D testcallback: received msg: 28 . from server pid=667105-08 12:49:01.009 6597 6614 D testcallback: received msg: 29 . from server pid=667105-08 12:49:01.010 6631 6648 D testcallback: received msg: 29 . from server pid=667105-08 12:49:01.511 6597 6614 D testcallback: received msg: 30 . from server pid=667105-08 12:49:01.512 6631 6648 D testcallback: received msg: 30 . from server pid=667105-08 12:49:02.014 6597 6614 D testcallback: received msg: 31 . from server pid=667105-08 12:49:02.014 6631 6648 D testcallback: received msg: 31 . from server pid=667105-08 12:49:02.517 6597 6614 D testcallback: received msg: 32 . from server pid=667105-08 12:49:02.517 6631 6648 D testcallback: received msg: 32 . from server pid=667105-08 12:49:03.019 6597 6614 D testcallback: received msg: 33 . from server pid=667105-08 12:49:03.019 6631 6648 D testcallback: received msg: 33 . from server pid=667105-08 12:49:03.521 6597 6614 D testcallback: received msg: 34 . from server pid=667105-08 12:49:03.522 6631 6648 D testcallback: received msg: 34 . from server pid=667105-08 12:49:04.024 6597 6614 D testcallback: received msg: 35 . from server pid=667105-08 12:49:04.024 6631 6648 D testcallback: received msg: 35 . from server pid=667105-08 12:49:04.527 6597 6614 D testcallback: received msg: 36 . from server pid=667105-08 12:49:04.527 6631 6648 D testcallback: received msg: 36 . from server pid=667105-08 12:49:05.030 6597 6614 D testcallback: received msg: 37 . from server pid=667105-08 12:49:05.030 6631 6648 D testcallback: received msg: 37 . from server pid=667105-08 12:49:05.329 6671 6687 D testcallback: unregister callback from pid=659705-08 12:49:05.533 6631 6648 D testcallback: received msg: 38 . from server pid=667105-08 12:49:06.034 6631 6648 D testcallback: received msg: 39 . from server pid=667105-08 12:49:06.537 6631 6648 D testcallback: received msg: 40 . from server pid=667105-08 12:49:07.039 6631 6648 D testcallback: received msg: 41 . from server pid=667105-08 12:49:07.542 6631 6648 D testcallback: received msg: 42 . from server pid=667105-08 12:49:08.043 6631 6648 D testcallback: received msg: 43 . from server pid=667105-08 12:49:08.545 6631 6648 D testcallback: received msg: 44 . from server pid=667105-08 12:49:09.046 6631 6648 D testcallback: received msg: 45 . from server pid=667105-08 12:49:09.548 6631 6648 D testcallback: received msg: 46 . from server pid=667105-08 12:49:10.051 6631 6648 D testcallback: received msg: 47 . from server pid=667105-08 12:49:10.513 6671 6687 D testcallback: unregister callback from pid=663105-08 12:49:14.176 6671 6687 D testcallback: unregister callback from pid=663105-08 12:49:14.896 6671 6687 D testcallback: unregister callback from pid=6597
服务端的功能是自增一个整数,并不断的返回给客户端。客户端通过bindservice拿到了服务端的Binder对象之后,通过调用服务端Binder对象的register和unregister来注册和注销实现的 ICallbackTestCallback 。
服务单通过 RemoteCallbackList 取到客户端注册的 ICallbackTestCallback 之后,调用对应的 onReceived() 函数来把服务端的数据返回给客户端。
以此完成客户端和服务端的双向通信。
来源:https://www.cnblogs.com/SupperMary/p/14746477.html