Android 蓝牙开发 入门级(史上最全)

第一节:了解蓝牙

1. 蓝牙基础


蓝牙是一种无线技术标准,用于短距离内的数据交换

在Android设备上,蓝牙技术允许进行设备发现、配对、连接以及数据传输

技术始于爱立信公司 1994 方案,它是研究在移动电话和其他配件间进行低功耗、低成本无线通信连接的方法。发明者希望为设备间的通讯创造一组统一规则(标准化协议)用来解决用户间相互不兼容的移动电子设备。

2.蓝牙分类 

3. 蓝牙权限


在开始开发之前,需要在Android项目的AndroidManifest.xml文件中声明蓝牙相关的权限。对于基本的蓝牙操作,需要以下权限:

<!-- 允许应用程序连接到配对的蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH" /><!-- 允许应用程序发现和配对蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

从Android 6.0(API级别23)开始,蓝牙扫描需要定位权限,因为蓝牙扫描可以被用来粗略地定位用户。

因此,如果你的应用目标是API级别23或更高,并且需要进行蓝牙扫描,你还需要添加:

<!-- 用于Android 6.0及以上版本的蓝牙扫描 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

从Android 10(API级别29)开始,ACCESS_FINE_LOCATION权限被分为更细粒度的权限,如果你的应用需要在后台访问位置,还需要声明:

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
4. 用户权限请求


对于运行时权限(如位置权限),你需要在应用运行时请求用户授权。这通常在你的应用尝试进行蓝牙扫描之前完成

这是请求权限的一个基本示例:

(1)安卓原生检查权限和申请权限

if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(thisActivity,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
}

在这里,MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION是一个应用定义的整数常量。将在回调方法onRequestPermissionsResult中使用它来接收请求结果

(2)第三方库xxPermissions

