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

2023-11-11

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

0.前言

Android BLE蓝牙配置全流程(一) 附APP源码 中已经完成了前期的准备工作,在这里我们得到了需要连接的蓝牙设备的名字和地址,需要完成蓝牙设备的连接和数据传输功能。

1.初始化界面

首先需要完成对这个界面包含的控件进行初始化,代码如下:

private TextView text1, text2;
private EditText editText;
private Button btn_send;
private String status = "Disconnected";
private final static String TAG = "hello";

//界面初始化
 private void initView() {
        text1 = findViewById(R.id.text1);
        text2 = findViewById(R.id.text2);
        editText = findViewById(R.id.edit_text);
        btn_send = findViewById(R.id.btn_send);

        text1.setText("状态:" + status);

        btn_send.setOnClickListener(this);
    }

控件text1中显示目前的连接状态,控件text2中显示接收到的数据,控件edittext中输入要发送的数据,控件btn_send是发送按钮

2.获取前一个界面传来的数据

首先得到前一个界面传来的数据,代码如下:

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

private Bundle bundle;
private String name;
private String address;

bundle = getIntent().getExtras();
name = bundle.getString(EXTRAS_DEVICE_NAME);
address = bundle.getString(EXTRAS_DEVICE_ADDRESS);

name中存放蓝牙设备的名字,address中存放蓝牙设备的地址。

3.创建一个服务类并继承Service

本文中创建了一个BlutoothBLEService 并继承了Service,完成蓝牙设备的初始化、连接、断开连接、读取特征值、写入特征值、设置特征值变化通知以及获取已连接蓝牙的所有服务等操作。

public class BlutoothBLEService extends Service {
			...............................
}

3.1 蓝牙设备初始化

public final static String ACTION_GATT_CONNECTED = "com.example.bluebledemo.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.example.bluebledemo.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluebledemo.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.bluebledemo.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA = "com.example.bluebledemo.EXTRA_DATA";

private final static int STATE_DISCONNECTED = 0;
private final static int STATE_CONNECTING = 1;
private final static int STATE_CONNECTED = 2;

private final static String TAG = "hello";

private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;

//蓝牙初始化 在第二个界面中的ServiceConnection中调用
public boolean initialize() {
    if (mBluetoothManager == null) {
        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            Log.i(TAG, "initialize: mBluetoothManager 初始化失败");
            return false;
        }
    }
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        Log.i(TAG, "initialize: mBluetoothAdapter 初始化失败");
        return false;
    }
    return true;
}

上面代码是在第二个界面成功绑定这个服务之后,在回调函数ServiceConnection中被执行,下文将详细介绍,这里先把这个服务详细的说清楚。

3.2 蓝牙设备的连接

private BluetoothGatt mBluetoothGatt;

//蓝牙连接外围设备
public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.i(TAG, "connect: BLE not init");
        return false;
    }
    if (mBluetoothDeviceAddress != null && mBluetoothGatt != null && mBluetoothDeviceAddress.equals(address)) {
        Log.i(TAG, "connect: Trying to use an existing mBluetoothGatt for connection");
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }
    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.i(TAG, "connect: device not found");
        return false;
    }
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    Log.i(TAG, "connect: Trying to create a connection");
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

在第二个界面中会创建一个BlutoothBLEService类的对象,然后调用这个方法并传入地址,即可连接成功。mBluetoothGatt = device.connectGatt(this, false, mGattCallback)通过绑定回调函数,本节的后续操作都可在回调函数中完成相应的操作。

3.3 蓝牙设备取消连接

//取消连接
public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.i(TAG, "disconnect: BLE not init");
        return;
    }
    mBluetoothGatt.disconnect();
}

//关闭所有蓝牙连接
public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

同样通过BlutoothBLEService类的对象调用这个方法即可取消连接。

3.4 读取特征值

//读取特征值
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.i(TAG, "readCharacteristic: BLE not init");
        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
}

在蓝牙设备连接成功后自动读一次特征值。

