STM32与BLE蓝牙通信 Android APP配置(一)

2023-10-27

事物的难度远远低于对事物的恐惧

0.前言

最近完成了一个基于BLE蓝牙通信的简单APP,在这里记录下来,供大家参考希望能给需要的人解决疑惑。这个APP中一共是两个界面,第一个界面实现打开蓝牙、关闭蓝牙、扫描蓝牙和显示扫描的结果,通过选择扫描得到的蓝牙设备跳转到第二个界面,再次过程中与选择的蓝牙设备进行连接并获取服务。在第二个界面上显示连接的状态,以及可以发送数据和接收数据,这个代码只是实现了蓝牙传输数据的最基本功能,可以在这个基础上根据实际的需求进行更多的开发!

1.获取权限

首先第一步要获取蓝牙权限和位置权限,在 Androi 6.0(API 23) 以上不仅需要静态添加权限(在 AndroidMainfest.xml 中配置),而且还需要在程序中动态的获取权限,以下是部分需要动态获取的权限。

所属权限组 权限
日历 READ_CALENDAR
日历 WRITE_CALENDAR
相机 CAMERA
联系人 READ_CONTACTS
联系人 WRITE_CONTACTS
联系人 GET_CONTACTS
位置 ACCESS_FINE_LOCATION
位置 ACCESS_COARSE_LOCATION
麦克风 RECORD_AUDIO
电话 READ_PHONE_STATE
电话 CALL_PHONE
电话 READ_CALL_LOG
电话 WRITE_CALL_LOG
电话 ADD_VOICEMAIL
电话 USE_SIP
电话 PROCESS_OUTGOING_CALLS
传感器 BODY_SENSORS
短信 SEND_SMS
短信 RECEIVE_SMS
短信 READ_SMS
短信 RECEICE_WAP_PUSH
短信 RECEIVE_MMS
短信 READ_EXTERNAL_STORAGE
短信 WRITE_EXTERNAL_STORAGE

注意:如果应用程序请求在 AndroidManifest.xml 中列出的危险权限,并且应用程序已经在同一权限组中具有另一个危险权限,系统会立即授予权限,而不会与用户进行任何交互,。例如,如果一个应用程序先前已经请求并被授予 ACCESS_FINE_LOCATION 权限,然后它请求 ACCESS_COARSE_LOCATION (同属于位置一组),系统会立即授予该权限,不会再弹出权限授予询问的对话框。

在本文中使用的是 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION危险权限和BLUETOOTHBLUETOOTH_ADMIN 普通权限,首先需要在AndroidMainfest.xml文件中静态添加权限:

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

其中BLUETOOTH是蓝牙的普通权限,如果需要对蓝牙进行操作还需要蓝牙的高级权限BLUETOOTH_ADMIN,因为这两个权限属于普通权限只需要静态添加即可,剩下两个权限属于危险权限需要动态添加。

2.动态添加权限

首先,先确定是否在AndroidMainfest.xml文件中静态添加了这个权限,若已经添加可以继续进行动态添加,否则无法进行动态添加。
先进行静态添加(模板)

<uses-permission android:name="android.permission.上表的权限字符" />

然后进行动态添加(模板)

public void requestPower() {
    //判断是否已经赋予权限
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.上表权限字符)
                != PackageManager.PERMISSION_GRANTED) {
           //如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.上表权限字符)) {//这里可以写个对话框之类的项向用户解释为什么要申请权限,并在对话框的确认键后续再次申请权限.它在用户选择"不再询问"的情况下返回false
            } else {
            //申请权限,字符串数组内是一个或多个要申请的权限,1是申请权限结果的返回参数,在onRequestPermissionsResult可以得知申请结果
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.上表权限字符,}, 1);
            }
        }
    }

注意,因为判断语句,其实实际上当用户拒绝一次权限申请后,再次调用方法,不会再出现申请权限对话框。

3.本文使用的动态申请权限方法

先静态添加

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

然后动态添加

