当前位置: 首页 > news >正文

Android 搭建AIDL Client和Server端,双向通信

一、背景

使用AIDL,搭建Client和Server端,实现跨进程通讯,即两个应用之间可以相互通讯。这里列举AIDL实现的方式和需注意的细节,并附上源码。

二、实现方式

2.1 定义AIDL需要的接口,名字为xxx.aidl,Client和Server端 AIDL接口的包名和aidl文件必须一致,在main 目录下,新建aidl目录,如下截图:

Server 端:

Client端:

2.2 IMessageManager.aidl文件
import com.vc.aidlservice.IOnServerCallback;interface IMessageManager {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);//客户端发送消息给服务端void sendMessageToServer(String message);//服务端发送消息给客户端void sendMessageToClient(String message);//注册监听,用于服务端主动发送消息给客户端void registerListener(IOnServerCallback callback);//注销监听void unRegisterListener();
}
2.3 IOnServerCallback.aidl 文件
package com.vc.aidlservice;// Declare any non-default types here with import statementsinterface IOnServerCallback {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/
//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
//            double aDouble, String aString);//用于客户端接收服务端发送的消息void onMessageReceived(String message);

三、Server端代码

3.1 VcService (AIDL服务端具体逻辑实现)
package com.vc.aidlserver.serviceimport android.app.Service
import android.content.Intent
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.util.Log
import android.widget.Toast
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.vc.aidlclient.bean.MessageInfo
import com.vc.aidlclient.enums.MessageType
import com.vc.aidlserver.MyApplication
import com.vc.aidlservice.IMessageManager
import com.vc.aidlservice.IOnServerCallback/*** @Time : On 2024/12/6 16:00* @Description : VcService*/
class VcService :Service(){private val TAG="AIDL_Server_"private val SEND_MSG_FAIL=0private val SEND_MSG_SUCCESS=1private var mCallback:IOnServerCallback?=nullprivate val mHanler=object:Handler(Looper.getMainLooper()){override fun handleMessage(msg: Message) {val msgValue=msg.whatif(msgValue==SEND_MSG_FAIL){Toast.makeText(MyApplication.instance,"连接未建立,发送消息失败",Toast.LENGTH_SHORT).show()}else{Toast.makeText(MyApplication.instance,"发送消息成功",Toast.LENGTH_SHORT).show()}}}override fun onBind(intent: Intent): IBinder {return binder}private val binder= object : IMessageManager.Stub() {override fun basicTypes(anInt: Int,aLong: Long,aBoolean: Boolean,aFloat: Float,aDouble: Double,aString: String?) {}/*** 接收客户端发送的消息*/override fun sendMessageToServer(message: String) {Log.d("$TAG", "VcService 服务端收到客户端消息:$message")try {val jsonObject = JSON.parseObject(message)val  requestId = jsonObject.getString("requestId")val  type = jsonObject.getString("type")val contentObject = jsonObject.getJSONObject("content")val message = contentObject.getString("message")val callbackMessage= MessageInfo.Message("应用:$packageName 收到透传的消息:$message,,,响应返回")val messageInfo=MessageInfo("callback:$requestId", MessageType.Server.toString(),callbackMessage)val jsonMessage= JSONObject.toJSONString(messageInfo)mCallback?.onMessageReceived(jsonMessage)}catch (e:Exception){Log.e("$TAG", "VcService 服务端解析客户端客户端消息异常 Exception:${e.message}")}}/*** 主动发送消息给客户端*/override fun sendMessageToClient(message: String?) {Log.d("$TAG", "VcService 服务端发送消息给客户端:$message,,,mCallback:$mCallback,,,是否主线程:${Looper.getMainLooper()==Looper.myLooper()}")if(mCallback==null){mHanler.sendEmptyMessage(SEND_MSG_FAIL)Log.d("$TAG", "VcService 连接未建立,sendMessageToClient fail,,,message:$message")return}mHanler.sendEmptyMessage(SEND_MSG_SUCCESS)mCallback?.onMessageReceived(message)}/*** 客户端调用注册监听*/override fun registerListener(callback: IOnServerCallback?) {mCallback=callback}/*** 客户端调用注销监听*/override fun unRegisterListener() {Log.d("$TAG", "VcService unRegisterListener")mCallback=null}}}
3.2 Server端主动发送消息给Client端
package com.vc.aidlserverimport android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.alibaba.fastjson.JSONObject
import com.vc.aidlclient.RequestIdCreatUtil
import com.vc.aidlclient.bean.MessageInfo
import com.vc.aidlclient.enums.MessageType
import com.vc.aidlserver.service.VcService
import com.vc.aidlservice.IMessageManagerclass MainActivity : AppCompatActivity() {private var bt_send: Button? = nullprivate var edt_message: EditText? = nullvar mIMessageManager: IMessageManager? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main_send_message)bt_send = findViewById(R.id.bt_send)edt_message = findViewById(R.id.edt_message)/***在VcService外,获取 mIMessageManager,服务端主动发送消息给AIDL客户端*/val intent = Intent(this, VcService::class.java)val result = bindService(intent, object : ServiceConnection {override fun onServiceConnected(name: ComponentName, service: IBinder) {Log.d("BBBBB", "onServiceConnected 连接服务端成功 name:$name")mIMessageManager = IMessageManager.Stub.asInterface(service) as IMessageManager}override fun onServiceDisconnected(name: ComponentName) {Log.d("BBBBB", "onServiceDisconnected 断开服务端连接 name:$name")}}, BIND_AUTO_CREATE)/*** 服务端主动发送消息给AIDL客户端*/bt_send?.setOnClickListener {Log.d("BBBBB", " mIMessageManager:$mIMessageManager")val message = edt_message?.text.toString().trim()if (message.isNullOrEmpty()) {Toast.makeText(this, "发送消息不能为空", Toast.LENGTH_SHORT).show()return@setOnClickListener}if (mIMessageManager == null) {Toast.makeText(this, "绑定VcService失败,应用发送消息失败", Toast.LENGTH_SHORT).show()} else {val sendMessage = MessageInfo.Message(message)val messageInfo = MessageInfo(RequestIdCreatUtil.creatId(),MessageType.Server.toString(),sendMessage)val jsonMessage = JSONObject.toJSONString(messageInfo)mIMessageManager!!.sendMessageToClient(jsonMessage)//Toast.makeText(this,"应用发送消息成功",Toast.LENGTH_SHORT).show()Log.d("BBBBB", "sendMessageToClient:$jsonMessage")}}}}
3.3  AndroidManifest.xml配置
     <service android:name=".service.VcService"android:enabled="true"android:exported="true"android:process=":remote"><intent-filter><action android:name="com.vc.aidlserver.service.VcService" /></intent-filter></service>

四、Client端代码

4.1 Client 逻辑代码
package com.vc.aidlclientimport android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.alibaba.fastjson.JSONObject
import com.vc.aidlclient.bean.MessageInfo
import com.vc.aidlclient.enums.MessageType
import com.vc.aidlservice.IMessageManager
import com.vc.aidlservice.IOnServerCallbackprivate var mIMessageManager: IMessageManager? = null
private var isConnect = falseclass MainActivity : AppCompatActivity(), View.OnClickListener {private var bt_connect: Button? = nullprivate var bt_send: Button? = nullprivate var bt_disconnect: Button? = nullprivate var isBound = falseprivate val TAG = "AIDL_Client_"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)bt_connect = findViewById(R.id.bt_connect)bt_send = findViewById(R.id.bt_send)bt_disconnect = findViewById(R.id.bt_disconnect)bt_connect?.setOnClickListener(this)bt_send?.setOnClickListener(this)bt_disconnect?.setOnClickListener(this)}/*** 连接服务端*/fun connectServer() {val intent = Intent()intent.setComponent(ComponentName("com.vc.aidlserver","com.vc.aidlserver.VcService"))isBound = bindService(intent, mServiceConnection, BIND_AUTO_CREATE)Log.d("$TAG", "bindService  isBound:$isBound")}private val serverListener = object : IOnServerCallback.Stub() {override fun onMessageReceived(message: String?) {Log.d("$TAG", "onMessageReceived :$message")}}/*** 销毁监听器*/private fun destroyService() {try {if (isBound) {isConnect = falsemIMessageManager?.unRegisterListener()mIMessageManager = nullunbindService(mServiceConnection)}} catch (e: Exception) {e.printStackTrace()Log.d("$TAG", "unbindService Exception:${e.message}")}}override fun onDestroy() {Log.d("$TAG", "onDestroy()")super.onDestroy()destroyService()}override fun onClick(v: View) {if (v.id == R.id.bt_connect) {if (isConnect) {Toast.makeText(this, "已连接服务端", Toast.LENGTH_SHORT).show()} else {connectServer()}} else if (v.id == R.id.bt_send) {if (mIMessageManager == null || !isConnect) {Toast.makeText(this, "连接未建立", Toast.LENGTH_SHORT).show()} else {val message = MessageInfo.Message("我是客户端")val messageInfo = MessageInfo(RequestIdCreatUtil.creatId(),MessageType.Client.toString(),message)val jsonMessage = JSONObject.toJSONString(messageInfo)Log.d("$TAG", "客户端发送消息给服务端 jsonMessage->>>:$jsonMessage")mIMessageManager?.sendMessageToServer(jsonMessage)}} else if (v.id == R.id.bt_disconnect) {destroyService()}}private val mServiceConnection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName, service: IBinder) {// 服务连接成功,你可以开始与服务交互Log.d("$TAG", "onServiceConnected 连接服务端成功 name:$name")isConnect = truemIMessageManager = IMessageManager.Stub.asInterface(service) as IMessageManagermIMessageManager?.registerListener(serverListener)}override fun onServiceDisconnected(name: ComponentName) {// 服务断开连接Log.d("$TAG", "onServiceDisconnected 断开服务端连接 name:$name")destroyService()}}
}
4.2 build.gradle配置

