时隔一年半了,终于写下了这个续篇,我发现我的很多博客有头无尾,都是有前面一点点,后面就没写去了,也正在想办法都补上

  • 初涉IPC,了解AIDL的工作原理及使用方法

今天聊聊的是客户端和服务端的相互通信,何谓双向通信,事实上,我们在上一篇的博客中,只是讲解了客户端请求服务端的方法,然后服务端返回一个值给我们这样,其实是最简单的用法,但是常常在我们的开发过程中,如果调用了某些方法,比如网络请求,那么就需要等待请求有结果了之后再回调给我们,这个回调的过程就是服务端向客户端通信,作为ipc通信的一种,如果你不会双向通信,那么你可以比较low的用广播,但是我还是建议你直接用一整套的AIDL复用,好的,那么问题来了,我们怎么下手呢?

服务端

我们新建两个工程,一个叫ADILClient,一个叫AIDLService,分别代表的是客户端和服务端

我们现在开始编写我们的aidl,这里我需要编写两个AIDL文件,一个是我们对外的方法,一个是我们对外的回调方法,如图

这里我做一下讲解,首先我new了一个aidl的文件夹,在main下,和java同级,然后定义了一个公共的包名:com.android.openimpl,最后在里面实现了两个aidl文件,我们来看下具体的文件内容

IMyLifeStyleInterface

