前言

本篇为蓝牙HID系列篇章之一,本篇以红米K30(MIUI13即Android 12)手机作为蓝牙HID设备,可以与电脑、手机、平板等其他蓝牙主机进行配对从而实现鼠标触控板的功能。
蓝牙HID系列篇章:
蓝牙HID——将android设备变成蓝牙键盘(BluetoothHidDevice)
蓝牙HID——android利用手机来解锁电脑/平板/iPhone
蓝牙HID——Android手机注册HID时出现 Could not bind to Bluetooth (HID Device) Service with Intent * 的问题分析

HID开发

Android 9开放了 BluetoothHidDevice 等HID相关的API,通过与系统蓝牙HID服务通信注册成蓝牙HID设备。首先通过 BluetoothProfile.HID_DEVICE 的描述类型得到 BluetoothHidDevice 抽象实例:

    private BluetoothAdapter mBtAdapter;private BluetoothHidDevice mHidDevice;private void callBluetooth() {Log.d(TAG, "callBluetooth");mBtAdapter = BluetoothAdapter.getDefaultAdapter();mBtAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {@Overridepublic void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {Log.d(TAG, "onServiceConnected:" + i);if (i == BluetoothProfile.HID_DEVICE) {if (!(bluetoothProfile instanceof BluetoothHidDevice)) {Log.e(TAG, "Proxy received but it's not BluetoothHidDevice");return;}mHidDevice = (BluetoothHidDevice) bluetoothProfile;registerBluetoothHid();}}@Overridepublic void onServiceDisconnected(int i) {Log.d(TAG, "onServiceDisconnected:" + i);}}, BluetoothProfile.HID_DEVICE);}

再调用 BluetoothHidDevice.registerApp() 将 Android 设备注册成蓝牙HID设备:

    private BluetoothDevice mHostDevice;private final BluetoothHidDeviceAppQosSettings qosSettings= new BluetoothHidDeviceAppQosSettings(BluetoothHidDeviceAppQosSettings.SERVICE_BEST_EFFORT,800, 9, 0, 11250, BluetoothHidDeviceAppQosSettings.MAX);private final BluetoothHidDeviceAppSdpSettings mouseSdpSettings = new BluetoothHidDeviceAppSdpSettings(HidConfig.MOUSE_NAME, HidConfig.DESCRIPTION, HidConfig.PROVIDER,BluetoothHidDevice.SUBCLASS1_MOUSE, HidConfig.MOUSE_COMBO);private void registerBluetoothHid() {if (mHidDevice == null) {Log.e(TAG, "hid device is null");return;}mHidDevice.registerApp(mouseSdpSettings, null, qosSettings, Executors.newCachedThreadPool(), new BluetoothHidDevice.Callback() {@Overridepublic void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {Log.d(TAG, "onAppStatusChanged:" + (pluggedDevice != null ? pluggedDevice.getName() : "null") + " registered:" + registered);if (registered) {Log.d(TAG, "paired devices: " + mHidDevice.getConnectionState(pluggedDevice));if (pluggedDevice != null && mHidDevice.getConnectionState(pluggedDevice) != BluetoothProfile.STATE_CONNECTED) {boolean result = mHidDevice.connect(pluggedDevice);Log.d(TAG, "hidDevice connect:" + result);}}if (mBluetoothHidStateListener != null) {mBluetoothHidStateListener.onRegisterStateChanged(registered, pluggedDevice != null);}}@Overridepublic void onConnectionStateChanged(BluetoothDevice device, int state) {Log.d(TAG, "onConnectionStateChanged:" + device + "  state:" + state);if (state == BluetoothProfile.STATE_CONNECTED) {mHostDevice = device;}if (state == BluetoothProfile.STATE_DISCONNECTED) {mHostDevice = null;}if (mBluetoothHidStateListener != null) {mBluetoothHidStateListener.onConnectionStateChanged(state);}}});}

蓝牙鼠标Mouse的描述信息如下,主要 为 MOUSE_COMBO 的描述协议,正确的描述协议才能成功与其他设备通信。