注:不配置无法生效aidl文件,Server端也一样,需要配置,有可能和AS版本有关,博主Android studio版本:Android Studio Koala Feature Drop | 2024.1.2

4.3 AndroidManifest.xml
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission"/><queries><package android:name="com.vc.aidlserver"/></queries>

Client需要配置以上,否则存在连接不上服务端现象

五、完整源码下载

源码下载连接:https://download.csdn.net/download/banzhuantuqiang/90159482

相关文章:

Android 搭建AIDL Client和Server端,双向通信

一、背景 使用AIDL,搭建Client和Server端,实现跨进程通讯,即两个应用之间可以相互通讯。这里列举AIDL实现的方式和需注意的细节&#xff0c;并附上源码。 二、实现方式 2.1 定义AIDL需要的接口,名字为xxx.aidl,Client和Server端 AIDL接口的包名和aidl文件必须一致&#xff0c…...

深度学习从入门到精通——图像分割实战DeeplabV3

DeeplabV3算法 参数配置关于数据集的配置训练集参数 数据预处理模块DataSet构建模块测试一下数据集去正则化模型加载模块DeepLABV3 参数配置 关于数据集的配置 parser argparse.ArgumentParser()# Datset Optionsparser.add_argument("--data_root", typestr, defa…...

STM32-笔记5-按键点灯(中断方法)