3.5 写入特征值

//写入特征值
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.i(TAG, "writeCharacteristic: BLE not init");
        return;
    }
    mBluetoothGatt.writeCharacteristic(characteristic);
}

这是完成手机APP向蓝牙设备写数据的操作。

3.6 设置特征值变化通知
这个很重要,如果不设置话,手机APP是接收不到蓝牙设备发送的数据。

//设置特征值变化通知
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristicNotification, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.i(TAG, "setCharacteristicNotification: BLE not init");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristicNotification, enabled);
    BluetoothGattDescriptor descriptor = characteristicNotification.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
    if (enabled) {
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    } else {
        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
    }
    mBluetoothGatt.writeDescriptor(descriptor);
}

这个方法同样也是在第二个界面中调用的,其中UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"HC-08蓝牙设备的监听UUID,如果蓝牙设备不是一个型号,请更改为自己所用的蓝牙设备的监听UUID

3.7 获取已连接蓝牙的所有服务

//获取已连接蓝牙的所有服务
public List<BluetoothGattService> getDupportedGattServices() {
    if (mBluetoothGatt == null) {
        return null;
    }
    return mBluetoothGatt.getServices();
}

返回已经连接蓝牙设备的所有服务。

3.8 读取蓝牙设备的RSSI值

//读取RSSI
public void readRssi() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.i(TAG, "readRssi: BLE not init");
        return;
    }
    mBluetoothGatt.readRemoteRssi();
}

该方法返回的是已连接的蓝牙设备的信号值(RSSI)。

3.9 连接外围设备的回调函数

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    //重写 蓝牙连接状态
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                mBluetoothGatt.discoverServices();   //发现服务
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                broadcastUpdate(intentAction);
            }
        }
    }

    //重写 蓝牙发现服务
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            Log.i(TAG, "onServicesDiscovered: 蓝牙发现服务");
        } else {
            Log.i(TAG, "onServicesDiscovered: 蓝牙发现服务失败" + status);
        }

    }

    //重写 蓝牙读特征
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "onCharacteristicRead: is called");
            byte[] sucString = characteristic.getValue();
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    }

    //重写 蓝牙写特征
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.i(TAG, "onCharacteristicWrite: 写数据成功");
    }

    //重写 蓝牙特征改变
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        Log.i(TAG, "onCharacteristicChanged: changed changed changed changed changed ");
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }

    //重写 蓝牙读描述值
    @Override
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "onDescriptorRead: Read Read Read");
            byte[] desc = descriptor.getValue();
            if (desc == null) {
                Log.i(TAG, "onDescriptorRead: desc is null null null");
            }
        }
    }

    //重写 蓝牙写描述值
    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "onDescriptorWrite: Write Write Write");
        }
    }


    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "onReliableWriteCompleted: onReliableWriteCompleted onReliableWriteCompleted onReliableWriteCompleted");
        }
    }

    //重写 获取蓝牙信号值
    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "onReliableWriteCompleted: RSSI RSSI RSSI");
            broadcastUpdate(ACTION_DATA_AVAILABLE, rssi);
        }
    }
};

在这个回调函数中可以得到已连接的蓝牙设备的所有状态,包括:是否连接成功、是否发送数据成功以及是否读取数据成功等等,然后在这个服务中通过BroadCast(广播)将信息发出去,在第二个界面中设置广播的监听接收器,进而完成数据的传输。

3.10 发送广播

//更新广播
private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

//更新广播
private void broadcastUpdate(final String action, int rssi) {
    final Intent intent = new Intent(action);
    intent.putExtra(EXTRA_DATA, rssi);
    sendBroadcast(intent);
}

//更新广播
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);
    final byte[] data = characteristic.getValue();
    //将data的数据传输给主空间中保存
    if (data != null && data.length > 0) {
        final StringBuilder stringBuilder = new StringBuilder(data.length);
        for (byte byteChar : data) {
            stringBuilder.append(String.format("%02X", byteChar));
            Log.i(TAG, "broadcastUpdate: byteChar is:" + byteChar);
        }
        intent.putExtra("BLE_BYTE_DATA", data);
        intent.putExtra(EXTRA_DATA, new String(data));
    }
    sendBroadcast(intent);

}

