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下载。若有不当之处,请留言讨论,一起学习进步。

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 改变Keil5所有窗口的背景颜色[通俗易懂]

    改变Keil5所有窗口的背景颜色[通俗易懂]在网上找了很多都没有找到如何更改Keil5左侧和下侧的背景颜色,后来根据一些提示找到了改背景的方法,在此分享给有需要的人。前面相信很多人都会该,不过我还是写一下,…

  • docker搭建kafka集群

    docker搭建kafka集群docker搭建kafka集群我在M1mbp上使用的以下镜像新建文件zk-kafka-docker-compose.ymlversion:”2″services:zookeeper:user:rootimage:docker.io/zookeeperports:-“12181:2181″environment:-ALLOW_ANONYMOUS_LOGIN=yesvolumes:-zoo

  • SQLyog安装教程详解

    SQLyog安装教程详解安装SQLyog的详细步骤(1)复制连接:https://pan.baidu.com/s/1IlkLChap1gYzCHo3meegew输入提取码:a1kw(2)等待下载(3)解压到新建文件夹(4)点击解压后的X64右键,以管理员的身份运行(5)选择语言Chinese(Simplified)(6)单击下一步(7)打开后需要证书姓名(Name):cr173序列号(Code):8d8120df-a5c3-4989-8f47-5afc79c56e7c或者(OR)姓名

  • QQ强制聊天代码(qq聊天代码)

    QQ加好友 点击添加我的QQ为好友uin就是QQ号你还可以在网页里添加跳转代码,打开网页自动添加你为好友哦。以下代码需要添加在head里。 ——————————————————QQ聊天tencent://Message/?Uin=5695

  • linux修改ftp目录_ftp切换目录命令

    linux修改ftp目录_ftp切换目录命令linuxFTP命令详解更新时间:2008年09月12日00:14:55作者:整理的比较全的linux下ftp命令详细说明FTP的命令行格式为:ftp-v-d-i-n-g[主机名],其中-v显示远程服务器的所有响应信息;-n限制ftp的自动登录,即不使用;.netrc文件;-d使用调试方式;-g取消全局文件名。ftp使用的内部命令如下(中括号表示可选项):1.![cmd[a…

  • Python源码阅读_python源代码文件

    Python源码阅读_python源代码文件最近想读读Python源码,任何东西学习方法基本都是一样的,先从总体框架进行了解,再从自己侧重的方面逐步深入。1.Python总体架构左边是Python提供的大量的模块、库以及用户自定义的模块。比如在执行importos时,这个os就是Python内建的模块,当然用户还可以通过自定义模块来扩展Python系统。右边是Python的运行时环境,包括对象/类型系统(Object/Type…

发表回复

您的电子邮箱地址不会被公开。

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