Android 蓝牙开发( 二 )

前言

上一篇文章给大家分享了Android蓝牙的基础知识和基础用法,不过上一篇都是一些零散碎片化的程序,这一篇给大家分享Android蓝牙开发实战项目的初步使用

效果演示 : 

Android蓝牙搜索,配对,连接,通信

Android蓝牙实战开发步骤

1.新建Android项目添加蓝牙权限

下图所示:MyBluetoothDemo为刚刚创建的Android空项目,我们现在清单文件中把我们需要用到的权限声明一下,其中定位权限还需要做动态申请

2.封装BluetoothAdapter类

BluetoothAdapter类提供了常用的蓝牙API,我这里创建了一个BlueToothController类,小编这里是先将这些API封装到了一个BlueToothController类中,方便后续使用和操作

package com.example.mybluetoothdemo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;

import java.util.ArrayList;
import java.util.List;

public class BlueToothController {

    private BluetoothAdapter mBluetoothAdapter;

    public BlueToothController(){
        //初始化蓝牙适配器
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    public BluetoothAdapter getBluetoothAdapter() {
        return mBluetoothAdapter;
    }




    /**
     * 检查设备是否支持蓝牙
     */
    public boolean isBluetoothSupport(){
        if(mBluetoothAdapter == null){
            return false;
        }else {
            return true;
        }
    }

    /**
     * 检查该设备蓝牙是否开启
     */
    public boolean isBluetoothEnabled(){
        if(mBluetoothAdapter.isEnabled()){
            return true;
        }else {
            return false;
        }
    }


    /**
     * 打开蓝牙
     */
    @SuppressLint("MissingPermission")
    public void turnOnBlueTooth(Activity activity, int requestCode) {
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        activity.startActivityForResult(intent, requestCode);
    }


    /**
     * 打开蓝牙可见性
     */
    @SuppressLint("MissingPermission")
    public void enableVisibily(Context context){
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
        context.startActivity(intent);
    }


    /**
     * 停止查找设备
     */
    @SuppressLint("MissingPermission")
    public void cancelFindDevice(){
        mBluetoothAdapter.cancelDiscovery();
    }

    /**
     * 判断当前设备是否在查找蓝牙设备
     */
    @SuppressLint("MissingPermission")
    public boolean isStartDiscovering(){
        if(mBluetoothAdapter.isDiscovering()){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 判断当前设备是否未在查找蓝牙设备
     */
    @SuppressLint("MissingPermission")
    public boolean isCancelDiscovering(){
        if(!mBluetoothAdapter.isDiscovering()){
            return true;
        }else {
            return false;
        }
    }


    /**
     * 查找设备
     */
    @SuppressLint("MissingPermission")
    public void findDevice() {
        mBluetoothAdapter.startDiscovery();
    }


    /**
     * 获取已绑定设备
     */
    @SuppressLint("MissingPermission")
    public List<BluetoothDevice> getBondedDeviceList(){
        return new ArrayList<>(mBluetoothAdapter.getBondedDevices());
    }

    /**
     * 判断蓝牙是否连接
     */
    @SuppressLint("MissingPermission")
    public boolean isConnectBlue(BluetoothSocket bluetoothSocket){
        return bluetoothSocket !=null && bluetoothSocket.isConnected();
    }
}

3. 编写UI页面

activity_main.xml:这是我们MainActivity的UI,放置了一个ListView和一个按钮,ListView用来显示搜索到的蓝牙设备和已配对的蓝牙设备,Button控件用来与连接后的设备发送信息时使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16.dp"
    android:paddingRight="16.dp"
    android:paddingBottom="16.dp">

    <ListView
        android:id="@+id/device_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#e90423"
        android:dividerHeight="1px"
        >
    </ListView>

    <Button
        android:id="@+id/btn_write_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:text="发送消息"/>

</RelativeLayout>

既然有了ListView,那就一定有ListView的每条子元素UI,新建list_item.xml

list_item.xml:    用来显示ListView列表的子内容

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="horizontal"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:textStyle="bold"
            android:textSize="18sp"/>

        <TextView android:id="@+id/text3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:textSize="13sp"/>

    </LinearLayout>

</LinearLayout>

然后,我们再为页面右上角写一个menu列表按钮,新建menu_main.xml

menu_main.xml:  

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity" >

    <item
        android:id="@+id/is_bluetooth_support"
        android:title="@string/is_bluetooth_support"
        android:orderInCategory="1" />

    <item
        android:id="@+id/is_bluetooth_enabled"
        android:title="@string/is_bluetooth_enabled"
        android:orderInCategory="2" />

    <item
        android:id="@+id/bonded_device"
        android:title="@string/bonded_device"
        android:orderInCategory="2" />

    <item
        android:id="@+id/find_device"
        android:title="@string/find_device"
        android:orderInCategory="3" />


</menu>

最后,再为大家分享一下string.xml字符串配置信息

4. 蓝牙列表适配器

创建DeviceAdapter类,具体如下图所示:

package com.example.mybluetoothdemo;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

public class DeviceAdapter extends BaseAdapter{

    private List<BluetoothDevice> mData;
    private Context mContext;

    public DeviceAdapter(List<BluetoothDevice> data, Context context){
        mData = data;
        mContext = context.getApplicationContext();
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Object getItem(int i) {
        return mData.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @SuppressLint("MissingPermission")
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View itemView = view;
        //复用view,优化性能
        if(itemView == null){
            itemView = LayoutInflater.from(mContext).inflate(R.layout.list_item, viewGroup,false);
        }


        TextView text1 = itemView.findViewById(R.id.text1);
        TextView text2 = itemView.findViewById(R.id.text3);

        text1.setTextColor(Color.BLACK);
        text2.setTextColor(Color.BLACK);

        //获取对应的蓝牙设备
        BluetoothDevice device = (BluetoothDevice) getItem(i);


        //显示设备名称
        if(device.getName()==null){
            text1.setText("N/A");
        }else {
            text1.setText(device.getName());
        }

        //显示设备地址
        text2.setText(device.getAddress());

        return itemView;
    }

    //刷新列表,防止搜索结果重复出现
    public void refresh(List<BluetoothDevice> data){
        mData = data;
        notifyDataSetChanged();
    }

}

5. 蓝牙搜索,配对,连接,通信

小编这里为了让大家方便,便将搜索,配对,连接都写在了MainActivity中了

package com.example.mybluetoothdemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;


import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

    private final String TAG = "yf";

    private BlueToothController blueToothController = new BlueToothController();

    private static final int REQUEST_ENABLE_BT = 1;

    private ListView listView;
    private DeviceAdapter blueToothDeviceAdapter,bondBlueToothDeviceAdapter;

    private List<BluetoothDevice> deviceList = new ArrayList<>();
    private List<BluetoothDevice> bondedDeviceList = new ArrayList<>();

    private BluetoothDevice device;

    private BluetoothGatt bluetoothGatt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUI();

        //判断是否有访问位置的权限,没有权限,直接申请位置权限
        isPermission();


        registerBluetoothReceiver();
    }



    @SuppressLint("MissingPermission")
    private void initUI(){
        listView = findViewById(R.id.device_list);
        blueToothDeviceAdapter = new DeviceAdapter(deviceList,this);
        bondBlueToothDeviceAdapter = new DeviceAdapter(bondedDeviceList,this);

        findViewById(R.id.btn_write_1).setOnClickListener(view -> {
            if (null == mWriter){
                Log.e("cx12345","ble:发送失败:null == writer !!!!");
            }else {
                mWriter.setValue(new byte[]{
                        (byte)0x0c,
                        (byte)0x11,
                        (byte)0x09,
                        (byte)0x41,
                        (byte)0x23,
                        (byte)0x00,
                        (byte)0x01,
                        (byte)0x03,
                        (byte)0xFF
                });

                mGatt.writeCharacteristic(mWriter);
            }
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main,menu);
        return true;
    }

    @SuppressLint("MissingPermission")
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        switch (id){
            case R.id.is_bluetooth_support:
                if (blueToothController.isBluetoothSupport()){
                    Toast.makeText(MainActivity.this, "该设备支持蓝牙功能", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(MainActivity.this, "该设备不支持蓝牙功能", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.is_bluetooth_enabled:
                if (blueToothController.isBluetoothEnabled()){
                    Toast.makeText(MainActivity.this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
                }else {
                    blueToothController.turnOnBlueTooth(this,REQUEST_ENABLE_BT);
                }
                break;
            case R.id.bonded_device:
                setTitle("已配对的设备");

                bondedDeviceList = blueToothController.getBondedDeviceList();
                listView.setAdapter(bondBlueToothDeviceAdapter);
                bondBlueToothDeviceAdapter.refresh(bondedDeviceList);


                break;
            case R.id.find_device:
                setTitle("可用设备");
                if(blueToothController.isStartDiscovering()){
                    blueToothController.cancelFindDevice();
                }
                blueToothController.findDevice();
//                blueToothController.getBluetoothAdapter().startLeScan(leScanCallback);
                listView.setAdapter(blueToothDeviceAdapter);
                blueToothDeviceAdapter.refresh(deviceList);
                listView.setOnItemClickListener(deviceBluetooth);
                break;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }


    private AdapterView.OnItemClickListener deviceBluetooth = new AdapterView.OnItemClickListener() {
        @SuppressLint("MissingPermission")
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            device = deviceList.get(i);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                //蓝牙绑定
//                device.createBond();

                //Gatt协议连接蓝牙
                bluetoothGatt = device.connectGatt(MainActivity.this,true,mGattCallback);
                bluetoothGatt.connect();
            }
        }
    };




    //动态获取位置权限
    private void isPermission(){
        if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
                || (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 200);
        }
    }




    @SuppressLint("MissingPermission")
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止设备搜索
        blueToothController.getBluetoothAdapter().cancelDiscovery();
        //注销广播
        unregisterReceiver(receiver);
    }


    private void registerBluetoothReceiver(){
        //filter注册广播接收器
        IntentFilter filter = new IntentFilter();


        //蓝牙当前状态
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);


        //开始扫描蓝牙设备广播
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);

        //找到蓝牙设备广播
        filter.addAction(BluetoothDevice.ACTION_FOUND);

        //扫描蓝牙设备结束广播
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);

        //蓝牙设备配对状态改变广播
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);