public void requestPower() {
    //判断是否已经赋予权限
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        //如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
            //这里可以写个对话框之类的项向用户解释为什么要申请权限,并在对话框的确认键后续再次申请权限.它在用户选择"不再询问"的情况下返回false
        } else {
            //申请权限,字符串数组内是一个或多个要申请的权限,1是申请权限结果的返回参数,在onRequestPermissionsResult可以得知申请结果
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
        }
    }
}

注意:这里将获取动态权限的方法封装在一个方法里面,需要在 onCreat 方法中进行调用,才能获取权限

4.初始化界面

在第一个界面上有三个按钮(开启蓝牙、关闭蓝牙、扫描蓝牙)和一个Spinner控件(用来显示扫描到的蓝牙设备),对其进行初始化,并设置相应的点击事件,看代码:

private final static String TAG = "hello";
private Button btn_Search, btn_On, btn_Off;
private Spinner mSpinner;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;

//初始化界面
public void initView() {

    bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    bluetoothAdapter = bluetoothManager.getAdapter();
    bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

    btn_Search = findViewById(R.id.btn_Search);		//扫描蓝牙按钮
    btn_On = findViewById(R.id.btn_On);				//开启蓝牙按钮
    btn_Off = findViewById(R.id.btn_Off);			//关闭蓝牙按钮
    mSpinner = findViewById(R.id.spinner);			//显示蓝牙设备的Spinne控件

    btn_Search.setOnClickListener(this);
    btn_On.setOnClickListener(this);
    btn_Off.setOnClickListener(this);
}

上面的代码完成了第一个界面里面控件的初始化和设置点击事件,其中的部分代码:

private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;

bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

是完成蓝牙的打开、关闭和扫描的操作,在下文将详细的介绍。

5.打开BLE蓝牙

添加完各种权限之后,接下来就需要对BLE蓝牙进行操作,首先是打开APP的时候需要打开蓝牙,先上代码:

private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();

先定义BluetoothManager 类(蓝牙管理器)和BluetoothAdapter类(蓝牙适配器)的对象,对象bluetoothManager通过getSystemService(Context.BLUETOOTH_SERVICE)方法获取,对象bluetoothAdapter通过bluetoothManager.getAdapter()方法获取,如果设备支持蓝牙则bluetoothAdapter不为null,否则为null。接下来就可以开启蓝牙了,上代码

public void turnOnBle() {
    if (bluetoothAdapter != null) {
        if (bluetoothAdapter.isEnabled() == false) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, 11);
        } else {
            Toast.makeText(this, "蓝牙已经打开了", Toast.LENGTH_SHORT).show();
        }
    } else {
        Toast.makeText(this, "该设备不支持蓝牙", Toast.LENGTH_SHORT).show();
    }
}

首先判断bluetoothAdapter不为null才能继续往下操作,否则说明该设备不支持蓝牙,然后通过bluetoothAdapter.isEnabled()方法判断该设备当前蓝牙的状态,若返回的是true则说明该设备当前蓝牙处于开启的状态,并通过Toast(弹窗)提示用户,若返回的是false则说明该设备当前蓝牙处于关闭的状态,然后通过Intent方法实现开启蓝牙,先创建一个Intent对象,接着用startActivityForResult(intent, 11)方法实现intent的传递,其中第一个参数是Intent类对象,第二个是一个Code可以自己定义,后续可以在回调函数中判断蓝牙是否成功的开启,在本文中没有进行这个回调方法的代码编写,感兴趣的可以自己实现一下。

6.关闭蓝牙

当打开蓝牙设备之后,我们还可以通过手动完成关闭蓝牙的操作,先看代码

public void turnOffBle() {
        if (bluetoothAdapter.isEnabled()) {
            bluetoothAdapter.disable();
        } else {
            Toast.makeText(this, "蓝牙已经关闭了", Toast.LENGTH_SHORT).show();
        }
    }