1、复制03-流水灯项目&#xff0c;重命名06-按键点灯&#xff08;中断法&#xff09; 在\Drivers\BSP目录下创建一个文件夹exti&#xff0c;在该文件夹下&#xff0c;创建两个文件exti.c和exti.h文件&#xff0c;并且把这两个文件加载到项目中&#xff0c;打开项目工程文件 加载…...

C++ 只出现一次的数字 - 力扣(LeetCode)

点击链接即可查看题目&#xff1a;136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间…...

C++设计模式:享元模式 (附文字处理系统中的字符对象案例)

什么是享元模式&#xff1f; 享元模式是一个非常实用的结构型设计模式&#xff0c;它的主要目的是节省内存&#xff0c;尤其在需要创建大量相似对象时。 通俗解释&#xff1a; 想象我们在写一本书&#xff0c;每个字母都需要表示出来。如果每个字母都单独用对象表示&#xff…...

android EditText密码自动填充适配

android上的密码&#xff08;其实不仅仅是密码&#xff0c;可以是用户名也可以是邮箱&#xff09;自动填充&#xff0c;是需要考虑适配的。 官方文档&#xff1a;https://developer.android.com/identity/autofill/autofill-optimize?hlzh-cn 什么是自动填充 手机厂商一般会…...

LeetCode 刷题笔记