        //设备扫描模式改变广播
        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);

        registerReceiver(receiver, filter);
    }

    //处理找到蓝牙设备和搜索完成的广播消息
    BroadcastReceiver receiver = new BroadcastReceiver() {

        @SuppressLint("MissingPermission")
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            //开始查找设备
            if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
                //初始化适配器列表
                deviceList.clear();
                bondedDeviceList.clear();
                blueToothDeviceAdapter.refresh(deviceList);
                bondBlueToothDeviceAdapter.refresh((bondedDeviceList));
            }
            //找到蓝牙设备
            else if(BluetoothDevice.ACTION_FOUND.equals(action)){
                //搜到蓝牙设备
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                    //把搜索到的设备添加到已找到列表中,显示它的信息
                    deviceList.add(device);
                    blueToothDeviceAdapter.refresh(deviceList);

            }
            //查找设备结束
            else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
                //搜索完毕
                Toast.makeText(MainActivity.this, "选择要配对的蓝牙设备", Toast.LENGTH_SHORT).show();
                blueToothDeviceAdapter.refresh(deviceList);
            }
            //配对状态
            else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if(device == null){
                    Toast.makeText(MainActivity.this, "无设备", Toast.LENGTH_SHORT).show();
                    return;
                }

                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
                if(state == BluetoothDevice.BOND_BONDED){
                    Toast.makeText(MainActivity.this, "已配对", Toast.LENGTH_SHORT).show();
                }else if(state == BluetoothDevice.BOND_BONDING){
                    Toast.makeText(MainActivity.this, "正在配对", Toast.LENGTH_SHORT).show();
                }else if(state == BluetoothDevice.BOND_NONE){
                    Toast.makeText(MainActivity.this, "未配对", Toast.LENGTH_SHORT).show();
                }
            }

        }
    };

    private BluetoothGatt mGatt;
    private BluetoothGattCharacteristic mWriter;

    /**
     * @param gatt     返回连接建立的gatt对象
     * @param status   返回的是此次gatt操作的结果,成功了返回0
     * @param newState 每次client连接或断开连接状态变化,
     *                 STATE_CONNECTED 0,
     *                 STATE_CONNECTING 1,
     *                 STATE_DISCONNECTED 2,
     *                 STATE_DISCONNECTING 3
     */

    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @SuppressLint("MissingPermission")
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

            //连接成功
            if(newState == BluetoothProfile.STATE_CONNECTED){
                //进行服务发现
                gatt.discoverServices();
                Log.d(TAG,"连接成功");
            }else if(newState == BluetoothProfile.STATE_DISCONNECTED){
                //连接断开,处理断开逻辑
                Log.d(TAG,"连接断开");
            }
        }

        @SuppressLint("MissingPermission")
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG,"onServicesDiscovered : " + status + " ==>> " + gatt.toString());

            //发现服务成功,处理服务和特征值
            if(status == BluetoothGatt.GATT_SUCCESS){
                //发送消息
                mGatt = gatt;
                BluetoothGattService service = gatt.getService(UUID.fromString("0000180a-0000-1000-8000-00805F9B34FB"));
                mWriter = service.getCharacteristic(UUID.fromString("00002ad9-0000-1000-8000-00805F9B34FB"));

                //打开消息通知
                mGatt.setCharacteristicNotification(mWriter,true);
                BluetoothGattDescriptor descriptor = mWriter.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                mGatt.writeDescriptor(descriptor);
            }else {
                Log.d(TAG,"发现服务失败");
            }

        }


        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(TAG,"onCharacteristicRead " + status);
            //读取特征成功,处理特征值
            if(status == BluetoothGatt.GATT_SUCCESS){

            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(TAG,"onCharacteristicWrite " + status);
            //写入特征成功
            if(status == BluetoothGatt.GATT_SUCCESS){
                Log.d(TAG,"发送成功");
            }else {
                Log.d(TAG,"发送失败");
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {

            //接收到数据
            byte[] data = characteristic.getValue();

            //处理接收到的数据
            Log.d(TAG,"Received data: " + bytesToHexFun2(data));
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }

        @Override
        public void onServiceChanged(@NonNull BluetoothGatt gatt) {
            super.onServiceChanged(gatt);
        }

        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }
    };

    private char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private  String bytesToHexFun2(byte[] bytes) {
        char[] buf = new char[bytes.length * 2];
        int index = 0;
        for(byte b : bytes) { // 利用位运算进行转换,可以看作方法一的变种
            buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
            buf[index++] = HEX_CHAR[b & 0xf];
        }

        return new String(buf);
    }

}