首先通过bluetoothAdapter.isEnabled()方法得到当前蓝牙的状态,只有当蓝牙处于开启的状态的时候才进行关闭的操作,用bluetoothAdapter.disable()方法实现蓝牙的关闭,否则提示用户当前蓝牙处于关闭的状态,无需进行关闭的操作。

7.扫描蓝牙

开始搜索代码
完成了蓝牙打开和关闭的操作之后,接下来要是实现获取周围蓝牙设备的操作,即蓝牙的扫描。刚开始本来想使用BluetoothAdapter.startLeScan(BluetoothAdapter.LeScanCallback)进行扫描,但是发现该方法提示已经过时,不建议使用,后来查阅资料后发现建议使用BluetoothLeScanner.startScan(ScanCallback)进行扫描,先看代码:

private Handler mHanler = new Handler();
private BluetoothLeScanner bluetoothLeScanner;
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

//开启蓝牙扫描
public void searchBle() {
    if (bluetoothAdapter.isEnabled()) {
        mHanler.postDelayed(myRunnable, 5000);
        btn_Search.setText("停止搜索");
        bluetoothLeScanner.startScan(scanCallback);
    } else {
        Toast.makeText(MainActivity.this, "请打开蓝牙", Toast.LENGTH_SHORT).show();
    }

}

//延时停止搜索线程
Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        bluetoothLeScanner.stopScan(scanCallback);
        btn_Search.setText("搜索设备");
        setBLESpinner();
    }
};

在开始扫描之前需要得到一个BluetoothLeScanner类的对象,然后在初始化函数中通过bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner()进行初始化,然后进行扫描蓝牙的操作。
在开启蓝牙扫描的代码中,同样首先是通过bluetoothAdapter.isEnabled()判断蓝牙当前的状态,若返回的是false则说明当前蓝牙没有打开,提示用户请打开蓝牙;若返回的是true则开启蓝牙扫描,首先是mHanler.postDelayed(myRunnable, 5000)设置一个延时的异步线程,即在5秒后执行bluetoothLeScanner.stopScan(scanCallback)关闭蓝牙的扫描,其中参数scanCallback是蓝牙扫描的回调函数,在回调函数中记录扫描得到的设备,在下文将详细的介绍。
然后bluetoothLeScanner.startScan(scanCallback)开启蓝牙的扫描,并将扫描蓝牙的按钮的文字设置成“停止搜索” btn_Search.setText("停止搜索"),扫描5秒,时间到了之后停止扫描,并将扫描蓝牙按钮的文字设置成“搜索设备”,这里更改按钮文字的目的是当用户点击了按钮,根据按钮上的文字选择执行不同的函数,如果显示的文字是“搜索设备”则执行searchBle()函数进行搜索,如果显示的文字是“停止搜索”则执行disSearchBle() 停止搜索,该函数将在下文进行详细的介绍。
在停止搜索的代码中的 setBLESpinner()函数是更新Spinner控件中的内容,即显示搜索到的设备。

停止搜索和回调函数代码

private List<BluetoothDevice> listdevices = new ArrayList<BluetoothDevice>();	//存放扫描到的设备
private List<String> listdevicename = new ArrayList<String>();					//存放扫描到的设备名字

//停止搜索
public void disSearchBle() {
    if (bluetoothLeScanner != null) {
        mHanler.removeCallbacks(myRunnable);
        bluetoothLeScanner.stopScan(scanCallback);
        btn_Search.setText("搜索设备");
        setBLESpinner();
    }

}

//扫描蓝牙设备的回调
public ScanCallback scanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, final ScanResult result) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                BluetoothDevice device = result.getDevice();
                String str = device.getName();
                if ((listdevicename.indexOf(str) == -1) && (device.getName() != null)) {
                    listdevices.add(device);
                    listdevicename.add(str);
                    Log.i(TAG, str);
                }
            }
        });
    }
    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
    }
};