通过广播的形式将数据发出去,在第二个界面中通过设置过滤器接收对应的广播。

3.11 获取服务的对象

当在第二个界面中成功绑定了该服务,通过调用这个方法即可返回一个该服务的对象。

public class LocalBinder extends Binder {
    public BlutoothBLEService getService() {
        return BlutoothBLEService.this;
    }

}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

public boolean onUnbind(Intent intent) {
    close();
    return super.onUnbind(intent);
}

4.绑定服务

在完成服务的代码编写之后,接着需要在第二个界面中绑定该服务,在onCreat方法中进行绑定,代码如下:

Intent getServiceIntent = new Intent(this, BlutoothBLEService.class);
bindService(getServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

5.绑定服务的回调函数

是否成功的绑定了服务,我们可以在回调函数中获知,代码如下:

private BlutoothBLEService mBlutoothBLEService;

//服务(BlutoothBLEService.class)连接 回调函数
private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mBlutoothBLEService = ((BlutoothBLEService.LocalBinder) service).getService();
        if (!mBlutoothBLEService.initialize()) {
            Log.i(TAG, "onServiceConnected: MainActivity BLE not init");
            finish();
        }
        Log.i(TAG, "onServiceConnected: 8888888888888");
        mBlutoothBLEService.connect(address);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBlutoothBLEService = null;
    }
};

如果绑定成功,首先执行mBlutoothBLEService = ((BlutoothBLEService.LocalBinder) service).getService()获取这个服务的对象,然后执行mBlutoothBLEService.initialize()对服务进行初始化。

6.注册/取消注册广播

//注册广播和IntentFilter
@Override
protected void onResume() {
    registerReceiver(mBroadCastReceiver, makeGattUpdateIntentFilter());
    if (mBlutoothBLEService != null) {
        Log.i(TAG, "onResume: 99999999999999");
        boolean res = mBlutoothBLEService.connect(address);
        Log.i(TAG, "onResume: " + res);
    }
    super.onResume();
}

//取消注册广播和IntentFilter
@Override
protected void onDestroy() {
    unregisterReceiver(mBroadCastReceiver);
    mBlutoothBLEService = null;
    super.onDestroy();
}

上述代码利用registerReceiver(mBroadCastReceiver, makeGattUpdateIntentFilter())unregisterReceiver(mBroadCastReceiver)方法完成注册和取消注册广播,下面的代码是设置广播接收和过滤器。

//IntentFilter 设置过滤 与广播进行注册
public IntentFilter makeGattUpdateIntentFilter() {
    IntentFilter filter = new IntentFilter();
    filter.addAction(BlutoothBLEService.ACTION_GATT_CONNECTED);
    filter.addAction(BlutoothBLEService.ACTION_GATT_DISCONNECTED);
    filter.addAction(BlutoothBLEService.ACTION_GATT_SERVICES_DISCOVERED);
    filter.addAction(BlutoothBLEService.ACTION_DATA_AVAILABLE);
    return filter;
}
//设置广播接收 服务(BlutoothBLEService.class)传过来得信息
BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String actionString = intent.getAction();
        if (BlutoothBLEService.ACTION_GATT_CONNECTED.equals(actionString)) {	//蓝牙设备连接成功
            Log.i(TAG, "onReceive: " + name + " 连接成功");
            status = "Connected";
            updateConnectionState(status);
        } else if (BlutoothBLEService.ACTION_GATT_DISCONNECTED.equals(actionString)) {	//蓝牙设备连接失败
            Log.i(TAG, "onReceive: " + name + " 断开连接");
            status = "Disconnected";
            updateConnectionState(status);
        } else if (BlutoothBLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(actionString)) {	//蓝牙设备设置服务
            Log.i(TAG, "onReceive: 广播接收到服务");
            displayGattServices(mBlutoothBLEService.getDupportedGattServices());
        } else if (BlutoothBLEService.ACTION_DATA_AVAILABLE.equals(actionString)) {	//蓝牙设备有数据提供
            Log.i(TAG, "onReceive: 有数据");
            displayData(intent.getExtras().getString(BlutoothBLEService.EXTRA_DATA), intent);
        }
    }
};