public class HidConfig {public final static String MOUSE_NAME = "VV Mouse";public final static String DESCRIPTION = "VV for you";public final static String PROVIDER = "VV";public static final byte[] MOUSE_COMBO = {(byte) 0x05, (byte) 0x01,              // USAGE_PAGE (Generic Desktop)(byte) 0x09, (byte) 0x02,              // USAGE (Mouse)(byte) 0xa1, (byte) 0x01,              // COLLECTION (Application)(byte) 0x85, (byte) 0x04,              // REPORT_ID (4)(byte) 0x09, (byte) 0x01,              //  USAGE (Pointer)(byte) 0xa1, (byte) 0x00,              //  COLLECTION (Physical)(byte) 0x05, (byte) 0x09,              //   USAGE_PAGE (Button)(byte) 0x19, (byte) 0x01,              //   USAGE_MINIMUM (Button 1)(byte) 0x29, (byte) 0x02,              //   USAGE_MAXIMUM (Button 2)(byte) 0x15, (byte) 0x00,              //   LOGICAL_MINIMUM (0)(byte) 0x25, (byte) 0x01,              //   LOGICAL_MAXIMUM (1)(byte) 0x95, (byte) 0x03,              //   REPORT_COUNT (3)(byte) 0x75, (byte) 0x01,              //   REPORT_SIZE (1)(byte) 0x81, (byte) 0x02,              //   INPUT (Data,Var,Abs)(byte) 0x95, (byte) 0x01,              //   REPORT_COUNT (1)(byte) 0x75, (byte) 0x05,              //   REPORT_SIZE (5)(byte) 0x81, (byte) 0x03,              //   INPUT (Cnst,Var,Abs)(byte) 0x05, (byte) 0x01,              //   USAGE_PAGE (Generic Desktop)(byte) 0x09, (byte) 0x30,              //   USAGE (X)(byte) 0x09, (byte) 0x31,              //   USAGE (Y)(byte) 0x09, (byte) 0x38,              //   USAGE (Wheel)(byte) 0x15, (byte) 0x81,              //   LOGICAL_MINIMUM (-127)(byte) 0x25, (byte) 0x7F,              //   LOGICAL_MAXIMUM (127)(byte) 0x75, (byte) 0x08,              //   REPORT_SIZE (8)(byte) 0x95, (byte) 0x03,              //   REPORT_COUNT (3)(byte) 0x81, (byte) 0x06,              //   INPUT (Data,Var,Rel)//水平滚轮(byte) 0x05, (byte) 0x0c,              //   USAGE_PAGE (Consumer Devices)(byte) 0x0a, (byte) 0x38, (byte) 0x02, //   USAGE (AC Pan)(byte) 0x15, (byte) 0x81,              //   LOGICAL_MINIMUM (-127)(byte) 0x25, (byte) 0x7f,              //   LOGICAL_MAXIMUM (127)(byte) 0x75, (byte) 0x08,              //   REPORT_SIZE (8)(byte) 0x95, (byte) 0x01,              //   REPORT_COUNT (1)(byte) 0x81, (byte) 0x06,              //   INPUT (Data,Var,Rel)(byte) 0xc0,                           //  END_COLLECTION(byte) 0xc0,                           // END_COLLECTION};

在注册完成后启动设备发现,让HID能被其他设备发现,下面ActivityResultLauncher.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE) 相当于调用 BluetoothAdapter.setScanMode() 的隐藏API