if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {// 检查蓝牙连接权限,如果没有,则请求权限getPermission(Manifest.permission.BLUETOOTH_CONNECT);return;}
// 获取权限的方法private void getPermission(String permission) {// 请求权限XXPermissions.with(getContext()).permission(permission) // 申请单个权限.request(new OnPermissionCallback() {@Overridepublic void onGranted(@NonNull List<String> permissions, boolean allGranted) {if (!allGranted) {// 如果未全部授予权限,则显示提示信息Toast.makeText(getContext(), "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show();Log.d("TAG", "获取部分权限成功");return;}// 权限全部成功授予后的操作Log.d("TAG", "获取权限成功");}@Overridepublic void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {if (doNotAskAgain) {// 如果用户选择不再询问,则提示用户手动开启权限,并跳转到应用设置页面Toast.makeText(getContext(), "被永久拒绝授权,请手动授予权限", Toast.LENGTH_SHORT).show();Log.d("TAG", "被永久拒绝授权,请手动授予权限");XXPermissions.startPermissionActivity(getContext(), permissions);} else {// 如果权限被拒绝但没有选择不再询问,则显示失败提示Toast.makeText(getContext(), "获取权限失败", Toast.LENGTH_SHORT).show();Log.d("TAG", "获取权限失败");}}});}

经典蓝牙BT开发流程

备注:后面几节讲解的都是以BLE为例子

这就是开始使用蓝牙开发所需了解的基础知识。

我们继续下一节。

第二节:启用蓝牙和检查设备支持


在这一节中,我们将学习如何在你的应用中启用蓝牙功能,并检查用户的设备是否支持蓝牙。

1. 获取蓝牙适配器


首先,你需要获取BluetoothAdapter的实例,它是所有蓝牙操作的入口点。

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

如果getDefaultAdapter()返回null,则表示设备不支持蓝牙

2. 检查蓝牙是否启用


接下来,检查蓝牙是否已经启用。使用BluetoothAdapterisEnabled()方法可以检查蓝牙是否启用:

if (bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {// 蓝牙未启用
}

bluetoothAdapter != null 说明设备支持蓝牙

!bluetoothAdapter.isEnabled() 说明蓝牙没启用

3. 请求用户启用蓝牙


如果蓝牙未启用,你可以请求用户启用它

这可以通过启动一个Intent来请求用户启用蓝牙,而不是直接调用enable()方法,因为enable()方法无需用户同意即可启用蓝牙,这可能不是最佳用户体验

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

在这里,REQUEST_ENABLE_BT是应用定义的整数请求码,用于在你的Activity的onActivityResult回调中接收结果。

4. onActivityResult回调

当用户响应启用蓝牙的请求时,系统会调用你的Activity的onActivityResult方法。

你可以在这里检查请求的结果:

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_ENABLE_BT) {if (resultCode == RESULT_OK) {// 用户启用了蓝牙} else {// 用户未启用蓝牙}}}

这就是如何在你的应用中启用蓝牙并检查设备是否支持蓝牙的基本步骤,理解这些步骤是进行蓝牙开发的基础。

第三节:发现设备和获取已配对设备


在这一节中,我们将学习如何发现附近的蓝牙设备以及如何获取已经与你的设备配对的蓝牙设备列表。

1. 获取已配对设备

你的Android设备可能已经与一些蓝牙设备配对过了。

要获取这些已配对的设备列表,你可以使用BluetoothAdapter的getBondedDevices()方法:

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {// 至少有一个已配对设备for (BluetoothDevice device : pairedDevices) {String deviceName = device.getName();String deviceHardwareAddress = device.getAddress(); // MAC地址Log.d("TAG", "蓝牙设备名称:"+deviceName);Log.d("TAG", "蓝牙设备地址:"+deviceHardwareAddress);}
}
2. 发现新设备


要发现附近的蓝牙设备,你需要调用BluetoothAdapter的startDiscovery()方法。这个方法是异步的,发现过程通常会持续12秒

需要注册一个BroadcastReceiver来监听BluetoothDevice.ACTION_FOUND广播,这个广播会在发现新设备时发送。

// 注册广播接收器以监听发现的设备
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, filter);// 发现设备
if (bluetoothAdapter.startDiscovery()) {// 发现过程成功启动
}

疑问:

BluetoothDevice.ACTION_FOUND 是一个什么值?

解答:

  • BluetoothDevice.ACTION_FOUND 是一个字符串常量,用于在广播中标识已找到一个蓝牙设备,值是"android.bluetooth.device.action.FOUND"。
  • 当你的应用调用BluetoothAdapter的startDiscovery()方法开始扫描附近的蓝牙设备时,每发现一个设备,系统就会发送这个ACTION_FOUND的广播
  • 你的应用可以通过注册一个BroadcastReceiver来监听这个广播,以便获取每个发现的蓝牙设备的信息。

在你的BroadcastReceiver中,你可以获取发现的设备信息:

private final BroadcastReceiver receiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 从Intent中获取发现的BluetoothDeviceBluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);String deviceName = device.getName();String deviceHardwareAddress = device.getAddress(); // MAC地址}}
};

疑问:

action有几种?

解答:

- BluetoothDevice.ACTION_ACL_CONNECTED:当与远程设备建立低级别(ACL)连接时发送的广播。

- BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED:当系统要断开与远程设备的低级别(ACL)连接时发送的广播。

- BluetoothDevice.ACTION_ACL_DISCONNECTED:当与远程设备的低级别(ACL)连接断开时发送的广播。

- BluetoothDevice.ACTION_BOND_STATE_CHANGED:当远程设备的配对状态发生变化时发送的广播。

- BluetoothDevice.ACTION_NAME_CHANGED:当远程设备的名称发生变化时发送的广播。

- BluetoothDevice.ACTION_PAIRING_REQUEST:在需要配对时发送的广播,通常是因为配对过程需要输入PIN码确认配对

- BluetoothAdapter.ACTION_DISCOVERY_STARTED:当设备发现开始时发送的广播。

- BluetoothAdapter.ACTION_DISCOVERY_FINISHED:当设备发现结束时发送的广播。

- BluetoothAdapter.ACTION_STATE_CHANGED:当蓝牙适配器的状态发生变化时发送的广播,例如蓝牙被开启或关闭。

 

监听这些action可以让你的应用更好地与蓝牙设备交互。

例如,