其中displayGattServices(mBlutoothBLEService.getDupportedGattServices())进一步完成发现服务,displayData(intent.getExtras().getString(BlutoothBLEService.EXTRA_DATA), intent)进一步完成数据接收的处理,updateConnectionState(status)是完成蓝牙状态的更新操作,这三个代码将在下文进行说明。

7.发现已连接蓝牙设备服务

public static String CHARACTERISTIC_UUID = "0000ffe1-0000-1000-8000-00805f9b34fb";
private BluetoothGattCharacteristic target_chara;
private Handler mHandler = new Handler();

//服务
public void displayGattServices(List<BluetoothGattService> gattServices) {
    if (gattServices == null) {
        return;
    }
    for (BluetoothGattService service : gattServices) {
        List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
        for (final BluetoothGattCharacteristic characteristic : gattCharacteristics) {
            if (characteristic.getUuid().toString().equals(CHARACTERISTIC_UUID)) {
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mBlutoothBLEService.readCharacteristic(characteristic);
                    }
                }, 200);
                mBlutoothBLEService.setCharacteristicNotification(characteristic, true);
                target_chara = characteristic;
            }
        }
    }
}

其中CHARACTERISTIC_UUIDHC-08蓝牙设备的读写特征UUID,如果使用的蓝牙设备型号不一样请改为自己所用的蓝牙设备的读写特征UUID,根据这个UUID找到蓝牙设备的读写特征,从而设置特征变化通知和记录这个特征。

8.数据的接收

//接收的数据
public void displayData(String rev_string, Intent intent) {

    try {
        byte[] data = intent.getByteArrayExtra("BLE_BYTE_DATA");
        if (data == null) {
            Log.i(TAG, "displayData: data is empty");
            return;
        }
        rev_string = new String(data, 0, data.length, "GB2312");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    rev_str = rev_str + rev_string;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            text2.setText(rev_str);
        }
    });
}

上面的代码完成数据的接收,并将接收到的数据放入控件text2中进行追加显示。

9.更新蓝牙连接状态

private tempHandler myHandler = new tempHandler();
private String rev_str = "";

//更新蓝牙连接状态
private void updateConnectionState(String Status) {
    Message msg = new Message();
    msg.what = 1;
    Bundle bundle = new Bundle();
    bundle.putString("connect_state", status);
    msg.setData(bundle);
    myHandler.sendMessage(msg);
}

private class tempHandler extends Handler {
    @Override
    public void handleMessage(@NonNull Message msg) {
        switch (msg.what) {
            case 1:
                String str;
                str = msg.getData().getString("connect_state");
                text1.setText("状态:" + str);
                break;
        }
        super.handleMessage(msg);
    }
}

在控件text1中更新显示当前蓝牙的连接状态。

10.发送数据

//发送数据线程
public class sendDataThread implements Runnable {

    public sendDataThread() {
        new Thread(this).start();
    }