上面的代码是停止扫描和扫描蓝牙的回调函数,在上文中我们设置了当用户点击了搜索设备的按钮,5秒之后才会停止搜索,这里我们添加了停止搜索的实现函数,在搜索的过程中用户可以点击停止搜索的按钮(和搜索设备是同一个按钮,只是显示的文字不同)停止搜索设备,当点击了这个按钮首先执行mHanler.removeCallbacks(myRunnable)移除我们之前设置的5秒后执行的线程,然后执行bluetoothLeScanner.stopScan(scanCallback)停止搜索,执行btn_Search.setText("搜索设备")将按钮显示的文字设置成“搜索设备”,最后执行setBLESpinner()更新Spinner控件的内容。
在扫描蓝牙的回调函数中,当设备正在搜索蓝牙设备的时候,每当搜索到一个设备就会执行一次public void onScanResult(int callbackType, final ScanResult result)方法,在这个方法中首先得到扫描到的设备并获取该设备的名字,然后判断名字是否为null,是否已经保存了,如果上述两个条件都没有满足,则将会把该设备存入到listdevicename集合中,将该设备的名字存入到listdevicename集合中,这两个集合时全局变量,后续会在setBLESpinner()方法中使用,实现显示扫描得到的设备。

8.显示扫描得到的蓝牙设备

在本文中是将扫描得到的蓝牙设备放到一个Spinner控件中进行显示,上文中我们已经得到了周围蓝牙设备的名字和设备,分别存在两个集合中,代码如下:

private Spinner mSpinner;
private ArrayAdapter<String> ListAdapter;

mSpinner = findViewById(R.id.spinner);			//显示蓝牙设备的Spinne控件

//将扫描到的设备添加到Spinner控件中
public void setBLESpinner() {
    mSpinner.setOnItemSelectedListener(this);
    ListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, listdevicename);
    ListAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mSpinner.setAdapter(ListAdapter);

}

首先为Spinner控件中的每一项设置选中监听,选中其中的某一项的时候会将这个蓝牙设备的名字和地址通过Intent传递给第二个界面,从而实现连接、传输数据等,这个将在下文进行详细的说明。然后将listdevicename中存放的蓝牙设备名字放入Spinner控件中进行显示,但是不能直接放入,中间需要一个ArrayAdapter类的对象进行过渡一下才能正常的显示。

9.选择需要连接的蓝牙设备

完成显示之后,我们就可以选择我们需要连接的蓝牙设备,先看代码:

public static String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
public static String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

//选择蓝牙设备
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

    String Item = parent.getSelectedItem().toString();
    if (Item != "<BLE List>") {
        for (BluetoothDevice device : listdevices) {
            if (Item.equals(device.getName())) {
                Intent intent = new Intent(this, BLECommunicationActivity.class);
                intent.putExtra(EXTRAS_DEVICE_NAME, device.getName());
                intent.putExtra(EXTRAS_DEVICE_ADDRESS, device.getAddress());
                startActivity(intent);
            }
        }
    }
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}

在上文的mSpinner.setOnItemSelectedListener(this)我们设置了选中监听事件,当我们选择一个蓝牙设备的时候就执行上面的代码,首先获得我们选中的Item即蓝牙设备的名字,如果不是我们添加的字符串“<BLE List>”,然后开始遍历我们之前保存的扫描到的蓝牙设备的集合,根据名字找到对应的蓝牙设备,然后将该设备的名字和地址传递给第二个界面,并跳转到第二个界面中,进行后续的操作。

10.按钮点击监听事件

下面是按钮的点击监听事件的代码:

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn_On:		//打开蓝牙按钮
            turnOnBle();
            break;
        case R.id.btn_Off:		//关闭蓝牙按钮
            turnOffBle();
            break;
        case R.id.btn_Search:	//搜索蓝牙设备按钮
            if (btn_Search.getText().equals("搜索设备")) {
            	listdevices.clear();
                listdevicename.clear();
                listdevicename.add("<BLE List>");
                searchBle();
            } else if (btn_Search.getText().equals("停止搜索")) {
                disSearchBle();
            }
            break;
    }
}

当点击搜索蓝牙设备按钮,会判断按钮显示的文字,根据不同的文字执行相应的方法,其中