LeetCode 刷题笔记 1. 20241218 &#xff08;1&#xff09;2447 std::gcd是C17引入的一个函数&#xff0c;用于计算两个整数的最大公因数。位于<numeric>头文件中。 #include <iostream> #include <numeric> // std::gcdint main() {int a 36;int b 60…...

【Java基础面试题034】Java泛型擦除是什么?

回答重点 泛型擦除指的是Java编译器在编译时将所有泛型信息删除的过程&#xff0c;以确保与Java1.4及之前的版本保持兼容 泛型参数在运行时会被替换为其上界&#xff08;通常是Object&#xff09;&#xff0c;这样一来在运行时无法获取泛型的实际类型。 作用&#xff1a;泛型…...

使用ssh命令远程登录服务器的两种便捷方式:简化ssh命令、创建bat文件

1. 简化ssh命令 使用记事本打开该路径C:\Users\<你的用户名>\.ssh\下的config文件&#xff0c;粘贴以下代码&#xff1a; Host myserverHostName 192.168.1.1(这里换成你的ip地址)User your_username(这里换成你的用户名)Port 22保存文件后现在在cmd中直接输入ssh myserv…...

access数据库代做/mysql代做/Sql server数据库代做辅导设计服务

针对Access数据库、MySQL以及SQL Server数据库的代做和辅导设计服务&#xff0c;以下是一些关键信息和建议&#xff1a; 一、服务概述 这些服务通常包括数据库的设计、创建、优化、维护以及相关的编程和查询编写等。无论是Access这样的桌面关系数据库管理系统&#xff08;RDB…...

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书-A 目录 一、竞赛阶段 二、竞赛任务书内容 &#xff08;一&#xff09;拓扑图 &#xff08;二&#xff09;模块A 基础设施设置与安全加固(200分) &#xff08;三&#xff09;B模块安全事件响应/网络安全数据取证/…...

Android学习(五)-Kotlin编程语言-面向对象中的 继承-构造函数-接口三模块学习

首先&#xff0c;我们需要定义一个 Person 类&#xff1a; open class Person {var name ""var age 0fun eat() {println("$name is eating.")} } 注意&#xff0c;Person 类前面加上了 open 关键字&#xff0c;表示这个类可以被继承。在 Kotlin 中&am…...

滑动窗口 + 算法复习

维护一个满足条件的窗口大小&#xff0c;然后进行双指针移动 1.最长子串 题目链接&#xff1a;1.最长子串 - 蓝桥云课 #include<bits/stdc.h> #define int long long using namespace std; string s; int k; signed main() {int max_len0,left0;cin>>s>>k;…...

贪心算法 greedy

