阅读 141

安卓 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

服务器评测 http://www.cncsto.com/ 


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