通过监听ACTION_BOND_STATE_CHANGED,你的应用可以知道何时一个设备已经成功配对,或者配对失败。

通过监听ACTION_ACL_CONNECTED和ACTION_ACL_DISCONNECTED,你的应用可以知道何时设备连接或断开连接

可以在这些节点做一些处理。

3. 停止发现


由于发现过程会消耗大量资源,一旦你找到了你感兴趣的设备,或者你想停止扫描,你应该使用cancelDiscovery()方法来停止发现过程:

bluetoothAdapter.cancelDiscovery();

4. 清理


记得在不再需要时,unregisterReceiver(receiver)注销你的BroadcastReceiver,例如,在你的Activity或Fragment的onDestroy()方法中:

@Overrideprotected void onDestroy() {super.onDestroy();// 确保我们不再监听广播unregisterReceiver(receiver);}

这就是如何发现新设备以及获取已配对设备的基本步骤。

理解这些步骤对于开发能够与其他蓝牙设备交互的应用是非常重要的。

当你准备好了,请告诉我,以便我们继续下一节。

第四节:连接到蓝牙设备


在这一节中,我们将学习如何使用BluetoothSocket来连接到一个蓝牙设备。连接到设备是进行数据交换的前提。

1. 创建BluetoothSocket


要与远程蓝牙设备建立连接,首先需要创建一个BluetoothSocket

BluetoothSocket代表了蓝牙套接字的接口,它是两个设备之间通信的通道。

对于大多数用途,你将使用createRfcommSocketToServiceRecord(UUID)方法来创建BluetoothSocket。

这个方法需要一个UUID参数,这个UUID必须是你想要连接的远程设备上的蓝牙服务的UUID。UUID代表通用唯一标识符,用于唯一标识你的应用的蓝牙服务。

BluetoothDevice device = ... // 获取一个BluetoothDevice对象UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // 示例UUIDBluetoothSocket socket = device.createRfcommSocketToServiceRecord(MY_UUID);

有几个概念:

  • 服务UUID
  • 特征UUID
  • 描述UUID
  • 公共标准UUID

2. 连接到远程设备


创建BluetoothSocket后,你可以调用它的connect()方法来尝试与远程设备建立连接。

这个方法是阻塞调用直到连接成功或抛出异常)。

