Android蓝牙开发—经典蓝牙详细开发流程

泪湿孤枕 提交于 2019-12-05 11:48:27

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!