Android蓝牙开发记录

本文主要记录下项目开发过程中的蓝牙功能
其中开发流程部分主要参考博文Android蓝牙开发—经典蓝牙详细开发流程

开发流程

  • 开启蓝牙
  • 扫描蓝牙
  • 配对蓝牙
  • 连接蓝牙
  • 状态监听
  • 通信

开启蓝牙

  1. 获取BluetoothAdapter对象
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  1. 判断当前设备是否支持蓝牙
/**
 * 判断该设备是否支持蓝牙
 *
 * @return
 */
public boolean isSupportBlue() {
    return mBluetoothAdapter != null;
}
  1. 判断蓝牙是否开启
 /**
  * 蓝牙是否开启
  *
  * @return
  */
 public boolean isBlueEnable() {
     return isSupportBlue() && mBluetoothAdapter.isEnabled();
 }
  1. 开启蓝牙
  • 异步自动开启蓝牙(需通过广播监听结果)
/**
 * 自动打开蓝牙(异步操作:蓝牙不会立刻就处于开启状态)
 * 这个方法打开蓝牙不会弹出提示
 */
public void openBlueAsync() {
    if (isSupportBlue()) {
        mBluetoothAdapter.enable();
    }
}
  • 同步提示开启蓝牙
/**
 * 自动打开蓝牙(同步)
 * 这个方法打开蓝牙会弹出提示
 * 需要在onActivityResult 方法中判断resultCode == RESULT_OK  true为成功
 */
public void openBlueSync(Activity activity, int requestCode) {
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    activity.startActivityForResult(intent, requestCode);
}
  1. 蓝牙权限
  • Android6.0以下权限处理

     在AndroidManifest里面添加以下权限
    

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

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  • Android6.0以上权限处理

     在AndroidManifest里面添加以下权限
    

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

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  • 备注

     Android6.0(API 级别 23)以上没有定位权限会导致无法扫描到周边设备,如果您的应用适配 Android 9.0(API 级别 28)或更低版本,则您可以声明 ACCESS_COARSE_LOCATION 权限而非 ACCESS_FINE_LOCATION 权限。Android12.0(API 级别 31)以后扫描设备不再需要定位权限。(定位权限需动态申请)
    

扫描蓝牙

  1. 获取设备已经配对的蓝牙对象(在执行设备发现之前,您必须查询已配对的设备集,以了解所需的设备是否处于已检测到状态。)
/**
 * 获取已经配对的蓝牙对象
 *
 * @return
 */
public List<BluetoothDevice> getBondedDevices() {
    if (!isBlueEnable()) {
        return null;
    }
    return new ArrayList<>(mBluetoothAdapter.getBondedDevices());
}
  1. 扫描周围蓝牙设备(配对上的设备有可能扫描不出来)
/**
 * 扫描方法
 * 通过接收广播获取到扫描结果
 *
 * @return
 */
public boolean scanBlue() {
    if (!isBlueEnable()) {
        Log.e(TAG, "Bluetooth not enable!");
        return false;
    }
    //当前是否在扫描,如果是就取消当前的扫描,重新扫描
    if (mBluetoothAdapter.isDiscovering()) {
        mBluetoothAdapter.cancelDiscovery();
    }
    //这个方法是异步操作,一般耗时12秒
    return mBluetoothAdapter.startDiscovery();
}
  1. 取消扫描蓝牙
/**
 * 取消扫描蓝牙设备
 *
 * @return
 */
public boolean cancelScanBlue() {
    if (isBlueEnable()) {
        return mBluetoothAdapter.cancelDiscovery();
    }
    return true;
}
  1. 通过广播方式得到扫描结果
  • 创建扫描结果接口
/**
 * author November
 * time 2022/3/10 17:07
 * desc 蓝牙扫描结果接口
 */
public interface ScanBlueCallBack {

    /**
     * 开始扫描
     */
    void onScanStarted();

    /**
     * 结束扫描
     */
    void onScanFinished();

    /**
     * 发现设备
     *
     * @param bluetoothDevice
     */
    void onScanning(BluetoothDevice bluetoothDevice);
}
  • 创建扫描广播接收类
/**
 * author November
 * time 2022/3/10 17:04
 * desc 扫描广播接收类
 */
public class ScanBlueReceiver extends BroadcastReceiver {

    private static final String TAG = ScanBlueReceiver.class.getSimpleName();

    private ScanBlueCallBack callBack;