文章目录 参考贪心算法[Leetcode455 分发饼干](https://leetcode.cn/problems/assign-cookies/description/)分析题解 [Leetcode135 分发糖果](https://leetcode.cn/problems/assign-cookies/description/)分析题解 leetcode435无重叠区间分析题解 参考 https://github.com/ch…...

基于python的家教预约网站-家教信息平台系统

标题:基于 Python 的家教预约网站-家教信息平台系统 内容:1.摘要 本文介绍了一个基于 Python 的家教预约网站-家教信息平台系统。该系统旨在为学生和家长提供一个方便、高效的家教预约平台&#xff0c;同时也为家教老师提供一个展示自己教学能力和经验的机会。本文详细介绍了系…...

基于深度学习多图像融合的屏幕缺陷检测方案

公司项目&#xff0c;已申请专利。 深度学习作为新兴技术在图像领域蓬勃发展&#xff0c;因其自主学习图像数据特征的性能避免了人工设计算法的繁琐&#xff0c;精准的检测性能、高效的检测效率以及对各种不同类型的图像任务都有比较好的泛化性能&#xff0c;使得深度学习技术在…...

MySQL基础笔记(三)

在此特别感谢尚硅谷-康师傅的MySQL精品教程 获取更好的阅读体验请前往我的博客主站! 如果本文对你的学习有帮助&#xff0c;请多多点赞、评论、收藏&#xff0c;你们的反馈是我更新最大的动力&#xff01; 创建和管理表 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数…...

【JetPack】WorkManager笔记

WorkManager简介&#xff1a; WorkManager 是 Android Jetpack 库中的一个重要组件。它用于处理那些需要在后台可靠执行的任务&#xff0c;这些任务可以是一次性的&#xff0c;也可以是周期性的&#xff0c;甚至是需要满足特定条件才执行的任务。例如&#xff0c;它可以用于在后…...

docker 安装 ftp

前言 经多次测试 不知道为什么 必须添加被动模式跟端口才可以 连接成功&#xff0c;有知道为什么可以评论下 下载镜像 docker pull fauria/vsftpd启动ftp 服务 参考链接 docker run -d -v /etc/localtime:/etc/localtime:ro -v /home/dr/data/ftp:/home/vsftpd \ -e "…...

5.C语言内存分区-堆-栈

目录 内存分区 运行之前 代码区 全局初始化数据区 、静态数据区 (data) 未初始化数据区(bss&#xff08;Block Started by Symbol&#xff09;区) 总结 运行之后 代码区 &#xff08;text segment&#xff09; 未初始化数据区(bss) 全局初始化数据区&#xff0c;静态…...

传统CV算法——基于opencv的答题卡识别判卷系统

基于OpenCV的答题卡识别系统&#xff0c;其主要功能是自动读取并评分答题卡上的选择题答案。系统通过图像处理和计算机视觉技术&#xff0c;自动化地完成了从读取图像到输出成绩的整个流程。下面是该系统的主要步骤和实现细节的概述&#xff1a; 1. 导入必要的库 系统首先导入…...

国产 HighGo 数据库企业版安装与配置指南

国产 HighGo 数据库企业版安装与配置指南 1. 下载安装包 访问 HighGo 官方网站&#xff08;https://www.highgo.com/&#xff09;&#xff0c;选择并下载企业版安装包。 2. 上传安装包到服务器 将下载的安装包上传至服务器&#xff0c;并执行以下命令&#xff1a; [rootmas…...

「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件

本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息&#xff0c;页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。 关键词 UI互动应用天气预报数据绑定动态展示状态管理 一、功能说明 自定义…...

Springboot @Transactional使用时需注意的几个问题

一、事务的隔离级别 在Springboot应用中&#xff0c;如果我们想实现方法一旦执行有异常产生&#xff0c;就触发事务回滚&#xff0c;可以在方法上面添加Transactional注解。如果应用采用mysql数据库&#xff0c;虽然mysql本身也有事务隔离机制&#xff0c;但在Sping数据库的应…...

数字经济下的 AR 眼镜

目录 1. &#x1f4c2; AR 眼镜发展历史 1.1 AR 眼镜相关概念 1.2 市面主流 XR 眼镜 1.3 AR 眼镜大事记 1.4 国内外 XR 眼镜 1.5 国内 AR 眼镜四小龙 2. &#x1f531; 关键技术 2.1 AR 眼镜近眼显示原理 2.2 AR 眼镜关键技术 2.3 AR 眼镜技术难点 3. &#x1f4a…...

力扣150题

88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 **注意&#xff1a;**…...

剑指offer搜索二维矩阵

题目连接 https://leetcode.cn/problems/search-a-2d-matrix-ii/’ 代码 自己想出来的 解法一 初始化两个指针&#xff0c;i0,j列数-1 若此时matrix[i][j]target 则返回true 若此时matrix[i][j]>target,表明在第j列中不可能存在target&#xff0c;因为列是升序的 若此时ma…...

如何设置浏览器不缓存网页

设置浏览器不缓存网页可以通过多种方法实现&#xff0c;以下是一些常见的策略&#xff1a; HTTP响应头控制&#xff1a; Cache-Control&#xff1a;这是最常用的HTTP头之一&#xff0c;用于控制响应的缓存行为。例如&#xff1a; Cache-Control: no-cache, no-store, must-r…...

Iris简单实现Go web服务器

package mainimport ("github.com/kataras/iris" )func main() {app : iris.New() // 实例一个iris对象//配置路由app.Get("/", func(ctx iris.Context) {ctx.WriteString("Hello Iris")})app.Get("/aa", func(ctx iris.Context) {ct…...

后端项目java中字符串、集合、日期时间常用方法

我这里只介绍了项目中最常用的哈,比如像集合有很多,但我们最常用的就是ArrayList。 然后我这里会以javascript中的字符串、数组的方法为基准来实现,有些方法js和java会有些区别也会介绍 字符串 每次修改 String 对象都会创建一个新的对象,而 StringBuffer 可以在同一个对象…...