Android AIDL的实现

简介

  • AIDL概念理解
  • AIDL服务端创建
  • 客户端创建
  • 测试结果
  • 测试方法,结果展示
  • 自定义接口回调,动态接收服务端消息
  • RemoteCallbackList 删除跨进程Listener

一、AIDL概念理解

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数

AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。

其中对于Java编程语言的基本数据类型 (int, long, char, boolean等),String和CharSequence,集合接口类型List和Map,不需要import 语句。 而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import.

需要特别注意的是, 对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
AIDL只支持接口方法,不能公开static变量。

其中AIDL的方法还提供了oneway这个关键字,可以用关键字oneway来标明远程调用的行为属性,使用了该关键字,那么远程调用将仅仅是调用所需的数据传输过来并立即返回,而不会等待结果的返回,也即是说不会阻塞远程线程的运行。AIDL接口将最终将获得一个从Binder线程池中产生的调用(和普通的远程调用类似)。如果关键字oneway在本地调用中被使用,将不会对函数调用有任何影响。

二、AIDL创建-- 服务端

  1. 创建Bean 对象 并实现Parcelable 序列化
package mvvm.com.git1;
public class HelloMsg  implements Parcelable{
    private String msg;
    private int pid;

    public HelloMsg(Parcel in) {
        msg = in.readString();
        pid = in.readInt();
    }

    public HelloMsg(String msg, int pid) {
        this.msg = msg;
        this.pid = pid;
    }

    public static final Creator<HelloMsg> CREATOR = new Creator<HelloMsg>() {
        @Override
        public HelloMsg createFromParcel(Parcel in) {
            return new HelloMsg(in);
        }

        @Override
        public HelloMsg[] newArray(int size) {
            return new HelloMsg[size];
        }
    };

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(msg);
        dest.writeInt(pid);
    }
}

2.AIDL服务端接口创建 ,注意要和Bean对象创建再同一个package 下 。AIDL的特殊要求


AIDL服务端接口创建.png

创建和HelloMsg Bean同名的 HelloMsg.aidl文件 ,且parcelable 引入 HelloMsg

// HelloMsg.aidl 注意所在的包 
package mvvm.com.git1;
//引入bean对象
parcelable HelloMsg;

创建IRemoteService.aidl 所在包 同HelloMsg 一致,且引入 import 引入 HelloMsg 。AIDL的特殊之处

// IRemoteService.aidl 。
package mvvm.com.git1;

import mvvm.com.git1.HelloMsg;
interface IRemoteService {
    HelloMsg sayHello();// 服务端 返回 语言
    List<HelloMsg> getMsgList(); // 返回 服务端 全部 语言总和
    void addMsg(in HelloMsg msg);// 客户端新增的语言
}

3.Rebuild Project 项目 ,会在app -> build -> generated ->source -> aidl -> debug -> 包名 -> IRemoteService 文件 。说明 AIDL创建成功,文件内容后面讲解。

  1. 创建服务端 RemoteService ,manifest文件中配置 process
/**
 * Created by stf on 2020/4/2.
 * 服务端
 */

public class RemoteService extends Service {