    public ScanBlueReceiver(ScanBlueCallBack callBack) {
        this.callBack = callBack;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "action:" + action);
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        switch (action) {
            case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
                Log.d(TAG, "开始扫描...");
                callBack.onScanStarted();
                break;
            case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                Log.d(TAG, "结束扫描...");
                callBack.onScanFinished();
                break;
            case BluetoothDevice.ACTION_FOUND:
                Log.d(TAG, "发现设备...");
                callBack.onScanning(device);
                break;
        }
    }
}
  • 注册广播(界面结束时需解注册)
IntentFilter intentFilter = new IntentFilter();
//开始扫描设备
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
//结束扫描设备
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
//发现蓝牙设备
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(mScanBlueReceiver, intentFilter);

配对蓝牙

  1. 开始配对
/**
 * 配对(配对成功与失败通过广播返回)
 *
 * @param device
 */
public void pin(BluetoothDevice device) {
    if (device == null) {
        Log.e(TAG, "bond device null!");
        return;
    }
    if (!isBlueEnable()) {
        Log.e(TAG, "Bluetooth not enable!");
        return;
    }
    //配对前先将扫描关闭
    if (mBluetoothAdapter.isDiscovering()) {
        mBluetoothAdapter.cancelDiscovery();
    }
    //判断设备是否配对,没有配对再配,配对了就不需做操作了
    if (device.getBondState() == BluetoothDevice.BOND_NONE) {
        Log.d(TAG, "attempts to bond:" + device.getName());
        try {
            Method createBondMethod = device.getClass().getMethod("createBond");
            Boolean returnValue = (Boolean) createBondMethod.invoke(device);
            returnValue.booleanValue();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "attempts to bond fail!");
        }
    }
}
  1. 取消配对
 /**
  * 取消配对(取消配对成功与失败通过广播返回 也就是配对失败)
  *
  * @param device
  */
 public void cancelPinBlue(BluetoothDevice device) {
     if (device == null) {
         Log.e(TAG, "cancel bond device null!");
         return;
     }
     if (!isBlueEnable()) {
         Log.e(TAG, "Bluetooth not enable!");
         return;
     }
     //判断设备是否配对,没有配对就不操作
     if (device.getBondState() != BluetoothDevice.BOND_NONE) {
         Log.e(TAG, "attempts to cancel bond:" + device.getName());
         try {
             Method removeBondMethod = device.getClass().getMethod("removeBond");
             Boolean returnValue = (Boolean) removeBondMethod.invoke(device);
             returnValue.booleanValue();
         } catch (Exception e) {
             e.printStackTrace();
             Log.e(TAG, "attempts to cancel bond fail!");
         }
     }
 }
  1. 通过广播方式得到配对结果
  • 创建配对结果接口
/**
 * author November
 * time 2022/3/11 10:59
 * desc 配对结果广播接收接口
 */
public interface PinBlueCallBack {

    /**
     * 配对请求
     */
    default void onBondRequest() {
    }

    /**
     * 取消配对/配对失败
     *
     * @param device
     */
    void onBondFail(BluetoothDevice device);

    /**
     * 配对中
     *
     * @param device
     */
    default void onBonding(BluetoothDevice device) {
    }

    /**
     * 配对成功
     *
     * @param device
     */
    void onBondSuccess(BluetoothDevice device);
}
  • 创建配对广播接收类
/**
 * author November
 * time 2022/3/11 10:52
 * desc 配对广播接收类
 */
public class PinBlueReceiver extends BroadcastReceiver {

    private static final String TAG = PinBlueReceiver.class.getSimpleName();

    /** 此处为要连接设备的初始秘钥,一般为1234或0000 */
    private final String pin = "1234";

    private PinBlueCallBack callBack;

    public PinBlueReceiver(PinBlueCallBack callBack) {
        this.callBack = callBack;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.e(TAG, "action:" + action);
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
            try {
                callBack.onBondRequest();
                //1.确认配对
                Method setPairingConfirmation = device.getClass().getDeclaredMethod("setPairingConfirmation", boolean.class);
                setPairingConfirmation.invoke(device, true);
                //2.终止有序广播
                abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
                //3.调----用setPin方法进行配对...
                Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class});
                Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{pin.getBytes()});
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
            switch (device.getBondState()) {
                case BluetoothDevice.BOND_BONDING:
                    Log.d(TAG, "配对中!");
                    callBack.onBonding(device);
                    break;
                case BluetoothDevice.BOND_BONDED:
                    Log.d(TAG, "配对成功!");
                    callBack.onBondSuccess(device);
                    break;
                case BluetoothDevice.BOND_NONE:
                    Log.d(TAG, "配对失败!");
                    callBack.onBondFail(device);
                    break;
            }
        }
    }
}
  • 注册广播(界面结束时需解注册)