listdevices.clear();
istdevicename.clear();
listdevicename.add("<BLE List>");

这两行代码作用是在每一次搜索设备之前需要将存放蓝牙设备和名字两个集合清空,存放新扫描到的设备和名字。由于Spinner控件会自动选中第一条的内容,为了防止发生误连接的现象,我们使用listdevicename.add("<BLE List>")这条语句确保每一次扫描结束之后存放蓝牙设备名字的集合第一条是我们自己定义的字符串,然后在public void onItemSelected(AdapterView<?> parent, View view, int position, long id)方法中判断选中的是否是我们自定义的字符串,如果不是才进行跳转和连接。

这是第一个界面上代码,只实现了一部分的功能,接下来的功能将在 Android BLE蓝牙配置全流程(二) 附APP源码 中进行说明,如果需要请前往查看。如有任何问题可以留言或者私信我,看到会及时回复,上述的内容如果有误,还请各位大佬不吝赐教,在此,祝愿各位学习顺利,前途无限~

Android BLE蓝牙(HC-08)源码免费获取方式
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32与BLE蓝牙通信 Android APP配置(一) 的相关文章

随机推荐

  • epic如何修改着色器缓存路径

    1 我们先找到缓存路径存放位置 C Users Administrator AppData Local UnrealEngine Common DerivedDataCache 可以自行删除 2 我们修改缓存位置 1 找到文件 E Epic
  • 版本管理可视化工具GitKraKe安装

    资源下载地址 https download csdn net download u012796085 87953404 1 解压后安装GitKrakenSetup 7 5 5 exe 2 命令窗口进入GitKraken存放目录 分别执行以下
  • C语言:用C语言实现进制转换

    这两天做题遇见了进制转换的问题 在网上看了他人的想法后自己的一些实践 目录 关于进制转换的问题和思考 1 将十进制以下的数据转换为十进制 2 将十进制的数据转换为十进制以上 3 十六进制转换成十进制 关于进制转换的问题和思考 1 高于十进制
  • 【知识学习】Git:如何利用Git实现Matlab代码版本管理

    目录 1 版本控制 1 1 版本控制是什么 1 2 常见的版本控制器 2 Git环境配置 2 1 软件下载 2 2 Git配置 3 Git 基本理论 4 Git项目搭建 4 1 本地搭建仓库 4 2 使用码云 Github 5 Git分支常
  • Arduino教程四——u8g2库OLED屏幕显示

    1 功能 u8g2库OLED屏幕显示英文 OLED 0 96寸 128X64 对于这几个参数进行说明 0 96指的是屏幕的显示尺寸0 96inch 128 64指的是屏幕的分辨率为128 64 128列64行 u8g2 屏幕显示 固定搭配
  • Arthas(阿尔萨斯) 的安装与使用

    arthas官方文档 https arthas aliyun com doc index html点击此处进入 是Alibaba开源的Java诊断工具 深受开发者喜爱 在线排查问题 无需重启 动态跟踪Java代码 实时监控JVM状态 Art
  • 11月20日 如何在场景开启Debug,自定义AI任务,EQS,创建自己的环境任务,使用Pawn环境检测来检测周围的环境,让AI动作更顺滑(动画混合

    如何在场景开启Debug 按F1开启线框模式 按 打开Debug数据栏 按数字键3打开EQSDEBUG 开启距离场debug 自定义AI任务 创建BTTask RangeAttack h Fill out your copyright no
  • 使用msfconsole拿到win2008 R2的shell并进行维权二(权限维持)

    声明 本博文仅供学习交流使用 不可用于任何违法犯罪活动 由此带来的任何法律后果 本人概不承担 使用msfconsole拿到win2008 R2的shell并进行维权二 权限维持 四 维权后门 4 1查询服务器信息 4 1 1查看当前用户以及
  • linux挂载磁盘超时问题解决记录

    上周公司一台k8节点nfs挂载超时 同事反映 这个盘挂载是有问题 开始各种排查 都没问题 最后排查到nfs server节点iptables规则限制所致 记录一下这次的排查过程 1 server端排查 看配置 检查 showmount e
  • 拆机小白的联想小新I1000内存升级过程

    终于有时间升级一下我的4GB内存的联想小新I1000了 原想着如果可以扩展的话 内存升到最高 硬盘加装一个不用太大的SSD硬盘 把系统就装在SSD上面 机械就只作为一个存储的硬盘 可惜联想小新I1000不支持呀 内存条和硬盘都只是一个卡槽
  • 三、OpenCV图像的预处理——二值化与自适应阈值

    教程汇总 python基础入门系列 定义 图像的二值化 就是将图像上的像素点的灰度值设置为0或255 也就是将整个图像呈现出明显的只有黑和白的视觉效果 一幅图像包括目标物体 背景还有噪声 要想从多值的数字图像中直接提取出目标物体 常用的方法
  • 矩阵求秩

    矩阵的秩怎么计算 这个问题一下子我居然不知道怎么下手 虽然本科的时候学过线性代数 但是好久不用 很多东西都忘了 今天略微梳理一下吧 最简单直观的方法 化成行最简形 或行阶梯形 然后数一下非零行数 例如 将矩阵做初等行变换后 非零行的个数叫行
  • Python 实现多个类别数据的直方图区间层面累积堆叠

    Python 实现多个类别数据的直方图区间层面累积堆叠 数据可视化是数据科学中不可缺少的一部分 它能够帮助我们更好地理解和分析数据 直方图是一种常用的数据可视化方法 它可以将数据分布情况以柱状图的形式展示出来 如果存在多个类别的数据 我们可
  • mysql convert函数 解决读取double为科学计数法问题

    convert顾名思义就是转化 cast差不多 MySQL CONVERT 函数 参考手册 为什么需要这个函数 mysql是弱类型的 where stringcol 1 and intcol 1 都行 会自动转化 那我为什么还要呢 mysq
  • 错误:编码GBK的不可映射字符解决方案(亲测有效)

    CMD编译运行JAVA程序出现的错误 原要求 这次作业要求用命令行输出 但是java命令后显示的是中文乱码 也有的出现错误 编码GBK的不可映射字符 原因 引用 由于JDK是国际版的 我们在用javac exe编译时 编译程序首先会获得我们
  • 插入mysql,Cause: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation:Data truncation: Data too long

    插入mysql 报错 Error updating database Cause com mysql cj jdbc exceptions MysqlDataTruncation Data truncation Data too long
  • Legal or Not HDU - 3342 拓扑排序 判环

    这道题的意思是 给你n个点 m行关系数据 左 gt 右 判断有无环的出现 方法 直接拓扑排序 如果能正常排序完 这个就是无环的有向图DAG 如果不能 在拓扑排序的过程中有些点的入度经过去边操作之后一直不为零 就是有环的存在 include
  • GPT4.0一句话实现各类图表制作,让数据可视化变得更简单!类图、流程图、ER图.....

    不知道大家有没有被ER建模工具复杂的操作按钮给困扰过 在作者学习ER建模时 曾希望能直接画出类图 但最终还是不得不学习繁琐的操作流程 然而 随着GPT的出现 AI现在也可以绘制UML图了 今天要向大家分享一个AI工具 它能够借助强大的GPT
  • STM32多中断模式

    1 基本概念 ARM Coetex M3内核共支持256个中断 其中16个内部中断 240个外部中断和可编程的256级中断优先级的设置 STM32目前支持的中断共84个 16个内部 68个外部 还有16级可编程的中断优先级的设置 仅使用中断
  • STM32与BLE蓝牙通信 Android APP配置(一)

    事物的难度远远低于对事物的恐惧 0 前言 最近完成了一个基于BLE蓝牙通信的简单APP 在这里记录下来 供大家参考希望能给需要的人解决疑惑 这个APP中一共是两个界面 第一个界面实现打开蓝牙 关闭蓝牙 扫描蓝牙和显示扫描的结果 通过选择扫描