Android Bluetooth development (2)

foreword

The previous article shared with you the basic knowledge and basic usage of Android Bluetooth, but the previous article was some fragmented programs. This article will share with you the initial use of Android Bluetooth development projects.

Effect demonstration: 

Android Bluetooth search, pairing, connection, communication

Android Bluetooth actual development steps

1. Create a new Android project to add Bluetooth permissions

As shown in the figure below: MyBluetoothDemo is an Android empty project just created, we now declare the permissions we need to use in the manifest file, and the positioning permission needs to be dynamically applied

2. Encapsulate the BluetoothAdapter class

The BluetoothAdapter class provides common Bluetooth APIs. I created a BlueToothController class here. The editor here encapsulates these APIs into a BlueToothController class for subsequent use and operation.

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. Write the UI page

activity_main.xml : This is the UI of our MainActivity, where a ListView and a button are placed. The ListView is used to display the searched Bluetooth devices and paired Bluetooth devices. The Button control is used to send information to the connected device.

<?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>

Now that there is a ListView, there must be a UI for each sub-element of the ListView, create a new list_item.xml

list_item.xml:     Used to display the sub-content of the ListView list

<?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>

Then, we write a menu list button for the upper right corner of the page and create a new 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>

Finally, let me share with you the string.xml string configuration information

4. Bluetooth list adapter

Create the DeviceAdapter class, as shown in the following figure:

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. Bluetooth search, pairing, connection, communication

In order to make it convenient for everyone, the editor here writes the search, pairing, and connection in 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);
    }

}

So far, our program is here. Bluetooth search, pairing, connection, and communication have been successfully realized. You can copy the code and run it. The specific effect demonstration picture is at the top of the article. You still want to know more For more about Android Bluetooth development, you can continue to read my next article for everyone to share

Guess you like

Origin blog.csdn.net/Ai1114/article/details/132277681