IntentFilter pinFilter = new IntentFilter();
//配对请求
pinFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
//配对状态变化
pinFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(mPinBlueReceiver, pinFilter);

连接蓝牙

经典蓝牙连接相当于socket连接,是个非常耗时的操作,所以应在Activity(界面)以外的线程中执行连接步骤。

  1. 创建蓝牙连接接口
/**
 * author November
 * time 2022/3/11 13:44
 * desc 连接线程接口回调
 */
public interface ConnectBlueCallBack {

    /** 开始连接 */
    default void onStartConnect() {
    }

    /**
     * 连接成功
     *
     * @param device
     * @param socket
     */
    void onConnectSuccess(BluetoothDevice device, BluetoothSocket socket);

    /**
     * 连接失败
     *
     * @param device
     * @param hint
     */
    void onConnectFail(BluetoothDevice device, String hint);

}
  1. 创建连接线程
/**
 * author November
 * time 2022/3/11 11:34
 * desc 连接线程
 */
public class ConnectBlueTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {

    private static final String TAG = ConnectBlueTask.class.getSimpleName();

    private BluetoothDevice bluetoothDevice;

    private final ConnectBlueCallBack callBack;

    public ConnectBlueTask(ConnectBlueCallBack callBack) {
        this.callBack = callBack;
    }

    @Override
    protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {
        bluetoothDevice = bluetoothDevices[0];
        BluetoothSocket socket = null;
        try {
            Log.e(TAG, "开始连接!");
            socket = bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothUtils.MY_UUID_SECURE);
            if (socket != null && !socket.isConnected()) {
                socket.connect();
            }
        } catch (IOException e) {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        return socket;
    }

    @Override
    protected void onPreExecute() {
        Log.e(TAG, "开始连接!");
        if (callBack != null) {
            callBack.onStartConnect();
        }
    }

    @Override
    protected void onPostExecute(BluetoothSocket socket) {
        if (socket != null && socket.isConnected()) {
            Log.e(TAG, "连接成功!");
            if (callBack != null) {
                callBack.onConnectSuccess(bluetoothDevice, socket);
            }
        } else {
            Log.e(TAG, "连接失败!");
            if (callBack != null) {
                callBack.onConnectFail(bluetoothDevice, "连接失败");
            }
        }
    }
}
  1. 启动连接线程
/**
 * 连接(需要在配对之后调用)
 *
 * @param device
 * @param callBack
 */
public void connect(BluetoothDevice device, ConnectBlueCallBack callBack) {
    if (device == null) {
        Log.e(TAG, "bond device null!");
        return;
    }
    if (!isBlueEnable()) {
        Log.e(TAG, "Bluetooth not enable!");
        return;
    }
    //连接之前把扫描关闭
    if (mBluetoothAdapter.isDiscovering()) {
        mBluetoothAdapter.cancelDiscovery();
    }
    new ConnectBlueTask(callBack).execute(device);
}
  1. 判断是否连接成功
/**
 * 蓝牙是否连接
 *
 * @return
 */
public boolean isConnectBlue(BluetoothSocket socket) {
    return socket != null && socket.isConnected();
}
  1. 断开连接
/**
 * 断开连接
 *
 * @param socket
 * @return
 */