    private ActivityResultLauncher<Intent> mActivityResultLauncher;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mouse);mActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {Log.d(TAG, "onActivityResult:" + result.toString());});}@Overridepublic void onRegisterStateChanged(boolean registered, boolean hasDevice) {if (registered) {if (!hasDevice) {// startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), 1);mActivityResultLauncher.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE));}}}

ActivityResultLauncher 的相关方法也可用 startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), REQUEST_CODE) 来替代,但 startActivityForResult() 是废弃的方法,不建议使用。
接下来与蓝牙主机(电脑、手机等)进行蓝牙配对,已配对过需要取消配对。配对完成即可实现对蓝牙主机的鼠标触摸控制。

手势识别

手势识别通过对触摸事件以及手势监听进行各种手势的判断(移动鼠标、左键单击、左键双击、右键双指单击、双指垂直/水平滚动)。

CustomMotionListener customMotionListener = new CustomMotionListener(this, mBluetoothHidManager);
findViewById(R.id.layout_touch).setOnTouchListener(customMotionListener);

手势逻辑处理代码如下:

package com.example.bluetoothproject;import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;import org.apache.commons.lang3.concurrent.BasicThreadFactory;import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class CustomMotionListener implements View.OnTouchListener, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {private final GestureDetector mGestureDetector;private BluetoothHidManager mBluetoothHidManager;private int mPointCount;private long mDoubleFingerTime;private final ScheduledExecutorService mExecutorService;private float mPreX;private float mPreY;private boolean mLongPress;public CustomMotionListener(Context context, BluetoothHidManager bluetoothHidManager) {mBluetoothHidManager = bluetoothHidManager;mGestureDetector = new GestureDetector(context, this);mGestureDetector.setOnDoubleTapListener(this);mExecutorService = new ScheduledThreadPoolExecutor(1,new BasicThreadFactory.Builder().namingPattern("mouse-schedule-pool-%d").daemon(true).build());}@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {return false;}@Overridepublic boolean onDoubleTap(MotionEvent e) {return false;}@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {//左键单指双击(选中文本的效果)if (e.getAction() == MotionEvent.ACTION_DOWN) {mBluetoothHidManager.sendLeftClick(true);} else if (e.getAction() == MotionEvent.ACTION_UP) {mBluetoothHidManager.sendLeftClick(false);}return true;}@Overridepublic boolean onDown(MotionEvent e) {return false;}@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {//左键单击mBluetoothHidManager.sendLeftClick(true);mBluetoothHidManager.sendLeftClick(false);return true;}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//双指滚动,x为水平滚动,y为垂直滚动,消抖处理if (mPointCount == 2) {if (Math.abs(distanceX) > Math.abs(distanceY))  {distanceX = distanceX > 0 ? 1 : distanceX < 0 ? -1 : 0;distanceY = 0;} else {distanceY = distanceY > 0 ? -1 : distanceY < 0 ? 1 : 0;distanceX = 0;}mBluetoothHidManager.sendWheel((byte) (distanceX), (byte) (distanceY));}return false;}@Overridepublic void onLongPress(MotionEvent e) {//单键长按效果mBluetoothHidManager.sendLeftClick(true);mLongPress = true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return false;}@Overridepublic boolean onTouch(View v, MotionEvent event) {float x = event.getX();float y = event.getY();if (mGestureDetector.onTouchEvent(event)) {return true;}mPointCount = event.getPointerCount();switch (event.getActionMasked()) {case MotionEvent.ACTION_POINTER_DOWN://双指单击代表右键记录时间if (event.getPointerCount() == 2) {mDoubleFingerTime = System.currentTimeMillis();}break;case MotionEvent.ACTION_MOVE://单指代表移动鼠标if (event.getPointerCount() == 1) {float dx = x - mPreX;if (dx > 127) dx = 127;if (dx < -128) dx = -128;float dy = y - mPreY;if (dy > 127) dy = 127;if (dy < -128) dy = -128;mBluetoothHidManager.senMouse((byte) dx, (byte) dy);} else {mBluetoothHidManager.senMouse((byte) 0, (byte) 0);}break;case MotionEvent.ACTION_UP:if (mLongPress) {mBluetoothHidManager.sendLeftClick(false);mLongPress = false;}break;case MotionEvent.ACTION_POINTER_UP://双指按下代表右键if (event.getPointerCount() == 2 && System.currentTimeMillis() - mDoubleFingerTime < ViewConfiguration.getDoubleTapTimeout()) {mBluetoothHidManager.sendRightClick(true);//延时释放避免无效mExecutorService.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {mBluetoothHidManager.sendRightClick(false);}}, 0, 50, TimeUnit.MILLISECONDS);                }break;default:break;}mPreX = x;mPreY = y;return true;}
}

向蓝牙主机发送的鼠标触摸按键的报告如下:

    private boolean mLeftClick;private boolean mRightClick;public void sendLeftClick(boolean click) {mLeftClick = click;senMouse((byte) 0x00, (byte) 0x00);}public void sendRightClick(boolean click) {mRightClick = click;senMouse((byte) 0x00, (byte) 0x00);}public void senMouse(byte dx, byte dy) {if (mHidDevice == null) {Log.e(TAG, "senMouse failed,  hid device is null!");return;}if (mHostDevice == null) {Log.e(TAG, "senMouse failed,  hid device is not connected!");return;}byte[] bytes = new byte[5];//bytes[0]字节:bit0: 1表示左键按下 0表示左键抬起 | bit1: 1表示右键按下 0表示右键抬起 | bit2: 1表示中键按下 | bit7~3:补充的常数,无意义,这里为0即可bytes[0] = (byte) (bytes[0] | (mLeftClick ? 1 : 0));bytes[0] = (byte) (bytes[0] | (mRightClick ? 1 : 0) << 1);bytes[1] = dx;bytes[2] = dy;Log.d(TAG, "senMouse   Left:" + mLeftClick+ ",Right:" + mRightClick + ",bytes: " + BluetoothUtils.bytesToHexString(bytes));mHidDevice.sendReport(mHostDevice, 4, bytes);}public void sendWheel(byte hWheel, byte vWheel) {if (mHidDevice == null) {Log.e(TAG, "sendWheel failed,  hid device is null!");return;}if (mHostDevice == null) {Log.e(TAG, "sendWheel failed,  hid device is not connected!");return;}byte[] bytes = new byte[5];bytes[3] = vWheel; //垂直滚轮bytes[4] = hWheel; //水平滚轮Log.d(TAG, "sendWheel vWheel:" + vWheel + ",hWheel:" + hWheel);mHidDevice.sendReport(mHostDevice, 4, bytes);}

效果

实现以上步骤即可将手机变成蓝牙鼠标/触控板,下面是实现的效果:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

鼠标移动:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

左键单击:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

左键单指快速双击:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

右键双指单击:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

双指垂直上下滚动:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

双指水平左右滚动:
蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网
完整视频效果展示:

蓝牙HID——将android设备变成蓝牙鼠标/触控板

查看全文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/230099.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章:

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)

前言
本篇为蓝牙HID系列篇章之一,本篇以红米K30(MIUI13即Android 12)手机作为蓝牙HID设备,可以与电脑、手机、平板等其他蓝牙主机进行配对从而实现鼠标触控板的功能。 蓝牙HID系列篇章: 蓝牙HID——将android设备变成……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

基于蚁群算法的TPS问题求解策略研究(Matlab代码实现)

🍒🍒🍒欢迎关注🌈🌈🌈 📝个人主页:我爱Matlab 👍点赞➕评论➕收藏 养成习惯(一键三连)🌻🌻🌻 🍌希……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

【数据结构】——单链表

目录
1.链表
1.1 链表的概念及结构
1.2 链表的分类
1. 单向或者双向 2. 带头或者不带头 3. 循环或者非循环 1.3实现一个单链表(无头单项非循环链表增删查改的实现)
1.链表结构的创建
2.创建一个节点
3.创建一个链表
4.打印链表
5……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

虚拟机(Vmware)磁盘扩容(xfs格式)

先将虚拟机关机,按上图调整虚拟磁盘大小。 1.开启并进入虚拟机,打开终端,输入命令 df -Th 查看格式,图示中 /dev/mapper/centos-root 类型为xfs。
[mangolocalhost ~]$ df -Th
Filesystem Type Size Used Ava……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

SpringFramework:SpringBean的注入方式

SpringBean的注入方式 文章目录SpringBean的注入方式一、Spring 容器1. 什么是容器2. 容器如何工作二、SpringBean 注入方式1. SpringBean 注入方式分类2. Autowiring 自动绑定三、获取 Spring Bean一、Spring 容器
1. 什么是容器
Spring IOC 容器就是一个 org.springframewo……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

BurpSuit官方实验室之信息泄露

BurpSuit官方实验室之信息泄露
这是BurpSuit官方的实验室靶场,以下将记录个人信息泄露共5个Lab的通关过程
Web Security Academy: Free Online Training from PortSwigger
lab1:
Information disclosure in error messages
错误消息中的信息泄露
在……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

27服务-安全访问状态转换

诊断协议那些事儿
诊断协议那些事儿专栏系列文章,本文将介绍安全访问状态图——作为UDS27服务的规范性附件。
可参考前两篇文章: 27服务-SecurityAccess UDS – 深论Security Access Service
27服务的初衷就是防止无权限人员进行非法数据操作&#xff……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

JavaScript内置对象总结介绍

目录
浏览器对象模型
(1)值属性
(2)函数属性
(3)基本对象
(4)错误对象
(5)数字和日期对象
(6)字符串
(7&#xff……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

brew安装特定版本flow,解决问题!

在atomreact-native项目中安装了nuclide。然而使用flow的时候出现了问题。
$ brew -v
Homebrew 1.2.0$ flow version
Flow, a static type checker for JavaScript, version 0.45.0$ flow
Launching Flow server for /Users/real/Desktop/reactPro/pro2
Wrong version of Flow……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

开源作品:引流宝!集活码、短网址等功能为一体的工具!致力于提高引流效率,减少资源流失!

前言
开发这款工具的初衷是为了辅助自己的工作,提供自己日常工作的效率,自己使用了一段时间下来觉得很有用,于是完善之后开源。如今已经开源近2年,第一个版本是在2020年9月份开源,收获了390个star,后来持续……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

数据要素化条件之一:原始性

随着技术的发展,计算机不仅成为人类处理信息的工具,而且逐渐地具有自主处理数据的能力,出现了替代人工的数据智能技术。数据智能的大规模使用需要关于同一分析对象或同一问题的、来源于不同数据源的海量数据。这种数据必须是针对特定对象的记……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

【面试题 高逼格利用 类实现加法】编写代码, 实现多线程数组求和.

编写代码, 实现多线程数组求和.关键1. 数组的初始化关键2. 奇偶的相加import java.util.Random;public class Thread_2533 {public static void main(String[] args) throws InterruptedException {// 记录开始时间long start System.currentTimeMillis();// 1. 给定一个很长的……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

一个python训练

美国:28:麻省理工学院,斯坦福大学,哈佛大学,加州理工学院,芝加哥大学,普林斯顿大学,宾夕法尼亚大学,耶鲁大学,康奈尔大学,哥伦比亚大学,密歇根大学安娜堡分校,约翰霍普金斯大学,西北大学,加州大学伯克利分校,纽约大学,加州大学洛杉矶分校,杜克大学,卡内基梅隆大学,加州大学圣地……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

Mybatis03学习笔记

目录 使用注解开发
设置事务自动提交
mybatis运行原理
注解CRUD
lombok使用(偷懒神器,大神都不建议使用)
复杂查询环境(多对一)
复杂查询环境(一对多)
动态sql环境搭建
动态sql常用标签……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

设置或取得c# NumericUpDown 编辑框值的方法,(注意:不是Value值)

本人在C#开发中使用到了NumericUpDown控件,但是发现该控件不能直接控制显示值,经研究得到下面的解决办法
NumericUpDown由于是由多个控件组合而来的控件,其中包含一个类似TextBox的控件,若想取得或改变其中的值要使用如下方法
N……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

使用NPOI 技术 的SetColumnWidth 精确控制列宽不能成功的解决办法(C#)

在使用NPOI技术开发自动操作EXCEL软件时遇到不能精确设置列宽的问题。

ISheet sheet1 hssfworkbook.CreateSheet("Sheet1");
sheet1.SetColumnWidth(0, 50 * 256); // 在EXCEL文档中实际列宽为49.29
sheet1.SetColumnWidth(1, 100 * 256); // 在EXCEL文……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

Mysql 数据库zip版安装时basedir datadir 路径设置问题,避免转义符的影响

本人在开发Mysql数据库自动安装程序时遇到个很奇怪的问题,其中my.ini的basedir 的路径设置是下面这样的:
basedir d:\测试\test\mysql
但是在使用mysqld安装mysql服务时老是启动不了,报1067错误,后来查看window事件发现一个独特……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

java stream sorted排序 考虑null值

项目里使用到排序, java里没有像C# 里的linq,只有stream,查找stream.sorted源码看到有个
Comparator.nullsLast
然后看了一下实现,果然是能够处理null值的排序,如:minPriceList.stream().sorted(Comparator.comparing(l -> l.g……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

spring @EnableConfigurationProperties 实现原理

查看DataSourceAutoConfiguration源码,发现如下代码: Configuration ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) EnableConfigurationProperties(DataSourceProperties.class) Import({ DataSourcePoolMetadataProvidersCon……

蓝牙HID——将android设备变成蓝牙鼠标/触控板(BluetoothHidDevice)-编程知识网

postman请求https网址没有响应,但是用浏览器有响应,解决办法

遇到个问题:同一个get请求的url,postman请求https网址没有响应,但是用浏览器有响应
url是https开头的,查看错误描述里有一个SSL的选项: 然后根据描述关掉这个选项: 然后就没问题了,能正常请求及……