    @Override
    public void run() {
        if (editText.getText() != null) {
            byte[] buff = null;
            try {
                buff = editText.getText().toString().getBytes("GB2312");
                Log.i(TAG, "run: " + buff.length);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            int[] sendDataLens = dataSparate(buff.length);
            for (int i = 0; i < sendDataLens[0]; i++) {
                byte[] data20 = new byte[20];
                for (int j = 0; j < 20; j++) {
                    data20[j] = buff[i * 20 + j];
                }
                target_chara.setValue(data20);
                mBlutoothBLEService.writeCharacteristic(target_chara);
            }
            if (sendDataLens[1] != 0) {
                byte[] lastData = new byte[sendDataLens[1]];
                for (int i = 0; i < sendDataLens[1]; i++) {
                    lastData[i] = buff[sendDataLens[0] * 20 + i];
                }
                if (lastData != null) {
                    target_chara.setValue(lastData);
                    mBlutoothBLEService.writeCharacteristic(target_chara);
                } else {
                    Log.i(TAG, "run: last是空的");
                }

            }

        }

    }
}

//发送数据实现点击事件
@Override
public void onClick(View v) {
    new sendDataThread();
}

//数据分包处理
private int[] dataSparate(int len) {
    int[] lens = new int[2];
    lens[0] = len / 20;
    lens[1] = len % 20;
    return lens;
}

通过点击发送按钮,创建一个发送数据的线程,发送的数据将进行分包处理依次发送,确保即使数据太大也能成功的发送。

至此Android的BLE蓝牙(HC-08)的配置全流程都已详细的说明,如有任何问题可以留言或者私信我,看到会及时回复,上述的内容如果有误,还请各位大佬不吝赐教,在此,祝愿各位学习顺利,前途无限~

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

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

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

随机推荐

  • 了解 React useMemo Hook

    介绍 随着 React 16 8 的发布 您现在可以在 React 应用程序中使用许多有用的钩子 16 8 中引入的内置 Hooks 之一是useMemo 该挂钩有可能提高应用程序的性能 本文将探讨重新渲染在 React 中的工作原理 为什
  • 了解 SSH 加密和连接过程

    介绍 SSH或安全 shell 是一种安全协议 也是安全管理远程服务器的最常用方法 SSH 使用多种加密技术 提供了一种在两方之间建立加密安全连接 向另一方验证每一方以及来回传递命令和输出的机制 在本指南中 我们将研究 SSH 使用的底层加
  • 如何在 Ubuntu 14.04 上导入和导出 MongoDB 数据库

    MongoDB 是最流行的 NoSQL 数据库引擎之一 它以可扩展 功能强大 可靠且易于使用而闻名 在本文中 我们将向您展示如何导入和导出 MongoDB 数据库 我们应该明确指出 本文中的导入和导出是指以人类可读的格式处理数据 并与其他软
  • 如何使用 Java List 和 ListArray 的 remove() 方法

    介绍 Java列表remove 方法用于从列表中删除元素 ArrayList是最广泛使用的实现列表界面 所以这里的例子将使用ArrayList remove 方法 Java列表remove Methods 那里有两个remove 方法从列表
  • 在 SSH Unix 服务器上运行 Shell 命令的 Java JSch 示例

    今天我们将研究 JSch 示例教程 我们可以使用 JSch 在 java 中创建 SSH 连接 早些时候我写了一个程序来连接SSH服务器上的远程数据库 今天 我将介绍一个可用于连接到启用 SSH 的服务器并执行 shell 命令的程序 我在
  • R 中的 Paste() 函数 - 简要指南

    在R 中使用paste 函数将是直接且简单的 在本教程中 让我们看看如何使用paste 来连接字符串和值 paste 从多个向量中获取多个元素并将它们连接成一个元素 除了paste 函数之外 R还有另一个名为paste0 的函数 是的 你没
  • DigitalOcean 的技术写作指南

    DigitalOcean 很高兴能够继续构建与服务器管理和软件工程相关的技术文章集 为了确保 DigitalOcean 文章具有一致的质量和风格 我们制定了以下准则 本指南分为四个部分 Style 我们编写技术教程的高级方法 结构 对我们的
  • 如何使用 Vanilla JavaScript 和 HTML 创建拖放元素

    介绍 拖放是一种常见的用户交互 您可以在许多图形用户界面中找到它 有预先存在的 JavaScript 库可用于向您的应用程序添加拖放功能 但是 在某些情况下 库可能不可用 或者会引入项目不需要的开销或依赖项 在这些情况下 了解现代 Web
  • 如何在 Ubuntu 18.04 上安装 Apache Tomcat 9