public boolean cancelConnect(BluetoothSocket socket) {
    if (socket != null && socket.isConnected()) {
        try {
            socket.getInputStream().close();
            socket.getOutputStream().close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    return true;
}
  1. 根据设备MAC地址进行连接
/**
 * 输入mac地址进行自动连接
 *
 * @param address
 * @param callBack
 */
public void connectMAC(String address, ConnectBlueCallBack callBack) {
    if (!isBlueEnable()) {
        return;
    }
    BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    connect(device, callBack);
}

状态监听

在蓝牙设备建立连接之后,因操作、距离、设备原因需要监听蓝牙间连接状态,以便进行相应的操作处理。

  1. 创建蓝牙状态监测接口
/**
 * author November
 * time 2022/3/29 16:05
 * desc 蓝牙状态检测接口回调
 */
public interface BluetoothMonitorCallBack {

    /** 蓝牙正在打开 */
    default void onOpening() {
    }

    /** 蓝牙已打开 */
    default void onOpened() {
    }

    /** 蓝牙正在关闭 */
    default void onClosing() {
    }

    /** 蓝牙已关闭 */
    default void onClosed() {
    }

    /** 设备已连接 */
    default void onConnected() {
    }

    /** 设备已断开 */
    default void onDisconnected() {
    }

}
  1. 创建蓝牙状态监测广播
/**
 * author November
 * time 2022/3/28 9:47
 * desc 蓝牙状态监测广播
 */
public class BluetoothMonitorReceiver extends BroadcastReceiver {

    private final String TAG = BluetoothMonitorReceiver.class.getSimpleName();

    private BluetoothMonitorCallBack callBack;

    public BluetoothMonitorReceiver(BluetoothMonitorCallBack callBack) {
        this.callBack = callBack;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (null != action) {
            switch (action) {
                case BluetoothAdapter.ACTION_STATE_CHANGED:
                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
                    switch (blueState) {
                        case BluetoothAdapter.STATE_TURNING_ON:
                            Log.e(TAG, "蓝牙正在打开!");
                            callBack.onOpening();
                            break;
                        case BluetoothAdapter.STATE_ON:
                            Log.e(TAG, "蓝牙已打开!");
                            callBack.onOpened();
                            break;
                        case BluetoothAdapter.STATE_TURNING_OFF:
                            Log.e(TAG, "蓝牙正在关闭!");
                            callBack.onClosing();
                            break;
                        case BluetoothAdapter.STATE_OFF:
                            Log.e(TAG, "蓝牙已关闭!");
                            callBack.onClosed();
                            break;
                    }
                    break;
                case BluetoothDevice.ACTION_ACL_CONNECTED:
                    Log.e(TAG, "蓝牙设备已连接!");
                    callBack.onConnected();
                    break;
                case BluetoothDevice.ACTION_ACL_DISCONNECTED:
                    Log.e(TAG, "蓝牙设备已断开!");
                    callBack.onDisconnected();
                    break;
            }
        }
    }
}
  1. 注册广播(界面结束时需要解注册)
IntentFilter intentFilter = new IntentFilter();
// 监视蓝牙关闭和打开的状态
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
// 监视蓝牙设备与APP连接的状态
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
registerReceiver(this.mBluetoothMonitorReceiver, intentFilter);

通信

  1. 方式一
  • 创建读取数据接口
/**
 * author November
 * time 2022/3/11 16:10
 * desc 读取数据接口
 */
public interface ReadCallBack {

    /** 开始读取 */
    default void onStarted() {
    }

    /**
     * 读取结果
     *
     * @param isSuccess
     * @param content
     */
    void onFinished(boolean isSuccess, String content);

}
  • 创建读取数据线程
/**
 * author November
 * time 2022/3/11 16:07
 * desc 读取线程
 */
public class ReadTask extends AsyncTask<String, Integer, String> {

    private static final String TAG = ReadTask.class.getSimpleName();

    private ReadCallBack callBack;

    private BluetoothSocket socket;

    public ReadTask(ReadCallBack callBack, BluetoothSocket socket) {
        this.callBack = callBack;
        this.socket = socket;
    }

    @Override
    protected String doInBackground(String... strings) {
        BufferedInputStream inputStream = null;
        try {
            StringBuilder stringBuffer = new StringBuilder();
            inputStream = new BufferedInputStream(socket.getInputStream());

            int length = 0;
            byte[] buf = new byte[1024];
            while ((length = inputStream.read()) != -1) {
                stringBuffer.append(new String(buf, 0, length));
            }
            return stringBuffer.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "读取失败!";
    }

    @Override
    protected void onPreExecute() {
        Log.e(TAG, "开始读取数据!");
        if (callBack != null) {
            callBack.onStarted();
        }
    }

    @Override
    protected void onPostExecute(String s) {
        Log.e(TAG, "完成读取数据!");
        if ("读取失败!".equals(s)) {
            callBack.onFinished(false, s);
        } else {
            callBack.onFinished(true, s);
        }
    }
}
  • 创建写入数据接口
/**
 * author November
 * time 2022/3/11 16:38
 * desc 写入数据接口
 */
public interface WriteCallBack {

    /** 开始写入 */
    default void onStarted() {
    }

    /**
     * 写入结果
     *
     * @param isSuccess
     * @param hint
     */
    void onFinished(boolean isSuccess, String hint);
    
}
  • 创建写入数据线程
/**
 * author November
 * time 2022/3/11 16:35
 * desc 写入
 */
public class WriteTask extends AsyncTask<String, Integer, String> {

    private static final String TAG = WriteTask.class.getSimpleName();

    private WriteCallBack callBack;

    private BluetoothSocket socket;

    public WriteTask(WriteCallBack callBack, BluetoothSocket socket) {
        this.callBack = callBack;
        this.socket = socket;
    }

    @Override
    protected String doInBackground(String... strings) {
        String string = strings[0];
        OutputStream outputStream = null;
        try {
            outputStream = socket.getOutputStream();
            outputStream.write(string.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
            return "发送失败!";
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "发送成功!";
    }

    @Override
    protected void onPreExecute() {
        Log.e(TAG, "开始写入数据!");
        if (callBack != null) {
            callBack.onStarted();
        }
    }

    @Override
    protected void onPostExecute(String s) {
        Log.e(TAG, "完成写入数据!");
        if (callBack != null) {
            if ("发送成功!".equals(s)) {
                callBack.onFinished(true, s);
            } else {
                callBack.onFinished(false, s);
            }
        }
    }
}
  1. 方式二(因项目功能需要而编写)
  • 创建连接操作接口
/**
 * author November
 * time 2022/3/28 10:20
 * desc 连接操作回调
 */
public interface ConnectedOperationCallBack {

    /**
     * 读取成功
     *
     * @param content 内容
     */
    default void onReadSuccess(String content) {
    }

    /**
     * 读取失败
     */
    default void onReadFile() {
    }

    /**
     * 写入成功
     */
    default void onWriteSuccess() {
    }

    /**
     * 写入失败
     */
    default void onWriteFile() {
    }

}
  • 创建连接线程
/**
 * author November
 * time 2022/3/28 10:06
 * desc 连接线程操作输入与输出
 */
public class ConnectedThread extends Thread {

    private final BluetoothSocket mBluetoothSocket;
    private final InputStream mInputStream;
    private final OutputStream mOutputStream;
    private ConnectedOperationCallBack mOperationCallBack;
    private boolean isRead = true;

    public ConnectedThread(BluetoothSocket bluetoothSocket, ConnectedOperationCallBack callBack) {
        this.mBluetoothSocket = bluetoothSocket;
        this.mOperationCallBack = callBack;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
        try {
            tmpIn = mBluetoothSocket.getInputStream();
            tmpOut = mBluetoothSocket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mInputStream = tmpIn;
        mOutputStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];
        int bytes = 0;

        //监听输入流以备获取数据
        while (isRead) {
            try {
                bytes = mInputStream.read(buffer);
                if (bytes != -1) {
                    String string = new String(buffer, 0, bytes, "utf-8");
                    if (null != mOperationCallBack) {
                        mOperationCallBack.onReadSuccess(string.substring(0, 7));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (null != mOperationCallBack) {
                    mOperationCallBack.onReadFile();
                }
            }
            try {
                //线程睡眠20ms以避免过于频繁工作  50ms->20ms 2017.12.2
                //导致UI处理发回的数据不及时而阻塞
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 写入
     *
     * @param buffer
     */
    public void write(byte[] buffer) {
        try {
            mOutputStream.write(buffer);
            if (null != mOperationCallBack) {
                mOperationCallBack.onWriteSuccess();
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (null != mOperationCallBack) {
                mOperationCallBack.onWriteFile();
            }
        }
    }

    /**
     * 取消连接
     */
    public void cancel() {
        isRead = false;
        try {
            mInputStream.close();
            mOutputStream.close();
            mBluetoothSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BluetoothSocket对象

问题

在项目中需要连接蓝牙电子秤进行重量获取,通过在“我的”模块进行蓝牙扫描、配对与连接,之后在订单模块结算时进行重量获取。此时就需要通过BluetoothSocket的getInputStream方法与getOutputStream进行数据获取处理。

分析

不同模块的Activity下都需要BluetoothSocket对象,就需要BluetoothSocket在不同模块中传递。
在Activity中传输数据有几种方法:

  • Intent.putExtra()
  • 实现Serializable
  • 实现Parcelable

但是上述方法都不适合
因为BluetoothSocket类被final关键字修饰,使得该类无法被继承,同时也没有实现Serializable与Parcelable。所以无法在Activity之间传递BluetoothSocket对象。
既然BluetoothSocket无法被传递,同时在设备连接后BluetoothSocket就需要一直存在(除主动断开与关闭APP),那就将BluetoothSocket设置为全局对象。在App的Application中声明BluetoothSocket,使App下的Activity都能使用。

解决方法

  1. 创建一个Application对象
/**
 * author November
 * time 2022/4/8 9:51
 * desc Application
 */
public class BaseApplication extends Application {

    private BluetoothSocket mBluetoothSocket;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public BluetoothSocket getBluetoothSocket() {
        return mBluetoothSocket;
    }

    public void setBluetoothSocket(BluetoothSocket bluetoothSocket) {
        this.mBluetoothSocket = bluetoothSocket;
    }
}
  1. 在AndroidManidest中进行注册
<application
    android:name=".ui.BaseApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.MyApplication">
    <activity android:name="com.november.bluetoothdemo.ui.MyDeviceActivity"></activity>
    <activity android:name="com.november.bluetoothdemo.ui.MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
  1. 连接成功后将BluetoothSocket传入到Application中
mApplication= (BaseApplication) getApplication();
mApplication.setBluetoothSocket(socket);
  1. 在使用时获取Application的mBluetoothSocket
mApplication= (BaseApplication) getApplication();
mBluetoothSocket= mApplication.getBluetoothSocket());

总结

通过全局变量可以实现多模块/多Activity之间对BluetoothSocket的赋值、获取与使用,保证了对象的唯一性,同时因为Application生命周期较长,对象占用资源的时间也长,所以应当根据功能需求来决定具体实现方案。

拓展

获取设备类型

通过扫描我们可以得到BluetoothDevice对象,其中BluetoothDevice.getType()方法,可以获取到远程设备的蓝牙设备类型,其中包括:

  • DEVICE_TYPE_UNKNOWN(未知类型)
  • DEVICE_TYPE_CLASSIC(传统类型)
  • DEVICE_TYPE_LE(BLE类型)
  • DEVICE_TYPE_DUAL(双模式类型传统类型与BLE类型)

上述类型并不满足项目需求,我们需要的是更加详细的设备类型,如游戏手柄、打印机、蓝牙耳机等,具体实现效果需要与系统蓝牙一致。
Android蓝牙开发记录_第1张图片

想要获取到详细的设备类型可以通过BluetoothDevice的getBluetoothClass方法来获取到蓝牙类,通过BluetoothClass的getMajorDeviceClass方法获得主要设备类型,通过BluetoothClass的getDeviceClass方法获得主要和次要设备类型。

//获得蓝牙主要设备类型
BluetoothDevice.getBluetoothClass().getMajorDeviceClass();
//获得蓝牙主要和次要设备类型
BluetoothDevice.getBluetoothClass().getDeviceClass();

其中主要类型如下类型(需要次要设备类型请参考BluetoothClass类)

/**
 * Defines all major device class constants.
 * 

See {@link BluetoothClass.Device} for minor classes. */ public static class Major { private static final int BITMASK = 0x1F00; public static final int MISC = 0x0000; public static final int COMPUTER = 0x0100; public static final int PHONE = 0x0200; public static final int NETWORKING = 0x0300; public static final int AUDIO_VIDEO = 0x0400; public static final int PERIPHERAL = 0x0500; public static final int IMAGING = 0x0600; public static final int WEARABLE = 0x0700; public static final int TOY = 0x0800; public static final int HEALTH = 0x0900; public static final int UNCATEGORIZED = 0x1F00; }

创建蓝牙类型工具类,根据类型返回对应设备的图标(如果需要更详细的划分可以自己根据需求实现)

/**
 * author November
 * time 2022/4/25 14:44
 * desc 蓝牙类型工具类
 */
public class BluetoothTypeUtils {

    /**
     * 根据蓝牙设备类型返回相应的设备图标
     *
     * @param bluetoothClass
     * @return
     */
    public static int getDeviceType(BluetoothClass bluetoothClass) {
        if (null == bluetoothClass) {
            return R.mipmap.icon_bluetooth;
        }
        switch (bluetoothClass.getMajorDeviceClass()) {
            case BluetoothClass.Device.Major.PHONE:
                return R.mipmap.icon_phone;
            case BluetoothClass.Device.Major.COMPUTER:
                return R.mipmap.icon_computer;
            case BluetoothClass.Device.Major.PERIPHERAL:
                return R.mipmap.icon_printer;
            case BluetoothClass.Device.Major.AUDIO_VIDEO:
                return R.mipmap.icon_earphone;
            default:
                return R.mipmap.icon_bluetooth;
        }
    }
}

实现效果
Android蓝牙开发记录_第2张图片

后续将会提供Demo连接,如有错误,欢迎指正!

你可能感兴趣的:(android)