Android蓝牙开发—经典蓝牙详细开发流程[通俗易懂]

Android蓝牙开发—经典蓝牙详细开发流程[通俗易懂]    Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别本文是针对经典蓝牙开发的,如果是BLE(低功耗)蓝牙开发,可以看Android蓝牙开发—BLE(低功耗)蓝牙详细开发流程开发流程开启蓝牙 扫描蓝牙 …

大家好,又见面了,我是你们的朋友全栈君。

       Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

本文是针对经典蓝牙开发的,如果是BLE(低功耗)蓝牙开发,可以看Android蓝牙开发—BLE(低功耗)蓝牙详细开发流程

开发流程

  • 开启蓝牙
  • 扫描蓝牙
  • 配对蓝牙
  • 连接蓝牙
  • 通信

开启蓝牙

1.获取BluetoothAdapter对象

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

2.判断设备是否支持蓝牙

/**
 * 设备是否支持蓝牙  true为支持
 * @return
 */
public boolean isSupportBlue(){
    return mBluetoothAdapter != null;
}

3.判断蓝牙是否开启

/**
 * 蓝牙是否打开   true为打开
 * @return
 */
public boolean isBlueEnable(){
    return isSupportBlue() && mBluetoothAdapter.isEnabled();
}

4.开启蓝牙

  • 异步自动开启蓝牙
/**
 * 自动打开蓝牙(异步:蓝牙不会立刻就处于开启状态)
 * 这个方法打开蓝牙不会弹出提示
 */
public void openBlueAsyn(){
    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);
}

5.权限处理

  • 处理6.0以下版本的权限

    在AndroidManifest里面添加权限

<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  • 处理6.0以上版本的权限

    (1)在AndroidManifest里面添加权限

<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--模糊定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--精准定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    (2)动态检查权限

/**
 * 检查权限
 */
private void checkPermissions() {
    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
    List<String> permissionDeniedList = new ArrayList<>();
    for (String permission : permissions) {
        int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
        if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
            onPermissionGranted(permission);
        } else {
            permissionDeniedList.add(permission);
        }
    }
    if (!permissionDeniedList.isEmpty()) {
        String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
        ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
    }
}

/**
 * 权限回调
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public final void onRequestPermissionsResult(int requestCode,
                                             @NonNull String[] permissions,
                                             @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_CODE_PERMISSION_LOCATION:
            if (grantResults.length > 0) {
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        onPermissionGranted(permissions[i]);
                    }
                }
            }
            break;
    }
}

    (3)开启GPS

/**
 * 开启GPS
 * @param permission
 */
private void onPermissionGranted(String permission) {
    switch (permission) {
        case Manifest.permission.ACCESS_FINE_LOCATION:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {
                new AlertDialog.Builder(this)
                        .setTitle("提示")
                        .setMessage("当前手机扫描蓝牙需要打开定位功能。")
                        .setNegativeButton("取消",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        finish();
                                    }
                                })
                        .setPositiveButton("前往设置",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                                        startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);
                                    }
                                })

                        .setCancelable(false)
                        .show();
            } else {
                //GPS已经开启了
            }
            break;
    }
}

    (4)检查GPS是否开启

/**
 * 检查GPS是否打开
 * @return
 */
private boolean checkGPSIsOpen() {
    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    if (locationManager == null)
        return false;
    return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
}

扫描蓝牙

1.扫描周围蓝牙设备(配对上的设备有可能扫描不出来)

/**
 * 扫描的方法 返回true 扫描成功
 * 通过接收广播获取扫描到的设备
 * @return
 */
public boolean scanBlue(){
    if (!isBlueEnable()){
        Log.e(TAG, "Bluetooth not enable!");
        return false;
    }

    //当前是否在扫描,如果是就取消当前的扫描,重新扫描
    if (mBluetoothAdapter.isDiscovering()){
        mBluetoothAdapter.cancelDiscovery();
    }

    //此方法是个异步操作,一般搜索12秒
    return mBluetoothAdapter.startDiscovery();
}

2.取消扫描蓝牙

/**
 * 取消扫描蓝牙
 * @return  true 为取消成功
 */
public boolean cancelScanBule(){
    if (isSupportBlue()){
        return mBluetoothAdapter.cancelDiscovery();
    }
    return true;
}

3.通过广播的方式接收扫描结果

    (1)注册广播

IntentFilter filter1 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED);
IntentFilter filter2 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
IntentFilter filter3 = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(scanBlueReceiver,filter1);
registerReceiver(scanBlueReceiver,filter2);
registerReceiver(scanBlueReceiver,filter3);

    (2)接收广播

/**
 *扫描广播接收类
 * Created by zqf on 2018/7/6.
 */

public class ScanBlueReceiver extends BroadcastReceiver {
    private static final String TAG = ScanBlueReceiver.class.getName();
    private ScanBlueCallBack callBack;

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

    //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
    @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;
        }
    }
}