到此为止,我们的程序就到这里了,蓝牙搜索,配对,连接,通信便已经成功实现了,大家可以把代码copy一下拿去运行,具体效果演示图在文章最上方,大家还想了解更多关于Android蓝牙开发的可以继续看我下一篇给大家的分享

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

git-tf clone 路径有空格处理方案

git-tf clone 路径存在空格情况下&#xff0c;运行命令报错&#xff1b; 需要对路径进行双引号处理

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

Redis——》Redis的部署方式对分布式锁的影响

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

Java“魂牵”京东商品详情描述数据,京东商品详情API接口,京东API接口申请指南

要通过京东的API获取商品详情描述数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取商品详情&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者&#xff0c;并创…

xxl-job的简单使用

模块介绍 一、以做过的项目一为例&#xff0c;模块分配如下&#xff1a; xxl-job-admin&#xff1a;任务管理后台模块 xxl-job-executor-paofu&#xff1a;任务代码执行模块 二、以做过的项目二为例&#xff0c;模块分配如下&#xff1a; 为什么要介绍一下模块的分配&#x…

【kubernetes】使用kubepshere部署中间件服务

KubeSphere部署中间件服务 入门使用KubeSphere部署单机版MySQL、Redis、RabbitMQ 记录一下搭建过程 (内容学习于尚硅谷云原生课程) 环境准备 VMware虚拟机k8s集群&#xff0c;一主两从&#xff0c;master也作为工作节点&#xff1b;KubeSphere k8skubesphere devops比较占用磁…