try {socket.connect();// 连接成功,可以开始进行数据交换} catch (IOException e) {// 连接失败e.printStackTrace();}

3. 数据交换


连接成功后,你可以通过BluetoothSocket的输入输出流来进行数据的读写。

try {InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();// 从InputStream读取数据byte[] buffer = new byte[1024];int bytes;bytes = inputStream.read(buffer);String receivedData = new String(buffer, 0, bytes);// 向OutputStream写入数据String dataToSend = "Hello, Bluetooth!";outputStream.write(dataToSend.getBytes());} catch (IOException e) {// 处理异常}

4. 断开连接


完成数据交换后,应该关闭BluetoothSocket来释放资源

try {socket.close();} catch (IOException e) {// 处理异常}

注意事项


- 连接过程可能会花费一些时间,建议在一个单独的线程中进行连接操作,以避免阻塞UI线程。
- 确保你的UUID与远程设备提供的服务UUID匹配。
- 在连接过程中和之后,要妥善处理异常。

疑问:

我听同事说发送数据还有片段发送,是怎么样的

解答:

在蓝牙通信中,由于蓝牙协议本身的限制以及不同设备的性能差异,发送大量数据时直接发送整个数据包可能会导致数据传输失败或者效率低下。

因此,将大数据分割成小片段(fragments)进行发送是一种常见的做法,这就是所谓的“片段发送”或“数据分片”。

1.数据分片的基本思想


数据分片的基本思想,是将大的数据包分割成多个较小的数据片段,然后逐个片段地发送这些数据。接收方收到所有片段后,再将它们重新组合成原始的数据包。

2.如何实现数据分片?


实现数据分片的一个简单方法是:

  • (1)确定片段大小:
    • 首先,你需要确定每个数据片段的大小。
      • 这个大小需要根据你的应用需求和蓝牙硬件的能力来决定。一个常见的做法是使用512字节作为每个片段的大小。
  • (2)分割数据
    • 将你的数据分割成多个片段。
      • 如果数据大小不是片段大小的整数倍,最后一个片段可能会更小。
  • (3)发送数据片段:
    • 逐个发送每个数据片段。
      • 在每个片段发送之后,可能需要等待接收方的确认,再发送下一个片段
  • (4)接收方重组数据:
    • 接收方需要按照发送顺序接收并存储每个数据片段。
      • 一旦所有片段都被接收,接收方就可以将它们组合成原始的数据包。
示例代码


以下是一个简化的示例,展示了如何将一个大的字节数组分割成多个片段并发送:

OutputStream outputStream = bluetoothSocket.getOutputStream();byte[] largeData = ...; // 假设这是要发送的大数据int chunkSize = 512; // 片段大小int offset = 0;while (offset < largeData.length) {// 计算剩余数据大小int remaining = largeData.length - offset;int byteCount = Math.min(remaining, chunkSize); // 这次发送的字节数// 发送数据片段outputStream.write(largeData, offset, byteCount);outputStream.flush(); // 确保数据被发送offset += byteCount; // 更新偏移量// 可选:等待接收方的确认}// 数据发送完毕
注意事项


- 数据分片会增加通信的复杂度,特别是在需要确保数据完整性和顺序的情况下。
- 接收方需要有相应的逻辑来处理接收到的数据片段,包括片段的重组和可能的错误处理。
- 在实际应用中,可能还需要考虑如何处理数据传输中的错误,例如重传丢失的片段。

通过数据分片,你可以更可靠地通过蓝牙发送大量数据,同时还可以提高数据传输的效率。



总之,这就是如何连接到蓝牙设备并进行数据交换的基本步骤。理解这些步骤对于开发能够与其他蓝牙设备交互的应用是非常重要的。

当你准备好了,请告诉我,以便我们继续下一节。

第五节:管理蓝牙连接


在这一节中,我们将讨论如何管理蓝牙连接,包括:数据的接收处理连接的维护断开连接的正确方式。

1. 接收数据


在前一节中,我们讨论了如何发送数据。接收数据同样重要,通常涉及到从BluetoothSocket的输入流中读取数据。

由于数据接收是阻塞操作,推荐在单独的线程中进行读取操作,以避免阻塞UI线程。

class ConnectedThread extends Thread {private final BluetoothSocket socket;private final InputStream inputStream;private final OutputStream outputStream;public ConnectedThread(BluetoothSocket socket) {this.socket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;try {tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch (IOException e) {e.printStackTrace();}inputStream = tmpIn;outputStream = tmpOut;}public void run() {byte[] buffer = new byte[1024];  // 缓冲区int bytes; // 读取的字节数while (true) {try {// 从InputStream读取数据bytes = inputStream.read(buffer);String receivedData = new String(buffer, 0, bytes);// 处理接收到的数据} catch (IOException e) {// 连接丢失时的处理break;}}}// 调用此方法从外部发送数据public void write(byte[] bytes) {try {outputStream.write(bytes);} catch (IOException e) {e.printStackTrace();}}// 调用此方法关闭连接public void cancel() {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}

2. 维护连接


维护蓝牙连接的关键,在于处理各种可能导致连接中断的情况。

例如,设备的距离过远干扰设备关闭

ConnectedThreadrun方法中,如果inputStream.read(buffer)抛出IOException,通常意味着连接已经丢失,此时应当关闭socket并尝试重新连接或更新UI。

3. 断开连接


当不再需要蓝牙连接时,正确的断开连接是很重要的。这包括关闭BluetoothSocket,以及与之关联的输入输出流。

ConnectedThread类中,我们提供了一个cancel方法来做这件事。调用cancel可以安全地关闭socket并终止线程

public void cancel() {try {socket.close();} catch (IOException e) {e.printStackTrace();}}

4. 重连策略


在实际应用中,可能需要实现自动重连的逻辑。这通常涉及到在连接丢失后等待一段时间,然后尝试重新连接到设备

重连策略的实现会根据具体的应用需求而有所不同,但基本思路是在捕获到连接丢失的异常后,延迟一段时间后再次尝试连接

这就是关于管理蓝牙连接的基本知识。通过正确地管理连接,你的应用可以更加稳定,为用户提供更好的体验。

当你准备好了,请告诉我,以便我们继续下一节。

第六节:蓝牙服务和特征


在这一节中,我们将探讨使用蓝牙低功耗(BLE时的高级概念,特别是蓝牙服务特征的概念。这些概念对于理解和开发BLE应用至关重要。

1. BLE简介


蓝牙低功耗(BLE),也称为蓝牙4.0 或 蓝牙Smart,是蓝牙技术的一个版本,专为低功耗设备设计,适用于需要长期运行在电池供电下的设备。与经典蓝牙相比,BLE在保持通信能力的同时,大大降低了能耗。

2. 服务和特征


在BLE中,数据交换是通过服务(Services)和特征(Characteristics)的概念来组织的。

- 服务(Service):

        服务是一组功能相关的特征的集合。每个服务都由一个唯一的UUID来标识。


- 特征(Characteristic):

        特征表示服务中的一个数据点,例如一个传感器的值或者一个配置参数。特征包含一个值以及0到多个描述符(Descriptors),用于描述特征的属性。特征也由UUID标识。

3. 发现服务和特征


当你的设备与一个BLE设备建立连接后,下一步通常是发现远程设备提供的服务和特征。这可以通过调用BluetoothGatt对象discoverServices()方法来完成。

服务发现完成后,你可以通过调用getServices()方法来获取服务列表,然后遍历这些服务以查找特定的服务和特征

@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {for (BluetoothGattService service : gatt.getServices()) {UUID serviceUUID = service.getUuid();// 检查是否是我们感兴趣的服务UUIDfor (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {UUID characteristicUUID = characteristic.getUuid();// 检查是否是我们感兴趣的特征UUID}}} else {Log.w(TAG, "onServicesDiscovered received: " + status);}}

4. 读写特征


一旦找到了感兴趣的特征,你可以读取特征的值或向其写入值。

- 读取特征值:

                调用BluetoothGattreadCharacteristic()方法。


写入特征值:

                首先设置特征的值,然后调用BluetoothGattwriteCharacteristic()方法。

// 读取特征值gatt.readCharacteristic(characteristic);// 写入特征值characteristic.setValue(value);gatt.writeCharacteristic(characteristic);

5. 订阅特征通知


对于一些特征,你可能希望当特征的值发生变化时得到通知

这可以通过对特征启用通知来实现 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE

这通常涉及到写入特征的一个相关描述符。

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);gatt.writeDescriptor(descriptor);

这就是BLE服务和特征的基本概念。

通过理解和使用这些概念,你可以开发出能够与BLE设备进行复杂交互的应用。

当你准备好了,请告诉我,以便我们继续下一节。

第七节:蓝牙安全性和最佳实践


在这一节中,我们将讨论蓝牙通信的安全性问题以及开发蓝牙应用时的一些最佳实践。

1. 蓝牙安全性概述


蓝牙技术包含多种安全特性,旨在保护通信不被未授权访问。这些安全特性包括配对过程中的认证和加密。然而,蓝牙设备和通信仍可能面临一些安全威胁,如中间人攻击(MITM)、身份欺骗等。因此,开发者在使用蓝牙技术时需要采取适当的安全措施。

2. 使用安全的配对模式


- 配对过程:

        在设备之间建立连接前,通常会进行配对过程,这个过程可以提供认证和加密。确保使用安全的配对模式,如Passkey Entry或Numeric Comparison,这些模式提供更高级别的安全性。


- 注意保护PIN码:

        如果你的应用需要用户输入PIN码来配对设备,确保这个过程是安全的,避免泄露PIN码。

3. 管理已配对设备


- 信任的设备:

                只允许已知和信任的设备连接。对于不再使用的设备,应从系统中删除其配对信息。
- 定期检查配对设备:应用应定期检查已配对设备列表,移除不再需要的设备。

4. 使用加密的数据传输


- 加密通信:

                确保数据传输过程中使用加密。对于BLE,一旦设备配对,数据传输通常是加密的。但是,开发者应验证加密确实被启用。


- 敏感信息处理:

                对于传输敏感信息,考虑在应用层再次加密数据,以增加安全性。

5. 最佳实践


- 最小权限原则:应用应仅请求完成其功能所必需的最小权限集合。
- 注意隐私问题:在处理用户数据和设备信息时,要注意用户隐私,遵守相关法律法规。
- 错误处理:妥善处理错误和异常,避免泄露敏感信息。
- 更新和维护:定期更新应用和蓝牙设备的固件,以修复已知的安全漏洞。

6. 性能优化


- 节能策略:

                蓝牙操作可能会消耗大量电量,特别是在进行设备发现时。合理安排发现过程,减少不必要的操作,可以帮助节省电量


- 连接管理:

                合理管理蓝牙连接,避免不必要的连接和断开,可以提高应用性能和用户体验

通过遵循这些安全性指南和最佳实践,你可以开发出既安全又高效的蓝牙应用。这些措施有助于保护用户数据,提高应用的稳定性和可靠性。

这标志着我们蓝牙开发基础教程的结束。如果你有任何问题,或者想要深入了解某个特定主题,请随时留言、点赞、加关注。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis面试问题纯享版

基础内容 1、简单介绍以下你了解的Redis 2、对比一下Redis和Memcache的异同&#xff1f; 3、为什么MySQL选用Redis作为缓存&#xff1f; 4、详细聊聊你对Redis各种数据类型的了解&#xff1f; 5、Redis中五种基本数据类型的底层数据结构是什么样的&#xff1f; Redis线程模型…

window mysql 安装出现的问题

1.安装到最后时&#xff0c;报错&#xff1a;authentication_string doesnt have a default value 解决办法&#xff1a; 1.不要关掉该页面&#xff0c;点击skip。 然后单击 back 回退到如下界面 2.去掉 Enable Strict Mode。 不要勾选 2. 最后一步&#xff1a;Start Servic…

一文读懂 Databend 的开放表格式引擎

本文介绍了 Databend 开放表格式引擎的支持情况&#xff0c;包括优势与不足、使用方法、与 Catalog 方案的对比。此外&#xff0c;还包含一个简单的 Workshop &#xff0c;介绍如何利用 Databend Cloud 分析位于对象存储中的 Delta Table 。 Databend 近期发布 Apache Iceberg …

小程序环形进度条爬坑

在做微信小程序的时候&#xff0c;发现用canvas做的环形进度条&#xff0c;在带滚动条的view里面显示有闪动、显示不全的问题&#xff0c;后面改成echart-weixin的pie图实现了&#xff0c;option配置如下 // 表示进度的百分比 var progressValue 70;option {series: [{type: …

深入解析Mybatis-Plus框架:简化Java持久层开发(八)

&#x1f340; 前言 博客地址&#xff1a; CSDN&#xff1a;https://blog.csdn.net/powerbiubiu &#x1f44b; 简介 本章节介绍如何通过Mybatis-Plus更新数据库中的数据。 本章节不需要前置准备&#xff0c;继续使用之前的测试类&#xff0c;数据库表进行操作。 &#x1f4…

Git 版本控制

Git 版本控制 1. About Version Control (关于版本控制)1.1. Local Version Control Systems (本地版本控制系统)1.2. Centralized Version Control Systems (集中化的版本控制系统)1.3. Distributed Version Control Systems (分布式版本控制系统) 2. 换行符的处理3. keyboard…

C# 由左上、右下两个坐标点计算矩形的长、宽以及两点的距离

一、计算长、宽 直接使用坐标点计算 // 定义矩形左上角和右下角的坐标 Point topLeft new Point(0, 0); Point bottomRight new Point(5, 10); // 计算矩形的长和宽 int width bottomRight.X - topLeft.X;//矩形宽度 int height bottomRight.Y - topLeft.Y;//矩形高度或是…

Vue中有哪些优化性能的方法?

Vue是一款流行的JavaScript框架&#xff0c;用于构建交互性强的Web应用程序。在前端开发中&#xff0c;性能优化是一个至关重要的方面&#xff0c;尤其是当应用程序规模变大时。Vue提供了许多优化性能的方法&#xff0c;可以帮助开发人员提升应用程序的性能&#xff0c;从而提升…

初学者如何使用QT新建一个包含UI界面的C++项目

文章目录 一、下载并安装QT51、下载安装包2、注册/登录账号3、安装qt6 二、新建QT Widget项目1、新建项目并且运行2、易错点&#xff1a;可能运行成功得到UI界面但是会报错&#xff08;原因是使用了中文路径&#xff09; 一、下载并安装QT5 1、下载安装包 进入下载网址 Windo…

链表习题-力扣oj (附加思路版)

LCR 140. 训练计划 IIhttps://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/ 给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号&#xff0c;请查找并返回倒数第 cnt 个训练项目编号。 思路&#xff1a;双指针&#xff0c;快指针先走cnt…

RabbitMQ队列

RabbitMQ队列 1、死信的概念 ​ 先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;字面意思可以这样理解&#xff0c;一般来说,producer将消息投递到broker或者直接到queue里了&#xff0c;consumer 从 queue取出消息进行消…

基于redis实现用户登陆

因为session有数据共享问题&#xff0c;不同tomcat服务器中的session不能共享&#xff0c;之后负载均衡就无法实现。所以我们用redis代替session。redis可以被多个tomcat服务器共享&#xff0c;redis基于内存。 之前的session可以看做登陆凭证&#xff0c;本次登陆凭证由sessi…

C语言指针总结(完结篇)

前言 这篇博客终于迎来了指针博客的大结局&#xff0c;本篇主要分析习题来回顾之前的指针总结的知识点&#xff0c;这篇博客的题有点绕&#xff0c;哈哈算是经典了 个人主页&#xff1a;小张同学zkf 若有问题 评论区见 感兴趣就关注一下吧 目录 1. sizeof和strlen的对比 1.1 …

TikTok矩阵获客软件的核心源代码是什么?

随着互联网的不断发展&#xff0c;社交媒体已成为企业获客的重要渠道之一&#xff0c;在众多的社交媒体平台中&#xff0c;TikTok凭借其庞大的用户群体和活跃的社交氛围&#xff0c;成为了众多企业竞相争夺的营销高地。 在这样的背景下&#xff0c;TikTok矩阵获客软件应运而生…

Linux Ubuntu部署SVN服务端结合内网穿透实现客户端公网访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

RISC-V架构学习资料整理

1、韦东山——D1S哪吒开发板的裸机代码仓库 https://github.com/bigmagic123/d1-nezha-baremeta 2、melis系统移植到D1S https://blog.51cto.com/u_13800193/6268813 3、韦东山的gitee仓库 https://gitee.com/weidongshan 4、D1S编译工具链下载 https://github.com/Tina-Linux/…

Tomcat安装步骤及详细配置教程(2022最新版)

网上的tomcat安装及配置教程一大堆&#xff0c;但是好多都过时了&#xff0c;根本不适用现在的版本&#xff0c;今天凯歌整理一篇Tomcat安装步骤及详细配置教程&#xff0c;2022年最新版~ Tomcat安装及配置教程主要分为四步&#xff1a; 步骤一&#xff1a;首先确认自己是否已…

学习Java的第一天

一、Java简介 Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发&#xff0c;并在 1995 年正式推出。 后来 Sun 公司被 Oracle &#xff08;甲骨文&#xff09;公司收购&#xff0c;Jav…

nvm安装和使用保姆级教程(详细)

一、 nvm是什么 &#xff1a; nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和npm都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的node.js。 二、卸载之前安装的node: …

java核心面试题汇总

文章目录 1. Java1.1. TCP三次握手/四次挥手1.2 HashMap底层原理1.3 Java常见IO模型1.4 线程与线程池工作原理1.5 讲一讲ThreadLocal、Synchronized、volatile底层原理1.6 了解AQS底层原理吗 2. MySQL2.1 MySQL索引为何不采用红黑树&#xff0c;而选择B树2.2 MySQL索引为何不采…