如何以JNI方式实现安卓APP控制GPIO?
本文档提供了在 Android 10 设备上通过应用程序(App)控制通用输入输出(GPIO)的详细指南。这涵盖了从创建 gpio驱动到App 配置 以及 SELinux 策略以允许特定访问的所有必要步骤。
1. 驱动实现
添加创建gpio控制驱动bsp\kernel\kernel4.14\drivers\gpio\gpio_led.c,并添加好对应的Makfile编译
#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/ioctl.h>#include <linux/uaccess.h>#include <linux/string.h>#include <linux/wait.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/of.h>#include <linux/of_gpio.h>#include <linux/gpio.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/err.h>#include <linux/gpio/consumer.h>#include <linux/io.h>#include <linux/miscdevice.h>#include <linux/irq.h>#include <linux/of_irq.h>#include <linux/kernel.h>#include <linux/dmi.h>#include <linux/firmware.h>#include <linux/gpio/consumer.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/acpi.h>#include <linux/of.h>#include <asm/unaligned.h>#define GPIO_HIGH _IO('L', 0)#define GPIO_LOW _IO('L', 1)#define LED_ON 1#define LED_OFF 0#define SIMPIE_LED_MAX 4//============================== Upper interface value ==============================//// 驱动模块名称定义#define MODULE_NAME "gpio_led" // 驱动模块的名字#define MISC_NAME "gpio_led_device" // 用于注册为“misc”设备的名字// 模块函数接口定义,供上层应用调用的接口。通过MM_DEV_MAGIC区分不同系统接口,通过_IO()加上自己的编号作为接口number。#define MM_DEV_MAGIC 'N'// LED 控制命令#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)#define RFID_LED _IO(MM_DEV_MAGIC, 138)static int major;static struct class *cls;// GPIO 描述数组struct gpio_desc *led_gpio[SIMPIE_LED_MAX];// cat命令将调用该函数static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf){return sprintf(buf, "%d\n", gpiod_get_value(led_gpio[0]));}// echo命令将调用该函数static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){pr_err("[vanxoak]%c\n", buf[0]);if ('0' == buf[0]){gpiod_direction_output(led_gpio[0], 0);pr_err("[vanxoak]: _%s_ :gpio off\n", __func__);}else if ('1' == buf[0]){gpiod_direction_output(led_gpio[0], 1);pr_err("[vanxoak]: _%s_ :gpio on\n", __func__);}elsepr_err("I only support 0 or 1 to ctrl gpio on or off\n");pr_err("[vanxoak]gpio_value_store\n");return len;}// 定义一个名为gpio_led的设备属性static DEVICE_ATTR(gpio_led, 0664, gpio_value_show, gpio_value_store);// 提供给上层控制的接口long gpio_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){switch (cmd){case RFID_LED:gpiod_direction_output(led_gpio[0], arg);break;case RFID_IO1:gpiod_direction_output(led_gpio[1], arg);break;case RFID_IO2:gpiod_direction_output(led_gpio[2], arg);break;case RFID_IO3:gpiod_direction_output(led_gpio[3], arg);break;default:pr_err("[vanxoak] %s default: break\n", __func__);break;}return 0;}struct file_operations gpio_led_ops = {.owner = THIS_MODULE,.unlocked_ioctl = gpio_led_ioctl,};// LED灯初始化static int simpie_led_init(struct platform_device *pdev){int ret = 0;int i;// 申请gpio设备led_gpio[0] = devm_gpiod_get(&pdev->dev, "led0", GPIOD_OUT_LOW);led_gpio[1] = devm_gpiod_get(&pdev->dev, "led1", GPIOD_OUT_LOW);led_gpio[2] = devm_gpiod_get(&pdev->dev, "led2", GPIOD_OUT_LOW);led_gpio[3] = devm_gpiod_get(&pdev->dev, "led3", GPIOD_OUT_LOW);for (i = 0; i < SIMPIE_LED_MAX; i++){if (IS_ERR(led_gpio[i])){ret = PTR_ERR(led_gpio[i]);return ret;}// 输出初始电平ret = gpiod_direction_output(led_gpio[i], LED_OFF);}device_create_file(&pdev->dev, &dev_attr_gpio_led);return ret;}// 驱动入口static int gpio_led_probe(struct platform_device *pdev){int ret = 0;pr_err("[vanxoak]gpio_led_probe start...\n");// LED灯gpio初始化及输出配置ret = simpie_led_init(pdev);pr_err("[vanxoak]gpio_led_probe end...\n");return 0;}// 绑定设备static struct of_device_id gpio_led_match_table[] = {{.compatible = "yz,gpio-led"},{}};static int gpio_led_remove(struct platform_device *pdev){pr_err("[vanxoak]gpio_led_remove...\n");return 0;}static struct platform_driver gpio_led_driver = {.driver = {.name = MODULE_NAME,.owner = THIS_MODULE,.of_match_table = gpio_led_match_table,},.probe = gpio_led_probe,.remove = gpio_led_remove,};// gpio初始化入口static int gpio_led_init(void){struct device *mydev;pr_err("[vanxoak]gpio_led_init start...\n");platform_driver_register(&gpio_led_driver);major = register_chrdev(0, "gpiotest", &gpio_led_ops);// 创建gpio_led_class设备cls = class_create(THIS_MODULE, "gpio_led_class");// 在gpio_led_class设备目录下创建一个gpio_led_device属性文件mydev = device_create(cls, 0, MKDEV(major, 0), NULL, MISC_NAME);if (sysfs_create_file(&(mydev->kobj), &dev_attr_gpio_led.attr)){return -1;}return 0;}static void gpio_led_exit(void){pr_err("[vanxoak]gpio_led_exit...\n");platform_driver_unregister(&gpio_led_driver);device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "gpiotest");}module_init(gpio_led_init);module_exit(gpio_led_exit);MODULE_DESCRIPTION("Device_create Driver");MODULE_LICENSE("GPL");
设备树配置
gpio_led: yz,gpio-led {status = "disabled";compatible = "yz,gpio-led";led0-gpio = <&ap_gpio 138 GPIO_ACTIVE_HIGH>;led1-gpio = <&ap_gpio 93 GPIO_ACTIVE_HIGH>;led2-gpio = <&ap_gpio 130 GPIO_ACTIVE_HIGH>;led3-gpio = <&ap_gpio 121 GPIO_ACTIVE_HIGH>;};
配置好上面gpio驱动后重新编译更新kernel 可以在/dev目录下找到对应的设备文件
"/dev/gpio_led_device",通过读写设备文件就可以操作gpio了。
1.2 创建 Native 库
1.2.1设置 JNI 方法
在 App 中定义 JNI 方法以实现与 GPIO 设备的交互。
public class NativeClass {static {try {System.loadLibrary("jni_gpiocontrol");Log.d("NativeClass", "Native library loaded successfully.");} catch (UnsatisfiedLinkError e) {Log.e("NativeClass", "Failed to load native library: " + e.getMessage());// throw new RuntimeException("Failed to load native library", e);}}// 声明本地方法public native int controlGPIO(int cmd, long arg);
}
1.2.2 实现 Native 方法
在app/src/main目录下创建一个cpp文件夹(如果你的项目是用Kotlin编写的,这个步骤仍然适用,因为JNI是用C/C++实现的)。将你的libjni_gpiocontrol.cpp文件放到这个cpp目录中。
注意事项:确保本地方法签名正确,Java方法签名和本地(C/C++)方法实现之间必须完全匹配。
#include <jni.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include <stdio.h>#include <android/log.h>#include <errno.h>#include <string.h>#define MM_DEV_MAGIC 'N'#define RFID_LED _IO(MM_DEV_MAGIC, 138)#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)#define DEVICE_PATH "/dev/gpio_led_device"#define LOG_TAG "GPIOControl"extern "C" JNIEXPORT jint JNICALLJava_com_example_gpio_NativeClass_controlGPIO(JNIEnv *env, jobject obj, jint cmd, jlong arg) {int device_fd;long ioctl_result;unsigned int ioctl_cmd = cmd;// Open the device filedevice_fd = open(DEVICE_PATH, O_RDWR);if (device_fd < 0) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Could not open device: %s", strerror(errno));return -1;}// Translate cmd to appropriate ioctl command based on inputswitch (cmd) {case 138:ioctl_cmd = RFID_LED;break;case 93:ioctl_cmd = RFID_IO1;break;case 130:ioctl_cmd = RFID_IO2;break;case 121:ioctl_cmd = RFID_IO3;break;default:__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Invalid command");close(device_fd);return -1;}// Send an ioctl to the deviceioctl_result = ioctl(device_fd, ioctl_cmd, arg);if (ioctl_result < 0) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to call ioctl: %s", strerror(errno));close(device_fd);return -1;}// Close the deviceclose(device_fd);return 0;}
1.2.3 编译 Native 库
使用 CMake 或 ndk-build 工具编译你的 native 代码为共享库(.so 文件)。
添加app\src\main\cpp\CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)project("gpiotest")add_library(jni_gpiocontrol SHARED libjni_gpiocontrol.cpp)find_library( log-lib log )target_link_libraries(jni_gpiocontrol${log-lib} )
1.2.4 调用 Native 方法
通过 JNI 接口在 App 中调用实现的 native 方法以控制 GPIO。
public class MainActivity extends AppCompatActivity {private NativeClass nativeClass;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);nativeClass = new NativeClass();// 示例:打开LEDint result = nativeClass.controlGPIO(138, 1);// 根据result处理结果}}
2. SELinux 配置
由于直接访问硬件设备在 Android 中受到 SELinux 策略的限制,需要修改 SELinux 策略以允许 App 访问 GPIO 设备文件。
定义设备类型:为 GPIO 设备定义一个新的 SELinux 类型(如 gpio_led_device_t)。
在SDK_dir/device/sprd/sharkle/common/sepolicy/device.te 添加
# 定义新的设备类型type gpio_led_device_t, dev_type;
分配文件上下文:为 GPIO 设备文件分配新定义的 SELinux 类型。
SDK_dir/device/sprd/sharkle/common/sepolicy/file_contexts中添加
/dev/gpio_led_device u:object_r:gpio_led_device_t:s0
授予权限:在 SELinux 策略中添加规则,允许 App 访问 GPIO 设备。
SDK_dir/device/sprd/sharkle/common/sepolicy/system_app.te
# 允许 system_app 访问 gpio_led_deviceallow system_app gpio_led_device_t:chr_file { read write };
重新编译 SELinux 策略:对更改的 SELinux 策略进行编译,并将其部署到设备上。这一步骤的目的是将自定义的安全策略更改应用到Android构建系统的预设SELinux策略中,确保在编译系统镜像时,这些更改会被包含进去。
cp system/sepolicy/public/app.te system/sepolicy/prebuilts/api/29.0/public/app.tecp system/sepolicy/private/coredomain.te system/sepolicy/prebuilts/api/29.0/private/coredomain.te
3. 测试与部署
测试 App:在具有所需硬件支持的 Android 10 设备上测试 App。确保 App 能成功加载 native 库,并能通过 JNI 调用控制 GPIO。
SELinux 策略测试:验证 SELinux 策略更改是否允许 App 无障碍地访问 GPIO 设备。
问题排查:如果遇到访问被拒绝的情况,请检查 SELinux 审计日志以确定是否需要进一步调整策略。
3.1注意事项
安全性:在修改 SELinux 策略以增加访问权限时,务必小心谨慎,避免引入安全漏洞。
设备兼容性:确保你的实现考虑到了不同设备可能存在的硬件和配置差异。
文档和维护:适当记录你的设计和实现过程,包括 JNI 接口、native 代码和 SELinux 策略更改,以便于未来的审计和维护。
通过遵循以上步骤,你可以在遵守 Android 安全模型的同时,实现 App 对 GPIO 的有效控制。
相关文章:
如何以JNI方式实现安卓APP控制GPIO?
本文档提供了在 Android 10 设备上通过应用程序(App)控制通用输入输出(GPIO)的详细指南。这涵盖了从创建 gpio驱动到App 配置 以及 SELinux 策略以允许特定访问的所有必要步骤。 1. 驱动实现 添加创建gpio控制驱动bsp\kernel\ke…...
计算机网络学习笔记——运输层(b站)
目录 一、 运输层概述 二、运输层端口号、复用与分用的概念 三、UDP和TCP的对比 四、TCP的流量控制 五、TCP的拥塞控制 六、TCP超时重传时间的选择 七、TCP可靠传输的实现 八、TCP报文段的首部格式 一、 运输层概述 物理层、数据链路层、网络层实现了主机到主机的通信…...
HBase数据库面试知识点:第二部分 - 核心技术(持续更新中)
目录 1. 分布式存储与HDFS 2. 面向列的存储 3. 数据版本控制 4. Region与RegionServer 5. 分布式协调服务(ZooKeeper) 1. 分布式存储与HDFS HBase利用Hadoop的HDFS作为其底层存储系统,确保数据的高可靠性和可扩展性。 数据块࿰…...
Spring 使用SSE(Server-Sent Events)学习
什么是SSE SSE 即服务器发送事件(Server-Sent Events),是一种服务器推送技术,允许服务器在客户端建立连接后,主动向客户端推送数据。 SSE 基于 HTTP 协议,使用简单,具有轻量级、实时性和断线重…...
词法分析器的设计与实现--编译原理操作步骤,1、你的算法工作流程图; 2、你的函数流程图;3,具体代码
实验原理: 词法分析是编译程序进行编译时第一个要进行的任务,主要是对源程序进行编译预处理之后,对整个源程序进行分解,分解成一个个单词,这些单词有且只有五类,分别时标识符、关键字(保留字&a…...
linux查看磁盘类型命令
在Linux中,有多种方法可以查看磁盘是固态硬盘(SSD)还是机械硬盘(HDD)。以下是一些常用的方法: 查看/sys/block/目录 /sys/block/目录包含了系统中所有块设备的信息。你可以查看这个目录中的设备属性来判断…...
多线程调用同一个不包含可变状态,并且是线程安全的方法时,可同时执行,不必等待排队
多线程调用同一个不包含可变状态,并且是线程安全的方法时,可同时执行,不必等待排队 前言同时执行方法的条件示例并发执行的优势实验验证总结 前言 如果方法不包含可变状态,并且是线程安全的,那么在高并发环境下&#…...
Java文件操作①——XML文件的读取
系列文章目录 文章目录 系列文章目录前言一、邂逅XML二、应用 DOM 方式解析 XML三、应用 SAX 方式解析 XML四、应用 DOM4J 及 JDOM 方式解析 XMLJDOM 方式解析 XMLDOM4J 方式解析 XML前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。…...
【记录】网络|没有路由器没有网线,分别使用手机或Windows电脑共享网络给ARM64开发板,应急连接
事情是这样的,我的开发板明明已经选择了记住热点 WiFi 密码,但是却没有在开机的时候自动连接,我又没有放显示器在身边,又不想为了这点事去找个显示器来,就非常难受。 我手边有的设备是: 笔记本电脑&#…...
一键设置常用纸张和页面边距-Word插件-大珩助手
Word大珩助手是一款功能丰富的Office Word插件,旨在提高用户在处理文档时的效率。它具有多种实用的功能,能够帮助用户轻松修改、优化和管理Word文件,从而打造出专业而精美的文档。 【新功能】常用纸张和常用边距 1、一键设定符合中国人常用…...
在树莓派3B+中下载opencv(遇到的各种问题及解决)
目录 前言 1、删除原版本下新版本 2、python虚拟环境 3、python版本共存换链接——给版本降低 4、烧录之前版本的文件(在清华源中可以找,不用官网的烧录文件就行; 比如:(balenaEtcher)重新烧录有问题…...
精准检测,安全无忧:安全阀检测实践指南
安全阀作为一种重要的安全装置,在各类工业系统和设备中发挥着举足轻重的作用。 它通过自动控制内部压力,有效防止因压力过高而引发的设备损坏和事故风险,因此,对安全阀进行定期检测,确保其性能完好、工作可靠…...
Transformer系列:图文详解KV-Cache,解码器推理加速优化
前言 KV-Cache是一种加速Transformer推理的策略,几乎所有自回归模型都内置了KV-Cache,理解KV-Cache有助于更深刻地认识Transformer中注意力机制的工作方式。 自回归推理过程知识准备 自回归模型采用shift-right的训练方式,用前文预测下一个…...
基础篇03——SQL约束
概述 约束示例 完成以下案例: create table user (id int primary key auto_increment comment 主键,name varchar(10) not null unique comment 姓名,age tinyint unsigned check ( age > 0 and age < 120 ) comment 年龄,status char(1) default 1 commen…...
人工智能--深度神经网络
目录 🍉引言 🍉深度神经网络的基本概念 🍈神经网络的起源 🍍 神经网络的基本结构 🍉深度神经网络的结构 🍈 卷积神经网络(CNN) 🍈循环神经网络(RNN&…...
VOC格式标签各个字段的解释
想了解一下VOC格式数据标签各个字段的含义,搜了一圈没看到,懒得去官网了,直接问了GPT-4o,以下回答字段解析来自GPT-4o,例子我自己写的 VOC (Visual Object Classes) 数据标签格式主要用于目标检测任务。VOC格式的标签…...
2024年端午节放假通知
致尊敬的客户以及全体同仁: 2024年端午节将至,根据国务院办公厅通知精神,结合公司的实际情况,现将放假事宜通知如下: 2024年6月8日(星期六)至6月10日(星期一)ÿ…...
Transformer系列:注意力机制的优化,MQA和GQA原理简述
前言 多查询注意力(MQA)、分组查询注意力(GQA)是Transformer中多头注意力(MHA)的变种,它们大幅提高了解码器的推理效率,在LLaMA-2,ChatGLM2等大模型中有广泛使用,本篇介绍MQA、GQA的原理并分析其源码实现。 使用MQA,G…...
Python知识点11---高阶函数
提前说一点:如果你是专注于Python开发,那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了,而如果你和作者一样只是操作其他技术的Python API那就足够了。 本篇介绍一下Python的内置函数也叫高阶函数,就是Python自…...
JavaSE——【逻辑控制】(习题)
一、分支结构 2.1 if 语句 【练习】2.1.1 小明,如果这次考到90分以上,给你奖励一个大鸡腿,否则奖你一个大嘴巴子 int score 92;if(score > 90){System.out.println("吃个大鸡腿!!!");}else{System.out.println("挨大嘴…...
自动驾驶仿真:python和carsim联合仿真案例
文章目录 前言一、Carsim官方案例二、Carsim配置1、车辆模型2、procedure配置3、Run Control配置 三、python编写四、运行carsim五、运行python总结 前言 carsim内部有许多相关联合仿真的demo,simulink、labview等等都有涉及,这里简单介绍下python和car…...
Qt报错:libvlc开发的程序,出现Direct3D output全屏窗口
问题描述: 在qt中开发重播模块时,第一次在窗口正常播放,点击重播按钮后会弹出新的Direct3D output窗口播放视频 分析: 因为libvlc_media_player_set_hwnd 这个函数 设置了不存在的窗口句柄,导致vlc视频播放窗口没有嵌…...
yolov5的口罩识别系统+GUI界面 (附代码)
基于YOLOv5模型的口罩识别系统,结合了GUI界面,旨在帮助用户快速、准确地识别图像或视频中佩戴口罩的情况。YOLOv5是一种流行的目标检测模型,具有高效的实时检测能力,而GUI界面则提供了友好的用户交互界面,使得整个系统…...
WPF中Window的外观实现及常用属性
文章目录 1. 概要2. Window的外观2.1 Window的外观组成2.2 Window的实现2.3 Window外观配置2.4 Window 的其他常用属性1. AllowsTransparency 2. WindowStartupLocation3. ShowInTaskbar4. ShowActivated5. SizeToContent6. Topmost7. WindowStyle 1. 概要 和 Android 类似, W…...
(有代码示例)Vue 或 JavaScript中使用全局通信的3种方式
在 Vue 或 JavaScript 应用中,可以使用以下库来实现全局事件通信: Vue.js 中的 EventBus: 在 Vue.js 中,可以使用 EventBus 来实现全局事件通信。EventBus 是一个 Vue 实例,用于在组件之间传递事件。你可以使用 $on、…...
MAB规范(1):概览介绍
前言 MATLAB的MAAB(MathWorks Automotive Advisory Board)建模规范是一套由MathWorks主导的建模指南,旨在提高基于Simulink和Stateflow进行建模的代码质量、可读性、可维护性和可重用性。这些规范最初是由汽车行业的主要厂商共同制定的&…...
基于振弦采集仪的土木工程安全监测技术研究
基于振弦采集仪的土木工程安全监测技术研究 随着土木工程的发展,安全监测成为了非常重要的一部分。土木工程的安全监测旨在及早发现结构的变形、位移、振动等异常情况,以便及时采取措施进行修复或加固,从而保障工程的安全运行。振弦采集仪作…...
这个高考作文满分的极客,想和你聊聊新媒体写作
计育韬 曾为上海市高考作文满分考生 微信官方 SVG AttributeName 开发者 新榜 500 强运营人 复旦大学青年智库讲师 浙江传媒学院客座导师 上海团市委新媒体顾问 上海市金山区青联副主席 文案能力,从来就不是一蹴而就的。今天,来和大家聊聊当年我的…...
AI推介-多模态视觉语言模型VLMs论文速览(arXiv方向):2024.05.25-2024.05.31
文章目录~ 1.Empowering Visual Creativity: A Vision-Language Assistant to Image Editing Recommendations2.Bootstrap3D: Improving 3D Content Creation with Synthetic Data3.Video-MME: The First-Ever Comprehensive Evaluation Benchmark of Multi-modal L…...
如何通过Python SMTP配置示例发附件邮件?
Python SMTP配置的步骤?SMTP服务器的优缺点有哪些? 当我们需要发送包含附件的邮件时,自动化的解决方案显得尤为重要。Python提供了SMTP库,使我们能够轻松配置并发送带有附件的邮件。AokSend将通过一个示例来展示如何操作…...
ps做网站需注意什么/网站推广平台有哪些
题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid1317 【题目描述】 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,……...
网页游戏网站排名前10名/软文代写公司
VirtIO 是一个用来实现“虚拟IO”的通用框架,典型的用于虚拟机上面实现虚拟的pci,网卡,磁盘等虚拟设备,kvm等都使用了这个技术。大概浏览了一下相关的内核代码,这个virtio应该是专门应用于那种“半虚拟化?&…...
国外免费logo网站/百度网址提交
增加并发: 默认scrapy开启的线程数为32个,可以适当进行增加,在seeting配置文件中修改councurrent_requests100 降低日志等级: 在运行scrapy运行时,会有大量日志输出,为了减少cpu的使用率,可将日…...
全网展示型网站建设/优化关键词排名软件
未来的模样作文2000字未来的模样毫无悬念,成成首先跑到了家门口,着急的等待着我为他开门。见我和微微跟上来,他不停的跺跺脚,踢踢门,嘴里还欢快的喊着:“我又赢了!我又赢了!”边喊还…...
长春个人网站制作公司/宁波网站推广哪家公司好
1.3 消息监听器MessageListener在Spring整合JMS的应用中我们在定义消息监听器的时候一共可以定义三种类型的消息监听器,分别是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。下面就分别来介绍一下这几种类型的区别。1.3.1 MessageListene…...
门户网站建设/搜索网站关键词
1、官方文档 沙箱环境使用说明:https://docs.open.alipay.com/200/105311。 开放平台--沙箱环境:https://openhome.alipay.com/platform/appDaily.htm?tabinfo。 rsa签名验签工具:https://docs.open.alipay.com/291/106097。 2、沙箱调试…...