【Unity开发必备】100多个 Unity 学习网址 资源 收藏整理大全【持续更新】

Unity 相关网站整理大全 众所周知&#xff0c;工欲善其事必先利其器&#xff0c;有一个好的工具可以让我们事半功倍&#xff0c;有一个好用的网站更是如此&#xff01; 但是好用的网站真的太多了&#xff0c;收藏夹都满满的(但是几乎没打开用过&#x1f601;)。 所以本文是对…

NodeJs-Buffer(缓冲器)

目录 一、概念二、特点三、使用3.1 创建Buffer3.2 Buffer 与字符串的转化3.3 Buffer 的读写 一、概念 Buffer 是一个类似于数组的 对象 &#xff0c;用于表示固定长度的字节序列 Buffer 本质是一段内存空间&#xff0c;专门用来处理 二进制数据 。 二、特点 Buffer 大小固定且…

【Docker】01-Centos安装、简单使用

参考教程&#xff1a; https://www.bilibili.com/video/BV1Qa4y1t7YH/?p5&spm_id_frompageDriver&vd_source4964ba5015a16eb57d0ac13401b0fe77 什么是Docker&#xff1f; Docker是一种开源的容器化平台&#xff0c;用于构建、打包、部署和运行应用程序。它通过使用容…

flutter和原生利用pigeon建立通道

