Android藍牙開發—經典藍牙詳細開發流程

  Android藍牙開發前,首先要區分是經典藍牙開發仍是BLE(低功耗)藍牙開發,它們的開發是有區別的,若是還分不清經典藍牙和BLE(低功耗)藍牙的小夥伴,能夠先看Android藍牙開發—經典藍牙和BLE(低功耗)藍牙的區別android

本文是針對經典藍牙開發的,若是是BLE(低功耗)藍牙開發,能夠看Android藍牙開發—BLE(低功耗)藍牙詳細開發流程
開發流程app

    開啓藍牙
    掃描藍牙
    配對藍牙
    鏈接藍牙
    通訊異步

開啓藍牙socket

1.獲取BluetoothAdapter對象ide

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();函數

2.判斷設備是否支持藍牙學習

    /**
     * 設備是否支持藍牙  true爲支持
     * @return
     */
    public boolean isSupportBlue(){
        return mBluetoothAdapter != null;
    }ui

3.判斷藍牙是否開啓this

    /**
     * 藍牙是否打開   true爲打開
     * @return
     */
    public boolean isBlueEnable(){
        return isSupportBlue() && mBluetoothAdapter.isEnabled();
    }.net

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下載。如有不當之處,請留言討論,一塊兒學習進步。

相關文章
相關標籤/搜索