【Android】使用Binder(AIDL)实现利用自定义Bean和使用接口进行进程间通信(二)

项目前置

这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章,感兴趣的可以看一下: Binder(一)Binder的介绍和AIDL使用Binder的实例

项目目标

在两个APP之间进行数据传递,使用Android推荐的Binder通讯,而AIDL是Android推出的用来简化Binder写法的工具。

如果只是简短的使用java基本数据类型的话,只需要根据上面的那一篇文章对着来就可以了,这次我们需要使用我们自定义的数据bean来进行数据传递,而且是在实际项目中使用,就需要进行一些处理了。

项目需求

在这里为了区分,将接收数据的项目作为客户端,将发送数据的服务app作为服务端。服务端要获取外部数据,然后将这个数据通过Binder发送到客户端,客户端接收数据。

项目实现
服务端

首先我们要创建一个新的项目,然后在新的项目里面创建AIDL

创建AIDL
aidl文件夹和java文件夹同级,都在main文件夹下面

在这里插入图片描述
创建完之后,会得到一个这样的文件
在这里插入图片描述
然后再java的同名,同级别文件下面创建一个数据bean文件

在这里插入图片描述
数据bean代码如下

public class CpServiceMsg implements Parcelable {
    public int channel;
    public int id;
    public String data;

    public CpServiceMsg(int channel, int id, String data) {
        this.channel = channel;
        this.id = id;
        this.data = data;
    }

    public int getChannel() {
        return channel;
    }

    public void setChannel(int channel) {
        this.channel = channel;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    protected CpServiceMsg(Parcel in) {
        channel = in.readInt();
        id = in.readInt();
        data = in.readString();
    }

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

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

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

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(channel);
        dest.writeInt(id);
        dest.writeString(data);
    }

    @Override
    public String toString() {
        return "CpServiceMsg{" +
                "channel=" + channel +
                ", id=" + id +
                ", data='" + data + '\'' +
                '}';
    }
}

注意:
1.这个数据bean要实现【Parcelable】接口
2.尽量不要用byte[]类型,因为如果是高版本和低版本软件进行通信的时候,低版本的没有读取byte[]数据的快捷方法。

接下来在aidl的相同的目录下创建这个
首先创建文件夹bean
在这里插入图片描述
然后创建文件【CpServiceMsg.aidl】
在这里插入图片描述
这两个文件是同名文件,而且他们的文件目录也是同样的,只是一个在aidl里面,一个在java文件夹里面而已,这个需要注意一下。

然后在【CpServiceMsg.aidl】文件里面添加
在这里插入图片描述
然后在这个【ICPService.aidl】文件里面添加方法
在这里插入图片描述
这样的话我们AIDL就写好了,然后需要【Build】一下项目。

这样Android Studio会自动帮我们生成相关代码,可以在这个地方查看
在这里插入图片描述
接下来我们需要使用这个AIDL

首先我创建一个服务类

在这里插入图片描述

然后为了方便可以在这个服务类里面创建类继承系统生成的aidl

在这里插入图片描述
然后在MyForegroundService类里面
在这里插入图片描述
然后
在这里插入图片描述
这样当客户端连接到这个服务端的时候,这个onBind会调用,返回这个myBinderCpService

然后客户端调用这个getCanMsg方法的时候,就会获得这个
在这里插入图片描述
还有,一定要记得在【AndroidManifest.xml】注册清单文件里面对这个服务类进行注册
在这里插入图片描述

客户端

客户端这边也要创建一个aidl文件,但是客户端的aidl文件不要自己写,要直接从服务端直接复制过来,不要改什么东西。
在这里插入图片描述
然后在这个java文件夹下面创建同样的目录结构,当用在项目里面的时候,这个是肯定要做的,因为每个项目都有自己的目录结构,这就需要自己创建这个相关的目录结构,不然到时候aidl找不到文件

在这里插入图片描述
这个文件直接从服务端直接复制就好了。

然后需要进行【Build】一下,这样系统会创建项目的文件类。

在需要使用的地方,比如在一个【Activity】里面

在这里插入图片描述
然后创建一个【ServiceConnection】

在这里插入图片描述
然后就可以进行服务的绑定了
在这里插入图片描述
接下里就是使用了
在这里插入图片描述
然后就饿没什么了,启动服务端,启动客户端,进行数据通信了。

更新:怎么在跨进程里面使用接口回调方式进行数据通信

原因:有时候数据交互是非常频繁的,比如在服务端有个数据一直在不断刷新,每次刷新之后都要及时给客户端数据返回,这个时候使用接口是一个比较舒服的操作。

首先定义一个接口【ICPServiceListerner】

package com.cp.jyservice;
import com.cp.jyservice.bean.CpServiceMsg;

interface ICPServiceListerner {

   void serviceBack(int id,int channel,String result);

   void serviceMsg(in CpServiceMsg cpMsg);

   boolean serviceLogOpen();
}

位置在
在这里插入图片描述
然后修改 ICPService 代码

package com.cp.jyservice;
import com.cp.jyservice.bean.CpServiceMsg;
import com.cp.jyservice.ICPServiceListerner;

interface ICPService {

     CpServiceMsg getCanMsg();
     void setCanMsg(in CpServiceMsg canMsg);