首先导入依赖&#xff1a; dependencies: pigeon: ^10.0.0定义一个文件&#xff1a; /// 用于定于flutter和平台的桥接方法 /// HostApi() 标记的&#xff0c;是用于 Flutter 调用原生的方法&#xff1b; /// FlutterApi() 标记的&#xff0c;是用于原生调用 Flutter 的方法&…

iPhone 15 Pro与iPhone 13 Pro:最大的预期升级

如果你在2021年首次发布iPhone 13 Pro时就抢到了它,那么你的合同很可能即将到期。虽然距离iPhone 15系列还有几周的时间,但你可能已经在想:是时候把你的旧iPhone升级为iPhone 15 Pro了吗? 我们认为iPhone 13 Pro是你现在能买到的最好的手机之一。但如果你想在2023年晚些时…

数据结构】二叉树篇|超清晰图解和详解:后序篇

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; 是瑶瑶子啦每日一言&#x1f33c;: 你不能要求一片海洋&#xff0c;没有风暴&#xff0c;那不是海洋&#xff0c;是泥塘——毕淑敏 目录 一、核心二、题目 一、核心 我们清楚…

STM32--RTC实时时钟

文章目录 Unix时间戳时间戳转换BKPRTC简介RTC框图硬件电路RTC的注意事项RTC时钟实验工程 Unix时间戳 Unix 时间戳是从1970年1月1日&#xff08;UTC/GMT的午夜&#xff09;开始所经过的秒数&#xff0c;不考虑闰秒。 时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64…

部署问题集合(二十二)Linux设置定时任务,并设置系统时间

前言 因为项目中经常用到定时任务&#xff0c;特此总结记录一下 步骤 大部分虚拟机创建后就自带定时服务&#xff0c;直接用命令就好编辑定时任务&#xff1a;crontab -e&#xff0c;在该文件下添加如下内容开机自启&#xff1a;reboot /home/autoRun.sh定时执行&#xff1a…

走进低代码平台| iVX-困境之中如何突破传统

前言&#xff1a; “工欲善其事,必先利其器”&#xff0c;找到和使用一个优质的工具平台&#xff0c;往往会事半功倍。 文章目录 1️⃣认识走近低代码2️⃣传统的低代码开发3️⃣无代码编辑平台一个代码生成式低代码产品iVX受面性广支持代码复用如何使用&#xff1f; 4️⃣总结…

极氪汽车的云资源治理细探

作者&#xff1a;极氪汽车吴超 前言 2021 年&#xff0c;极氪 001 迅速崭露头角&#xff0c;仅用 110 天便创下了首款车型交付量“最快破万”的纪录。2022 年 11 月&#xff0c;极氪 009 在短短 76 天内便率先完成了首批交付&#xff0c;刷新了中国豪华纯电品牌交付速度的纪录…

分布式集群——jdk配置与zookeeper环境搭建

系列文章目录 分布式集群——jdk配置与zookeeper环境搭建 分布式集群——搭建Hadoop环境以及相关的Hadoop介绍 文章目录 系列文章目录 前言 一 zookeeper介绍与环境配置 1.1 zookeeper的学习 1.2 Zookeeper的主要功能 1.2.1 znode的节点类型 1.2.2 zookeeper的实现 …

SQL Server 2019导入txt数据

1、选择导入数据 2、选择Flat file Source 选择文件&#xff0c;如果第一行不是列名&#xff0c;就不勾选。 3、下一步 可以看看数据是否是对的 4、下一步 选择SQL server Native Client 11&#xff0c;数据库选择导入进的库 输入连接数据库的名字和要导入的数据库 下一…

渲染如何做到超强渲染?MAX插件CG MAGIC中的渲染功能!

渲染工作应该算是设计师的日常工作流程中最重要的环节之一了。如果渲染速度加快&#xff0c;可能是要看渲染技巧掌握的有多少了。 大家熟悉的3d Max本地渲染通道&#xff0c;对于CG MAGIC渲染功能你也一定不能错过&#xff0c;要知道操作简单易使用&#xff0c;就完全拿捏了效率…

22.3D等距社交媒体菜单的悬停特效

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>CSS Isometric Social Media Menu</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.…