配对蓝牙

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, "attemp to bond:" + device.getName());
        try {
            Method createBondMethod = device.getClass().getMethod("createBond");
            Boolean returnValue = (Boolean) createBondMethod.invoke(device);
            returnValue.booleanValue();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.e(TAG, "attemp to bond fail!");
        }
    }
}

2.取消配对

/**
 * 取消配对(取消配对成功与失败通过广播返回 也就是配对失败)
 * @param device
 */
public void cancelPinBule(BluetoothDevice device){
    if (device == null){
        Log.d(TAG, "cancel bond device null");
        return;
    }
    if (!isBlueEnable()){
        Log.e(TAG, "Bluetooth not enable!");
        return;
    }
    //判断设备是否配对,没有配对就不用取消了
    if (device.getBondState() != BluetoothDevice.BOND_NONE) {
        Log.d(TAG, "attemp to cancel bond:" + device.getName());
        try {
            Method removeBondMethod = device.getClass().getMethod("removeBond");
            Boolean returnValue = (Boolean) removeBondMethod.invoke(device);
            returnValue.booleanValue();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.e(TAG, "attemp to cancel bond fail!");
        }
    }
}

3.通过广播的方式接收配对结果

    (1)注册广播

IntentFilter filter4 = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
IntentFilter filter5 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(pinBlueReceiver,filter4);
registerReceiver(pinBlueReceiver,filter5);

    (2)接收广播

/**配对广播接收类
 * Created by zqf on 2018/7/7.
 */

public class PinBlueReceiver extends BroadcastReceiver {
    private String pin = "0000";  //此处为你要连接的蓝牙设备的初始密钥,一般为1234或0000
    private static final String TAG = PinBlueReceiver.class.getName();
    private PinBlueCallBack callBack;

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

    //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "action:" + action);
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

        if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){
            try {
                callBack.onBondRequest();
                //1.确认配对
//                ClsUtils.setPairingConfirmation(device.getClass(), device, true);
                Method setPairingConfirmation = device.getClass().getDeclaredMethod("setPairingConfirmation",boolean.class);
                setPairingConfirmation.invoke(device,true);
                //2.终止有序广播
                Log.d("order...", "isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());
                abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
                //3.调用setPin方法进行配对...
//                boolean ret = ClsUtils.setPin(device.getClass(), device, pin);
                Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class});
                Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{pin.getBytes()});
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
            switch (device.getBondState()) {
                case BluetoothDevice.BOND_NONE:
                    Log.d(TAG, "取消配对");
                    callBack.onBondFail(device);
                    break;
                case BluetoothDevice.BOND_BONDING:
                    Log.d(TAG, "配对中");
                    callBack.onBonding(device);
                    break;
                case BluetoothDevice.BOND_BONDED:
                    Log.d(TAG, "配对成功");
                    callBack.onBondSuccess(device);
                    break;
            }
        }
    }
}

连接蓝牙

经典蓝牙连接相当于socket连接,是个非常耗时的操作,所以应该放到子线程中去完成。

1.连接线程

/**连接线程
 * Created by zqf on 2018/7/7.
 */

public class ConnectBlueTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {
    private static final String TAG = ConnectBlueTask.class.getName();
    private BluetoothDevice bluetoothDevice;
    private ConnectBlueCallBack callBack;

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

    @Override
    protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {
        bluetoothDevice = bluetoothDevices[0];
        BluetoothSocket socket = null;
        try{
            Log.d(TAG,"开始连接socket,uuid:" + ClassicsBluetooth.UUID);
            socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(ClassicsBluetooth.UUID));
            if (socket != null && !socket.isConnected()){
                socket.connect();
            }
        }catch (IOException e){
            Log.e(TAG,"socket连接失败");
            try {
                socket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
                Log.e(TAG,"socket关闭失败");
            }
        }
        return socket;
    }

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

    @Override
    protected void onPostExecute(BluetoothSocket bluetoothSocket) {
        if (bluetoothSocket != null && bluetoothSocket.isConnected()){
            Log.d(TAG,"连接成功");
            if (callBack != null) callBack.onConnectSuccess(bluetoothDevice, bluetoothSocket);
        }else {
            Log.d(TAG,"连接失败");
            if (callBack != null) callBack.onConnectFail(bluetoothDevice, "连接失败");
        }
    }
}

2.启动连接线程

/**
 * 连接 (在配对之后调用)
 * @param device
 */
public void connect(BluetoothDevice device, ConnectBlueCallBack callBack){
    if (device == null){
        Log.d(TAG, "bond device null");
        return;
    }
    if (!isBlueEnable()){
        Log.e(TAG, "Bluetooth not enable!");
        return;
    }
    //连接之前把扫描关闭
    if (mBluetoothAdapter.isDiscovering()){
        mBluetoothAdapter.cancelDiscovery();
    }
    new ConnectBlueTask(callBack).execute(device);
}

3.判断是否连接成功

/**
 * 蓝牙是否连接
 * @return
 */