    介绍 Apache Tomcat 是一个 Web 服务器和 servlet 容器 用于为 Java 应用程序提供服务 Tomcat 是 Java Servlet 和 JavaServer Pages 技术的开源实现 由 Apache Sof
  • 如何在 Apache Web 服务器中安装、配置和使用模块

    Status 已弃用 本文介绍不再受支持的 Ubuntu 版本 如果您当前运行的服务器运行 Ubuntu 12 04 我们强烈建议您升级或迁移到受支持的 Ubuntu 版本 升级到Ubuntu 14 04 从 Ubuntu 14 04 升级
  • 如何使用 Python 调试器

    介绍 在软件开发中 调试是查找并解决阻止软件正常运行的问题的过程 Python调试器为Python程序提供了调试环境 它支持设置条件断点 一次一行单步执行源代码 堆栈检查等 先决条件 您应该在计算机或服务器上安装 Python 3 并设置编
  • 如何在 Apache 上为 Debian 8 创建 SSL 证书

    介绍 本教程将引导您完成使用 SSL 证书保护的 Apache 服务器的设置和配置 在本教程结束时 您将拥有一个可通过 HTTPS 访问的服务器 SSL 基于将大整数解析为其同样大的质因数的数学难题 使用它 我们可以使用私钥 公钥对来加密信
  • 如何在 DigitalOcean 上使用 WordPress 一键安装

    介绍 WordPress是世界上最受欢迎的内容管理和博客平台之一 可让您高效地创建和管理网站内容 本教程将指导您使用以下命令设置 WordPress 网站WordPress 一键式应用程序 一键部署 除了常规 Ubuntu 20 04 Dr
  • localStorage和sessionStorage简介

    介绍 The localStorage and sessionStorage对象是 Web 存储 API 的一部分 是用于在本地保存键 值对的两个出色工具 使用localStorage and sessionStorage用于存储是使用 c
  • vue 遍历目录下的文件,获取图片名并直接遍历渲染

    使用场景 搭了个资源网站 想直接遍历显示当前图片目录下的所有图片 但是图片名字乱七八糟花里胡哨 举例说明获取目录下的文件名 新创建一个 vue 项目 获取 views 目录下的以 vue 结尾的文件的文件名 mounted 参数 1 路径
  • web安全漏洞总结

    目录 一 网络安全常见漏洞 1 sql注入漏洞 漏洞解释与形成原因 漏洞分类 漏洞存在常见地方 漏洞利用 漏洞防御 攻击流量特征 绕开waf拦截的常用方法 2 文件上传漏洞 漏洞解释与形成原因 漏洞利用 漏洞存在常见地方 漏洞防御 绕开wa
  • 各类常用符号

    常用符号 1 几何符号 2 代数符号 3 运算符号 如加号 减号 乘号 或 除号 或 两个集合的并集 交集 根号 对数 log lg ln 比 微分 dx 积分 曲线积分 等 4 集合符号 5 特殊符号 圆周率 6 推理符号 a
  • 项目环境由pytorch1.10升级1.11中间要改的东西

    1 THC THCDeviceUtils cuh 该文件弃用 nightly missing THC THCDeviceUtils cuh include
  • VMware中CentOS7.5 启用NAT模式配置静态IP连接外网

    1 在cmd中查看本机VMnet8的ipv4地址及子网掩码 C gt ipconfig 2 在VMware里 依次点击 编辑 虚拟网络编辑器 如下图 选择NAT模式 3 取消勾选 使用本地DHCP服务将IP分配给虚拟机 这个选项 配置子网i
  • STM32与BLE蓝牙通信 Android APP配置(二)

    事务的难度远远低于对事物的恐惧 0 前言 在 Android BLE蓝牙配置全流程 一 附APP源码 中已经完成了前期的准备工作 在这里我们得到了需要连接的蓝牙设备的名字和地址 需要完成蓝牙设备的连接和数据传输功能 1 初始化界面 首先需要