    List<HelloMsg> helloList = new ArrayList<>();
    IRemoteService.Stub stub = new IRemoteService.Stub() {
        @Override
        public HelloMsg sayHello() throws RemoteException {
            return new HelloMsg("来自服务端的消息了是1111", 1);
        }

        @Override
        public List<HelloMsg> getMsgList() throws RemoteException {
            Log.i("stf", "语言总共" + helloList.size() + "个");
            for (int i = 0; i < helloList.size(); i++) {
                HelloMsg helloMsg = helloList.get(i);
                Log.i("stf", "服务端语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            return helloList;
        }

        @Override
        public void addMsg(HelloMsg msg) throws RemoteException {
            Log.i("stf", "服务端收到了增加的语言" + msg.getMsg() + "--->" + msg.getPid());
            helloList.add(msg);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return stub;\\绑定bind
    }


}

manifest文件中配置 process

<service
            android:name=".RemoteService"
            android:process="com.stf.remote" />

三、创建客户端

1.客户端负责连接、绑定、解绑 服务端

  1. 给服务端设置 死亡代理 ,防止 服务端 异常 导致客户端连接失败,导致客户端异常。
  2. 创建客户端调用 服务端,数据交互
  • 连接 服务端
// 客户端向服务端 建立 请求链接
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteService = IRemoteService.Stub.asInterface(service);
            Toast.makeText(Main6Activity.this, "onServiceConnected ", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            Toast.makeText(Main6Activity.this, "onServiceDisconnected ", Toast.LENGTH_SHORT).show();
        }
    };
  • 绑定远程服务
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
  • 解绑远程服务
// 防止 服务端 连接泄漏 解除绑定
        unbindService(mConnection);
  • 给远程服务设置死亡代理类
// 当绑定的service异常断开连接后,自动执行此方法
    IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mRemoteService == null) {
                toastMsg();
                return;
            }

            //解除死亡通知,如果Binder死亡了,不会在触发binderDied方法
            mRemoteService.asBinder().unlinkToDeath(deathRecipient, 0);
            // 重新绑定一次
            Intent intent = new Intent(Main6Activity.this, RemoteService.class);
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        }
    };

service.linkToDeath(deathRecipient, 0); // 给远程服务设置死亡代理类
  • 测试方法
 // 增加语言内容
    public void say_add_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            int i = new Random().nextInt(100);
            HelloMsg helloMsg = new HelloMsg("羊皮卷" + i, i);
            mRemoteService.addMsg(helloMsg);
            mPidText.setText(helloMsg.getMsg() + "-->" + helloMsg.getPid());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }


// 获取服务端语言内容
    public void say_get_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            List<HelloMsg> helloList = mRemoteService.getMsgList();
            Log.i("stf", "服务端返回语言总数为:" + helloList.size());
            for (int i = 0; i < helloList.size(); i++) {
                HelloMsg helloMsg = helloList.get(i);
                Log.i("stf", "客户端收到服务端返回语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            mPidText.setText("服务端返回语言总数为:" + helloList.size());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

// 每次调用服务端之前 ,判断bind 是否是否存活
    public Boolean isBinderAlive() {
        return mRemoteService != null && mRemoteService.asBinder().isBinderAlive();
    }

四、客户端全部代码

// 客户端
public class Main6Activity extends AppCompatActivity {
    private IRemoteService mRemoteService = null;
    private TextView mPidText;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);
        mPidText = (TextView) findViewById(R.id.my_pid_text_view);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 绑定远程服务
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    // 客户端向服务端 建立 请求链接
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteService = IRemoteService.Stub.asInterface(service);
            Toast.makeText(Main6Activity.this, "onServiceConnected ", Toast.LENGTH_SHORT).show();
            try {
                service.linkToDeath(deathRecipient, 0); // 给远程服务设置死亡代理类
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            Toast.makeText(Main6Activity.this, "onServiceDisconnected ", Toast.LENGTH_SHORT).show();
        }
    };

    // 当绑定的service异常断开连接后,自动执行此方法
    IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mRemoteService == null) {
                toastMsg();
                return;
            }

            //解除死亡通知,如果Binder死亡了,不会在触发binderDied方法
            mRemoteService.asBinder().unlinkToDeath(deathRecipient, 0);
            // 重新绑定一次
            Intent intent = new Intent(Main6Activity.this, RemoteService.class);
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        // 防止 服务端 链接泄漏 解除绑定
        unbindService(mConnection);
    }

    // 增加语言内容
    public void say_add_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            int i = new Random().nextInt(100);
            HelloMsg helloMsg = new HelloMsg("羊皮卷" + i, i);
            mRemoteService.addMsg(helloMsg);
            mPidText.setText(helloMsg.getMsg() + "-->" + helloMsg.getPid());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    // 获取服务端语言内容
    public void say_get_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            List<HelloMsg> helloList = mRemoteService.getMsgList();
            Log.i("stf", "服务端返回语言总数为:" + helloList.size());
            for (int i = 0; i < helloList.size(); i++) {
                HelloMsg helloMsg = helloList.get(i);
                Log.i("stf", "客户端收到服务端返回语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            mPidText.setText("服务端返回语言总数为:" + helloList.size());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    // 每次调用服务端之前 ,判断bind 是否是否存活
    public Boolean isBinderAlive() {
        return mRemoteService != null && mRemoteService.asBinder().isBinderAlive();
    }

    public void toastMsg() {
        Toast.makeText(this, "服务端异常,请稍后重试", Toast.LENGTH_SHORT).show();
    }
}

五、测试结果

  • 点击say_add_button 像服务端增加语言内容 。需求:模拟别人给你留言时发消息
    log日志如下
服务端收到新增留言.png
  • 点击 say_get_button 从服务端获取添加的所有语言 。需求:你自己主动去服务端查询 别人和你的留言内容
    log日志如下
服务端返回前 记录.png
客户端收到返回结果 记录.png

到此为止 完成了基本的信息传递。显然上面的交互时留言,貌似没问题,但是 假如 当有新的留言时 ,服务端能不能主动的通知你,上面的交互显然时需要你自己主动查询,并不能满足你的需求。为解决这个问题 ,请继续往下看。

六、自定义接口 动态接收 服务端消息
  1. 首先创建一个AIDL 接口 ,每个客户端都需要实现这个接口 ,来达到 新留言来的时候通知到它。
    因为AIDL 无法创建普通的接口,所以这里创建一个IOnNewMsgArrivedListener.aidl 文件 ,它所在的包还是和前面保持一致


    aidl.png
package mvvm.com.git1;

import mvvm.com.git1.HelloMsg;

interface IOnNewMsgArrivedListener {
     void OnNewMsgArrived(in HelloMsg msg);//回调方法
}
  1. 在原来的IRemoteService.aidl 中新增两个接口,注册和解除 接口 。
package mvvm.com.git1;

import mvvm.com.git1.HelloMsg;
import mvvm.com.git1.IOnNewMsgArrivedListener;
interface IRemoteService {
    HelloMsg sayHello();// 服务端 返回 语言
    List<HelloMsg> getMsgList(); // 返回 服务端 全部 语言总和
    void addMsg(in HelloMsg msg);// 客户端新增的语言
    void registerListener(IOnNewMsgArrivedListener listener); //注册 新语言来时的事件
    void unregisterListener(IOnNewMsgArrivedListener listener); //解除 新语言来时的事件
}
  1. RemoteService服务端 Sub实现两个新增接口 ,同时开启一个子线程 间隔1s 发送一条留言 并通知 用户。

为了线程安全 ,对前面的服务端RemoteService有点小修改

/**
 * Created by stf on 2020/4/2.
 * 服务端
 */

public class RemoteService extends Service {

    /***
     * CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,
     * 很常一段时间,这个容器数据、数量等没有发生变化的时候,
     * 大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,
     * 但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),
     * 再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,
     * 但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。
     *
     * 在java.util.concurrent.atomic包下,有AtomicBoolean , AtomicInteger, AtomicLong, AtomicReference等类,
     * 它们的基本特性就是在多线程环境下,执行这些类实例包含的方法时,具有排他性,
     * 即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,
     * 而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入。
     */
    private AtomicBoolean mIsServiceDestory = new AtomicBoolean(false); // 控制线程
    private CopyOnWriteArrayList<HelloMsg> mHelloList = new CopyOnWriteArrayList<>();
    private CopyOnWriteArrayList<IOnNewMsgArrivedListener> msgArrivedListeners = new CopyOnWriteArrayList<>();

    IRemoteService.Stub stub = new IRemoteService.Stub() {
        @Override
        public HelloMsg sayHello() throws RemoteException {
            return new HelloMsg("来自服务端的消息了是1111", 1);
        }

        @Override
        public List<HelloMsg> getMsgList() throws RemoteException {
            Log.i("stf", "语言总共" + mHelloList.size() + "个");
            for (int i = 0; i < mHelloList.size(); i++) {
                HelloMsg helloMsg = mHelloList.get(i);
                Log.i("stf", "服务端语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            return mHelloList;
        }

        @Override
        public void addMsg(HelloMsg msg) throws RemoteException {
            Log.i("stf", "服务端收到了增加的语言" + msg.getMsg() + "--->" + msg.getPid());
            mHelloList.add(msg);
        }

        @Override
        public void registerListener(IOnNewMsgArrivedListener listener) throws RemoteException {
            if (!msgArrivedListeners.contains(listener)) {
                msgArrivedListeners.add(listener);
            } else {
                Log.i("stf", "registerListener: already exists ");
            }
            Log.i("stf", "registerListener: size" + msgArrivedListeners.size());
        }

        @Override
        public void unregisterListener(IOnNewMsgArrivedListener listener) throws RemoteException {
            if (!msgArrivedListeners.contains(listener)) {
                msgArrivedListeners.remove(listener);
            } else {
                Log.i("stf", "unregisterListener: already exists ");
            }
            Log.i("stf", "unregisterListener: size" + msgArrivedListeners.size());
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestory.set(true);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //开启子线程 创建新留言
        new Thread(new ServiceWorker()).start();
    }



    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestory.get()) {
                try {
                    Thread.sleep(1000);

                    int helloId = mHelloList.size() + 1;
                    HelloMsg helloMsg = new HelloMsg("我来了,大哥" + helloId, helloId);
                    if (helloId == 10) {
                        mIsServiceDestory.set(true);
                    }
                    Log.i("stf", "服务端有新留言了: " + helloMsg.getMsg());
                    onMsgArrived(helloMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //通知用户 新留言来了
    private void onMsgArrived(HelloMsg helloMsg) {
        mHelloList.add(helloMsg); //收集所有留言
      //  Log.i("stf", "onMsgArrived: noitfy listenters:" + msgArrivedListeners.size());
        for (int i = 0; i < msgArrivedListeners.size(); i++) {
            IOnNewMsgArrivedListener listener = msgArrivedListeners.get(i);
            try {
                listener.OnNewMsgArrived(helloMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 客户端负责 注册 及 解绑 当有新留言来时的回调事件;
    Handler 切换到UI 线程 更新 页面 信息
// 客户端
public class Main6Activity extends AppCompatActivity {
    private IRemoteService mRemoteService = null;
    private TextView mPidText;
    private MyHandler myHandler;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);
        mPidText = (TextView) findViewById(R.id.my_pid_text_view);
        myHandler = new MyHandler(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 绑定远程服务
        Intent intent = new Intent(this, RemoteService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    // 客户端向服务端 建立 请求链接
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteService = IRemoteService.Stub.asInterface(service);
            Toast.makeText(Main6Activity.this, "onServiceConnected ", Toast.LENGTH_SHORT).show();
            try {
                service.linkToDeath(deathRecipient, 0); // 给远程服务设置死亡代理类
                mRemoteService.registerListener(msgArrivedListener);// 注册服务端有语言时 更新事件
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            Toast.makeText(Main6Activity.this, "onServiceDisconnected ", Toast.LENGTH_SHORT).show();
        }
    };

    // 当绑定的service异常断开连接后,自动执行此方法
    IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mRemoteService == null) {
                toastMsg();
                return;
            }

            //解除死亡通知,如果Binder死亡了,不会在触发binderDied方法
            mRemoteService.asBinder().unlinkToDeath(deathRecipient, 0);
            // 重新绑定一次
            Intent intent = new Intent(Main6Activity.this, RemoteService.class);
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        }
    };
    //创建 新语言来时的监听
    private IOnNewMsgArrivedListener msgArrivedListener = new IOnNewMsgArrivedListener.Stub() {
        @Override
        public void OnNewMsgArrived(HelloMsg msg) throws RemoteException {
            Message message = myHandler.obtainMessage(1, msg);
            message.obj = msg.getMsg();
            message.arg1 = msg.getPid();
            message.sendToTarget();

        }
    };
    // 创建静态的Handler 防止内存泄漏
    static class MyHandler extends Handler {
        WeakReference<Main6Activity> outerClass;

        MyHandler(Main6Activity activity) {
            outerClass = new WeakReference<Main6Activity>(activity);
        }

        @Override
        public void handleMessage(android.os.Message msg) {
             Main6Activity main6Activity = outerClass.get();
            switch (msg.what) {
                case 1: {
                    //使用theClass访问外部类成员和方法
                    Log.i("stf", "客户端收到了服务的新留言 :" + msg.obj + "-->" + msg.arg1);
                    break;
                }
                default: {
                    Log.w("stf", "未知的Handler Message:" + msg.what);
                    super.handleMessage(msg);
                }
            }

        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (!isBinderAlive()) {
            return;
        }

        try {
            Log.i("stf", "unregister lisenter :" + msgArrivedListener);
            mRemoteService.unregisterListener(msgArrivedListener);
            // 防止 服务端 链接泄漏 解除绑定
            unbindService(mConnection);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    // 增加语言内容
    public void say_add_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            int i = new Random().nextInt(100);
            HelloMsg helloMsg = new HelloMsg("羊皮卷" + i, i);
            mRemoteService.addMsg(helloMsg);
            mPidText.setText(helloMsg.getMsg() + "-->" + helloMsg.getPid());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    // 获取服务端语言内容
    public void say_get_button(View view) {
        if (!isBinderAlive()) {
            toastMsg();
            return;
        }

        try {
            List<HelloMsg> helloList = mRemoteService.getMsgList();
            Log.i("stf", "服务端返回语言总数为:" + helloList.size());
            for (int i = 0; i < helloList.size(); i++) {
                HelloMsg helloMsg = helloList.get(i);
                Log.i("stf", "客户端收到服务端返回语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            mPidText.setText("服务端返回语言总数为:" + helloList.size());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    // 每次调用服务端之前 ,判断bind 是否是否存活
    public Boolean isBinderAlive() {
        return mRemoteService != null && mRemoteService.asBinder().isBinderAlive();
    }

    public void toastMsg() {
        Toast.makeText(this, "服务端异常,请稍后重试", Toast.LENGTH_SHORT).show();
    }
    
}
  1. 测试
    启动服务端,每间隔1s 向注册 回调事件的用户发送一条留言
    log 如下图
服务端.png
客户端.png

七、RemoteCallBackList 删除跨进 listener

到这里你感觉AIDL已经结束了,很简单嘛。那你像的太简单了
从上面的代码看出 ,Main6Activity 的onStop方法 去解除 已经注册到服务端的listner ,这就相当于用户不用 接收新留言了。如果按back 键退出Main6Activity ,打印出的logo 如下

image.png

程序没像我们预期的那样去执行,在解除的过程中,服务端竟然无法找到之前注册的哪个listener。在客户端我们注册和解除的时候明明时同一个,但是结果显示 却不是一个。
其实这是必然的,上面的写法在我们平时开始时对的,因为那是在同一个进程中,但是在多进程中却无法生效,因为Binder会把客户端传来的对象重新转化为一个新的对象。客户端虽然时同一个listener 但是到了服务端就发生了变化,对象是不能夸进程传输的 ,这就是AIDL 中自定义对象都必须实现Parcelable 接口的原因。那么此问题怎么破呢?

办法来了
使用RemoteCallBackList ,系统专门提供的删除跨进程listener接口。它的原理很简单,内部有一个Map 来保存AIDL回调。
当客户端解除注册的时候,只要遍历服务端所有的listener ,找到客户端相同的listener 具有相同的Binder对象,删掉即可。它还有其他功能,内部自动实现了线程同步,所以在注册和解除的时候不需要手动做任何同步工作。
对服务端做以下修改
RemoteCallbackList 替换 CopyOnWriteArrayList 、registerListener 和unregisterListener 回调 修改、onMsgArrived 方法修改
最终服务端代码如下

/**
 * Created by stf on 2020/4/2.
 * 服务端
 */

public class RemoteService extends Service {

    /***
     * CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,
     * 很常一段时间,这个容器数据、数量等没有发生变化的时候,
     * 大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,
     * 但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),
     * 再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,
     * 但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。
     *
     * 在java.util.concurrent.atomic包下,有AtomicBoolean , AtomicInteger, AtomicLong, AtomicReference等类,
     * 它们的基本特性就是在多线程环境下,执行这些类实例包含的方法时,具有排他性,
     * 即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,
     * 而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入。
     */
    private AtomicBoolean mIsServiceDestory = new AtomicBoolean(false); // 控制线程
    private CopyOnWriteArrayList<HelloMsg> mHelloList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList<IOnNewMsgArrivedListener> msgArrivedListeners = new RemoteCallbackList<>();

    IRemoteService.Stub stub = new IRemoteService.Stub() {
        @Override
        public HelloMsg sayHello() throws RemoteException {
            return new HelloMsg("来自服务端的消息了是1111", 1);
        }

        @Override
        public List<HelloMsg> getMsgList() throws RemoteException {
            Log.i("stf", "语言总共" + mHelloList.size() + "个");
            for (int i = 0; i < mHelloList.size(); i++) {
                HelloMsg helloMsg = mHelloList.get(i);
                Log.i("stf", "服务端语言详情--->" + helloMsg.getMsg() + "--->" + helloMsg.getPid());
            }
            return mHelloList;
        }

        @Override
        public void addMsg(HelloMsg msg) throws RemoteException {
            Log.i("stf", "服务端收到了增加的语言" + msg.getMsg() + "--->" + msg.getPid());
            mHelloList.add(msg);
        }

        @Override
        public void registerListener(IOnNewMsgArrivedListener listener) throws RemoteException {
            msgArrivedListeners.register(listener);
              // 下面两行仅仅是为了统计注册事件个数写的,后期可以删除掉
            Log.i("stf","-registerListener size-->"+msgArrivedListeners.beginBroadcast());
            msgArrivedListeners.finishBroadcast();
        }

        @Override
        public void unregisterListener(IOnNewMsgArrivedListener listener) throws RemoteException {
            msgArrivedListeners.unregister(listener);
            // 下面两行仅仅是为了统计注册事件个数写的,后期可以删除掉
            Log.i("stf","-unregisterListener size-->"+msgArrivedListeners.beginBroadcast());
            msgArrivedListeners.finishBroadcast();
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestory.set(true);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //开启子线程 创建新留言
        new Thread(new ServiceWorker()).start();
    }


    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestory.get()) {
                try {
                    Thread.sleep(1000);

                    int helloId = mHelloList.size() + 1;
                    HelloMsg helloMsg = new HelloMsg("我来了,大哥" + helloId, helloId);
                    if (helloId == 10) {
                        mIsServiceDestory.set(true);
                    }
                    Log.i("stf", "服务端有新留言了:" + helloMsg.getMsg());
                    onMsgArrived(helloMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //通知用户 新留言来了
    private void onMsgArrived(HelloMsg helloMsg) {
        mHelloList.add(helloMsg); //收集所有留言
        
        final int i = msgArrivedListeners.beginBroadcast(); // 和finishBroadcast 配对使用
        for (int j = 0; j < i; j++) {
            IOnNewMsgArrivedListener broadcastItem = msgArrivedListeners.getBroadcastItem(j);
            if (broadcastItem != null) {
                try {
                    broadcastItem.OnNewMsgArrived(helloMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        msgArrivedListeners.finishBroadcast();
    }
}
  • 验证结果 Main6Activity 10条留言执行完之后 ,按Back 键 ,log如下


    image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容