public boolean isConnectBlue(){
    return mBluetoothSocket != null && mBluetoothSocket.isConnected();
}

4.断开连接

/**
 * 断开连接
 * @return
 */
public boolean cancelConnect(){
    if (mBluetoothSocket != null && mBluetoothSocket.isConnected()){
        try {
            mBluetoothSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    mBluetoothSocket = null;
    return true;
}

5.MAC地址连接

/**
 * 输入mac地址进行自动配对
 * 前提是系统保存了该地址的对象
 * @param address
 * @param callBack
 */
public void connectMAC(String address, ConnectBlueCallBack callBack) {
    if (!isBlueEnable()){
        return ;
    }
    BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice(address);
    connect(btDev, callBack);
}

通信

1.读取数据线程

/**读取线程
 * Created by zqf on 2018/7/7.
 */

public class ReadTask extends AsyncTask<String, Integer, String> {
    private static final String TAG = ReadTask.class.getName();
    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 in = null;
        try {
            StringBuffer sb = new StringBuffer();
            in = new BufferedInputStream(socket.getInputStream());

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

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

    @Override
    protected void onPostExecute(String s) {
        Log.d(TAG,"完成读取数据");
        if (callBack != null){
            if ("读取失败".equals(s)){
                callBack.onFinished(false, s);
            }else {
                callBack.onFinished(true, s);
            }
        }
    }
}

2.写入数据线程

/**写入线程
 * Created by zqf on 2018/7/7.
 */

public class WriteTask extends AsyncTask<String, Integer, String>{
    private static final String TAG = WriteTask.class.getName();
    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) {
            Log.e("error", "ON RESUME: Exception during write.", e);
            return "发送失败";
        }finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "发送成功";


    }

    @Override
    protected void onPreExecute() {
        if (callBack != null) callBack.onStarted();
    }

    @Override
    protected void onPostExecute(String s) {
        if (callBack != null){
            if ("发送成功".equals(s)){
                callBack.onFinished(true, s);
            }else {
                callBack.onFinished(false, s);
            }

        }
    }
}

 

以上就是经典蓝牙的开发流程和部分代码,后期会提供demo下载。若有不当之处,请留言讨论,一起学习进步。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148078.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月15日 下午3:46
下一篇 2022年6月15日 下午4:00


相关推荐

  • git命令–切换分支[通俗易懂]

    git命令–切换分支[通俗易懂]&gt;我们在日常开发中,有时需要从github或者gitee上拉取新项目,但是拉取的那个项目可能有很多分支,然后本地拉取后只有一个默认分支(一般是master)。甚至可能只有一个readme.md文件。。 &gt;如果我们想查看远程的其他分支该怎么办呢? **gitbranch**&gt;首先进入项目根目录(有个.git文件的那个目录),执行`gitbranch`命…

    2022年6月20日
    36
  • Python 股票历史数据的获取

    Python 股票历史数据的获取本文主要讨论的是pytho免费股票数据的获取及处理。国内提供股票数据的接口如sinajs,money.163.com,yahoo,它们提供的API接口不同,每家提供的数据大同小异,可以选择一家的数据来处理。

    2022年6月24日
    44
  • visual studio新手使用教程「建议收藏」

    visual studio新手使用教程「建议收藏」目录1.下载安装VisualStudio2.新建工程3.新建项目4.添加源文件5.编译一闪而退的问题6.使用scanf函数报错问题7.VS常用调试程序快捷键8.VS的调试窗口9.工具->选项对于刚刚接触编程的新手来说,最先需要熟练掌握的当然是语言之祖——C语言,而C语言编译器首当其冲的必然是visualstudio,小编入门使用VC6.0,…

    2022年6月15日
    60
  • 拉氏变换 & 传递函数

    拉氏变换 & 传递函数拉氏变换和传递函数

    2026年3月19日
    4
  • 3. java缓存-线程内缓存guava cache

    3. java缓存-线程内缓存guava cacheguavacache的缓存结构常用的guavacache缓存根据上图中的缓存框架,我们常用的一些缓存实例有:LocalManualCache和LocalLoadingCache,两者唯一的区别就是LocalLoadingCacheextendsLocalManualCacheimplementsLoadingCache&amp;amp;amp;lt;K,V&amp;amp;amp;gt;接口。LocalManualCache…

    2022年10月4日
    4
  • 细说php第四版笔记,细说PHP 学习笔记(二)

    细说php第四版笔记,细说PHP 学习笔记(二)一 课程内容 什么是 HTTP 协议 web 的工作原理 多种网站开发平台比较 LAMP 网站开发组合概述二 HTTP 协议 URL uniformResou 统一资源定位符 组成 协议 域名 主机 端口 附加信息 超文本传输协议 HTTP HyperTextTra 是互联网上应用最为广泛的一种网络协议 所有的 WWW 文件都必须遵守这个标准 设计 HTTP 最初

    2026年3月18日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号