     oneway void registerListener(ICPServiceListerner listener);

     oneway void unregisterListener(ICPServiceListerner listener);
}

然后开始【Build】,这样Android studio自动生成代码之后,在服务端使用

在监听的时候,可以根据客户端的数量使用以下两种方式

    /**
     * 监听集合,适合多个客户端绑定这个服务端的时候
     */
    private RemoteCallbackList<ICPServiceListerner> mCallBackList = new RemoteCallbackList<>();
    /**
    * 适合单个客户端的时候
    */
    private ICPServiceListerner mCallBack;

    CpServiceMsg cpServiceMsg = null;
    
    private final IBinder myBinderCpService = new MyCPService();
    

创建这个【MyCPService】

    class MyCPService extends ICPService.Stub {

        @Override
        public CpServiceMsg getCanMsg() throws RemoteException {
            Log.e(TAG, "给客户端传数据");
            if (cpServiceMsg != null) {
                Log.e(TAG, "数据:" + cpServiceMsg);
            }
            return cpServiceMsg;
        }

        @Override
        public void setCanMsg(CpServiceMsg canMsg) throws RemoteException {

        }

        @Override
        public void registerListener(ICPServiceListerner listener) throws RemoteException {
            mCallBackList.register(listener);
//            mCallBack = listener;
        }

        @Override
        public void unregisterListener(ICPServiceListerner listener) throws RemoteException {
            mCallBackList.unregister(listener);
//            mCallBack = null;
        }
    }

这个别忘了

    public IBinder onBind(Intent intent) {
        Log.e(TAG, "服务端这边绑定Binder了");
        return myBinderCpService;
    }

然后写一个处理调用的方法

    private void serviceCast(int channel, int id, String result) {
//        if (mCallBack != null) {
//            try {
//                mCallBack.serviceBack(id, channel, result);
//            } catch (RemoteException e) {
//                Log.e(TAG, "ICPServiceListener异常:" + e.getMessage());
//                e.printStackTrace();
//            }
//        }
        /*
         * 其实RemoteCallbackList类似于java中{@link java.util.Observable},用来批量处理接口回调对象,
         * 其实如果确保只有一个客户端会bind到这个服务,只需要保存一个IMyAidlInterfaceCallback即可。
         * 但是如果有多个,强烈推荐使用其实RemoteCallbackList
         */
        int callBackSize = mCallBackList.beginBroadcast();
        if (callBackSize == 0) {
            return;
        }
        for (int i = 0; i < callBackSize; i++) {
            try {
                mCallBackList.getBroadcastItem(i).serviceBack(id, channel, result);
            } catch (Exception e) {
                Log.e(TAG, "RemoteCallbackList异常:" + e.getMessage());
                e.printStackTrace();
            }
        }
        /*
          回调完成之后一定要确保调用finishBroadcast,不然在下一次执行beginBroadcast会抛出
          IllegalStateException异常。这类似与{@link Observable#setChanged()}
          和{@link Observable#clearChanged()},但是RemoteCallbackList设计的更加谨慎,
          为了确保一次Broadcast仅正对当前的状态或者数据变化。
         */
        mCallBackList.finishBroadcast();
    }

这样当数据变化的时候,调用【serviceCast】方法,传入对应的数据。

同样在客户端的,也要保持这样的结构

在这里插入图片描述

然后进行【Build】之后

    /**
     * 绑定的服务
     */
    private ICPService mCPService;
    /**
     * 服务提供的接口
     */
    private ICPServiceListerner mCpRemoteCallback;
        Intent cpIntent = new Intent();
        cpIntent.setAction("com.cp.jyservice");
        cpIntent.setPackage("com.cp.jyservice");
        bindService(cpIntent, mServiceConnection, Context.BIND_AUTO_CREATE);

        initCpCallBack();
    private void initCpCallBack() {
        mCpRemoteCallback = new ICPServiceListerner.Stub() {
            @Override
            public void serviceBack(int id, int channel, String result) {
                try {
                    //处理数据
                } catch (Exception e) {
                    Log.e("TAG", "发送的数据异常:" + e.getMessage());
                    e.printStackTrace();
                }
            }

            @Override
            public void serviceMsg(CpServiceMsg cpMsg) throws RemoteException {

            }

            @Override
            public boolean serviceLogOpen() throws RemoteException {
                return false;
            }
        };
    }
    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
       
            Log.e("TAG", "已经连接到服务端了");

            mCPService = ICPService.Stub.asInterface(service);
            if (mCPService != null) {
                try {
                    mCPService.registerListener(mCpRemoteCallback);
                } catch (Exception e) {
                    Log.e("TAG", "mCPService注册失败了: " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
      
            Log.e("TAG", "服务端断开连接");
    
            if (mCPService != null) {
                try {
                    mCPService.unregisterListener(mCpRemoteCallback);
                } catch (Exception e) {
                    Log.e("TAG", "mCPService解除注册失败了: " + e.getMessage());
                    e.printStackTrace();
                }
            }
            mCPService = null;
        }
    };

最后不要忘了,在onDestroy里面注销

        if (mServiceConnection != null) {
            unbindService(mServiceConnection);
        }

这样就可以使用了,当服务端数据刷新之后,客户端也可以使用对应的方法接收到数据,然后进行处理。

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值