// IMyLifeStyleInterface.aidl
package com.android.openimpl;import com.android.openimpl.IMyLifeStyleListener;interface IMyLifeStyleInterface {//计算void sum(int a ,int b);//睡觉void sleep(boolean isSleep);//注册void registerCallback(IMyLifeStyleListener il);//解绑void unregisterCallback(IMyLifeStyleListener il);}

IMyLifeStyleListener

// IMyLifeStyleListener.aidl
package com.android.openimpl;//回调接口
interface IMyLifeStyleListener {//回调方法void OnCallBackSleep(String text);void OnCallBackSize(int size);
}

这里我定义了IMyLifeStyleInterface ,里面有两个方法,假设我定义的事一个人,他有基本的两个本能,一个是计算,我传两个数字给他,他进行一系列的处理,那么,问题来, 我不想用返回值,我想通过回调知道,这是一点,另一个是睡觉,而在IMyLifeStyleListener,我也定义了两个对应的回调OnCallBackSleep和OnCallBackSize,好了,现在开始来实现我们的远程Service服务

public class OpenImplService extends Service {private IMyLifeStyleListener lifeStyleListener;private IBinder mBinder = new IMyLifeStyleInterface.Stub() {@Overridepublic void sum(int a, int b) throws RemoteException {//经过一系列的计算后将值告知客户端int c = a * 2 + b;if (lifeStyleListener != null) {lifeStyleListener.OnCallBackSize(c);}}@Overridepublic void sleep(boolean isSleep) throws RemoteException {//告诉客户端我已经睡着if(isSleep){if (lifeStyleListener != null) {lifeStyleListener.OnCallBackSleep("帮我关下灯,谢谢!");}}}@Overridepublic void registerCallback(IMyLifeStyleListener il) throws RemoteException {if (il != null) {lifeStyleListener = il;}}@Overridepublic void unregisterCallback(IMyLifeStyleListener il) throws RemoteException {if (il != null) {lifeStyleListener = null;}}};@Overridepublic IBinder onBind(Intent intent) {return mBinder;}
}

这里,我定义了一个远程的服务OpenImplService,里面我只是new了IMyLifeStyleInterface.Stub并且把Binder对象给了onBind方法,而在Binder内部,我做的操作相信大家都看的明白吧,很简单,Ok,那我们的服务端就已经搞定了,我们来看下服务端的整体结构

当然,别忘了在清单文件中注册

       <service
            android:name=".service.OpenImplService"android:enabled="true"android:exported="true" />

客户端

好的,现在就开始来实现我们的客户端,客户端也需要同样的AIDL文件,所以我可以直接复制过去,但是要注意的是包名一定要相同,如图

这里,我的客户端就是app包下的东西,那么我们来实现UI上的逻辑

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="10dp"><EditText
        android:id="@+id/et_numer_1"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入a" /><EditText
        android:id="@+id/et_numer_2"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入b" /><Button
        android:id="@+id/btn_sum"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="计算"android:textAllCaps="false" /><Button
        android:id="@+id/btn_sleep"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="睡觉"android:textAllCaps="false" /></LinearLayout>

这里,我定义了两个输入框和有个按钮,对应的计算和睡觉,好的,我们run一下

我们来开始实现具体的逻辑了,这也是我们客户端经常要干的事情首先,我们initService来初始化了服务,在android5.0之后绑定远程服务都需要完整的包名了,如下

    private void initService() {Intent i = new Intent();//Android 5.0 之后需要直接定义包名i.setComponent(new ComponentName("com.liuguilin.aidlservice", "com.liuguilin.aidlservice.service.OpenImplService"));bindService(i, mConnection, Context.BIND_AUTO_CREATE);}

这里需要传一个mConnection,这是一个对应的关系,如果现在bind,你销毁的时候需要unBind

    @Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConnection);}

好的,我们具体来看下mConnection的内容吧

    private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {Log.i(TAG, "onServiceConnected");//获取AIDL对象iMyLifeStyleInterface = IMyLifeStyleInterface.Stub.asInterface(iBinder);//注册接口try {iMyLifeStyleInterface.registerCallback(new OpenImpleListener());} catch (RemoteException e) {e.printStackTrace();}isBind = true;}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.i(TAG, "onServiceDisconnected");try {iMyLifeStyleInterface.unregisterCallback(new OpenImpleListener());} catch (RemoteException e) {e.printStackTrace();}iMyLifeStyleInterface = null;isBind = false;}};

他一共会重写两个方法,服务绑定onServiceConnected和服务解绑onServiceDisconnected,所以这里我们用了一个标志位isBind来标记绑定状态,然后通过AIDL的asInterface方法来实例化对应的AIDL对象,然后就是注册这个接口回调了

    class OpenImpleListener extends IMyLifeStyleListener.Stub {@Overridepublic void OnCallBackSleep(String text) throws RemoteException {Toast.makeText(MainActivity.this, "text:" + text, Toast.LENGTH_SHORT).show();}@Overridepublic void OnCallBackSize(int size) throws RemoteException {Toast.makeText(MainActivity.this, "size:" + size, Toast.LENGTH_SHORT).show();}}

这里要注意的事需要继承的是AIDL的Stub,最后就是我们的点击事件了

    @Overridepublic void onClick(View v) {if (isBind) {switch (v.getId()) {case R.id.btn_sum:String a = et_numer_1.getText().toString().trim();String b = et_numer_2.getText().toString().trim();try {iMyLifeStyleInterface.sum(Integer.parseInt(a),Integer.parseInt(b));} catch (RemoteException e) {e.printStackTrace();}break;case R.id.btn_sleep:try {iMyLifeStyleInterface.sleep(true);} catch (RemoteException e) {e.printStackTrace();}break;}} else {Toast.makeText(this, "服务未连接", Toast.LENGTH_SHORT).show();}}

这里算是比较简单的了,传值调用即可,好的,下面贴上全部的代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final String TAG = "AIDL";private EditText et_numer_1;private EditText et_numer_2;private Button btn_sum;private Button btn_sleep;//绑定状态private static boolean isBind = false;private IMyLifeStyleInterface iMyLifeStyleInterface;private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {Log.i(TAG, "onServiceConnected");//获取AIDL对象iMyLifeStyleInterface = IMyLifeStyleInterface.Stub.asInterface(iBinder);//注册接口try {iMyLifeStyleInterface.registerCallback(new OpenImpleListener());} catch (RemoteException e) {e.printStackTrace();}isBind = true;}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.i(TAG, "onServiceDisconnected");try {iMyLifeStyleInterface.unregisterCallback(new OpenImpleListener());} catch (RemoteException e) {e.printStackTrace();}iMyLifeStyleInterface = null;isBind = false;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initService();initView();}private void initService() {Intent i = new Intent();//Android 5.0 之后需要直接定义包名i.setComponent(new ComponentName("com.liuguilin.aidlservice", "com.liuguilin.aidlservice.service.OpenImplService"));bindService(i, mConnection, Context.BIND_AUTO_CREATE);}private void initView() {et_numer_1 = (EditText) findViewById(R.id.et_numer_1);et_numer_2 = (EditText) findViewById(R.id.et_numer_2);btn_sum = (Button) findViewById(R.id.btn_sum);btn_sleep = (Button) findViewById(R.id.btn_sleep);btn_sum.setOnClickListener(this);btn_sleep.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (isBind) {switch (v.getId()) {case R.id.btn_sum:String a = et_numer_1.getText().toString().trim();String b = et_numer_2.getText().toString().trim();try {iMyLifeStyleInterface.sum(Integer.parseInt(a),Integer.parseInt(b));} catch (RemoteException e) {e.printStackTrace();}break;case R.id.btn_sleep:try {iMyLifeStyleInterface.sleep(true);} catch (RemoteException e) {e.printStackTrace();}break;}} else {Toast.makeText(this, "服务未连接", Toast.LENGTH_SHORT).show();}}class OpenImpleListener extends IMyLifeStyleListener.Stub {@Overridepublic void OnCallBackSleep(String text) throws RemoteException {Toast.makeText(MainActivity.this, "text:" + text, Toast.LENGTH_SHORT).show();}@Overridepublic void OnCallBackSize(int size) throws RemoteException {Toast.makeText(MainActivity.this, "size:" + size, Toast.LENGTH_SHORT).show();}}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConnection);}
}

可以发现,代码很是简洁,希望大家能够学会,最后我们运行一遍

这里可以看到,点击睡觉,服务端会叫我们关下灯,而计算的话,我传的是2和3,服务端计算了一下 2 * 2 + 3 = 7 并且返回回来了。

Demo下载:点击下载

我的公众号,期待你的关注

AIDL 客户端与服务端的双向通信相关推荐

  1. 客户端与服务端长连接的几种方式

    客户端与服务端长连接的几种方式 前言 一.ajax 轮询 二.long poll 长轮询 三.iframe 长连接 四.WebSocket 前言 在日常 Web 项目中,通常使用的是短连接.即一个 R ...

  2. Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信

    ## Netty实战 IM即时通讯系统(六)实战: 客户端和服务端双向通信 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 实战: 客户端和服务端双向通信 数据传输载 ...

  3. android studio中使用AIDL进行客户端与服务端互相通信

    前言 在AIDL实现IPC通信,调用远程服务端的方法.但是,远程服务端并不能主动给客户端返回信息.在很多情况下是需要远程服务端主动给客户端返回数据,客户端只需要进行监听即可,这是典型的观察者模式.这篇 ...

  4. 使用Netty实现客户端和服务端之间的双向通信

    欢迎阅读本篇文章 提示:本文只是提供部分核心代码,源码详见代码示例 使用Netty实现客户端和服务端之间的双向通信 前言 一.服务端 二.客户端 前言 在上个月的开发计划中,有一个系统控制喇叭播放的功 ...

  5. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

  6. Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline

    Netty实战 IM即时通讯系统(十二)构建客户端与服务端pipeline 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向 ...

  7. Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息

    Netty实战 IM即时通讯系统(十)实现客户端和服务端收发消息 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数据 ...

  8. 浅议C#客户端和服务端通信的几种方法:Rest和GRPC和其他

    本文来自:https://michaelscodingspot.com/rest-vs-grpc-for-asp-net/ 浅议C#客户端和服务端通信的几种方法:Rest和GRPC 在C#客户端和C# ...

  9. Java中Socket实现客户端和服务端通信(多线程实现全双工通信)

    效果图 目录结构 服务端Server package server;import thread.SocketReader; import thread.SocketWrite;import java. ...

最新文章

  1. Scala协变与Java泛型
  2. micropythonesp8266的温度湿度_用ESP8266上传温湿度给ONENET(不需要arduino)
  3. 针孔摄像头屡禁不止?用你手机自带的ToF传感器对付它,检测成功率近90%
  4. 前端(慕课网)笔记一
  5. RocketMQ(一):Linux安装RocketMQ和常用命令
  6. 信息系统项目管理师:第1章:信息化与信息系统-重点汇总
  7. 强化学习总结(1-2)——model-base(policy evaluation;policy control)
  8. android.os.binderproxy cannot be cast to,Android服务android.os.BinderProxy错误
  9. 数据库知识:SQLServer变量相关知识介绍
  10. 自己写的.Net(C#)代码×××
  11. textarea还剩余字数统计
  12. nginx 根据目录指定root_CentOS(7.6)基本操作与Nginx配置
  13. Xcode因为证书问题经常报的那些错
  14. html5实现拖拽上传图片,JS HTML5拖拽上传图片预览
  15. 【ZZULIOJ】郑州轻工业大学oj题解整合
  16. 夜神模拟器连接手柄无反应_夜神模拟器怎么连接手柄?夜神模拟器连接手柄具体操作...
  17. 穷举法破解密码-方法详解
  18. 智衡跨境电商:跨境电商运营和国内电商运营差别在哪?
  19. VC6.0补丁Vs6sp6安装方法
  20. 《视觉锤》 读书笔记

热门文章

  1. linux支持raid5阵列,linux下raid5阵列
  2. linux更改用户的shell,Linux下通过shell更改用户密码
  3. usb过滤驱动inf_N卡驱动全家桶专治工具升级:专为玩家打造
  4. python二分法递归_python 【递归 及 二分法】
  5. 详解@EnableEurekaServer和@EnableDiscoveryClient 或 @EnableEurekaClient注解
  6. dpi重启后会恢复_Linux 系统的备份恢复
  7. 计算机系统存数及取数方式,计算机系统结构总结2
  8. centos7镜像加速_使用阿里云容器镜像服务托管私有Docker镜像
  9. 评论与回复php代码,php – 显示评论和回复?
  10. vue项目中使用echarts实现词云