美文网首页
Android AIDL的实现

Android AIDL的实现

作者: 火星局 | 来源:发表于2020-04-03 15:52 被阅读0次

    简介

    • 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

    相关文章

      网友评论

          本文标题:Android AIDL的实现

          本文链接:https://www.haomeiwen.com/subject/fxdhphtx.html