安卓藍(lán)牙3.0開發(fā)詳解

前言

目前比較通用的藍(lán)牙類型主要有兩種,一種為手機(jī)端使用的經(jīng)典藍(lán)牙3.0,一種是當(dāng)前比較流行在穿戴設(shè)備上的低功耗藍(lán)牙,即常說的BLE藍(lán)牙。關(guān)于BLE藍(lán)牙開發(fā)可以查看另外一篇文章
安卓BLE藍(lán)牙開發(fā)詳解

經(jīng)典藍(lán)牙采用服務(wù)器客戶端模式進(jìn)行通信,手機(jī)端既可以是服務(wù)端也可以是客戶端。
安卓手機(jī)經(jīng)典藍(lán)牙開發(fā)通信流程如下:

添加權(quán)限

<!-- 藍(lán)牙權(quán)限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
為適配安卓6.0以及以上版本需要添加一個模糊定位的權(quán)限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

打開藍(lán)牙

// 詢問打開藍(lán)牙
if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(
            BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, 1);
}
    
// 申請打開藍(lán)牙回調(diào)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // TODO Auto-generated method stub
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1) {
        if (resultCode == RESULT_OK) {
            Toast.makeText(this, "藍(lán)牙已經(jīng)開啟", Toast.LENGTH_SHORT).show();
        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, "沒有藍(lán)牙權(quán)限", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

BluetoothAdapter類封裝了關(guān)于安卓系統(tǒng)藍(lán)牙的操作。其獲取方式為:

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // 設(shè)備不支持藍(lán)牙
}
搜索藍(lán)牙設(shè)備

安卓對于經(jīng)典藍(lán)牙的搜索采用注冊廣播的方式實現(xiàn)。

// 注冊掃描監(jiān)聽器,一般寫在onCreate()函數(shù)中執(zhí)行
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(searchReceiver, filter);

// 監(jiān)聽器監(jiān)聽藍(lán)牙掃描結(jié)果
BroadcastReceiver searchReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        BluetoothDevice device = intent
                .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                if (device.getName() == null) {     
                    return;
                } 
            
                if(device.getName().equals("目標(biāo)設(shè)備藍(lán)牙名")) {
                    targetDevice = device;
                }
            }
        }
    }
};
//啟動搜索周邊設(shè)備
if (mBluetoothAdapter.isDiscovering()) {
    mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();

注:當(dāng)注冊廣播后需要在銷毀界面時調(diào)用unregisterReceiver(searchReceiver);解除注冊防止內(nèi)存泄露。

連接設(shè)備

當(dāng)在廣播監(jiān)聽結(jié)果中獲取到目標(biāo)設(shè)備之后,進(jìn)行連接設(shè)備。安卓啟動連接的方式通常需啟動服務(wù)端線程作為監(jiān)聽,然后創(chuàng)建客戶端線程進(jìn)行連接。連接方法如下:

    //啟動藍(lán)牙連接
    public void connect(BluetoothDevice targetDevice) {
        
        this.targetDevice = targetDevice;
                
        //啟動服務(wù)端線程
        serverThread = new ServerThread();  
        serverThread.start();
        
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            
            e.printStackTrace();
        }
        //啟動客戶端線程
        clientThread = new ClientThread();  
        clientThread.start();
                    
    }
    
    
// 服務(wù)端線程
private class ServerThread extends Thread {

    public void run() {
        try {
            // 創(chuàng)建一個藍(lán)牙服務(wù)器 參數(shù)分別:服務(wù)器名稱、UUID
            mserverSocket = mBluetoothAdapter
                    .listenUsingRfcommWithServiceRecord(
                            "btspp",
                            UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));

            // 接受客戶端的連接請求
            socket = mserverSocket.accept();
            
        } catch (Exception e) {
            
            e.printStackTrace();
        }
    }
    
}

// 客戶端線程
private class ClientThread extends Thread {
    
    public void run() {
        try {

            Thread.sleep(500);

            socket = targetDevice.createRfcommSocketToServiceRecord(UUID
                    .fromString("00001101-0000-1000-8000-00805F9B34FB"));
            //開始連接
            socket.connect();
                            
            //連接成功后,啟動數(shù)據(jù)通信線程
            isReading = true;
            new ReadThread().start();
        } catch (Exception e) {

        }
    }
}

通信過程

安卓經(jīng)典藍(lán)牙的通信采用Socket通信機(jī)制。Socket通信線程如下:

private class ReadThread extends Thread {
    
    public void run() {
        Log.e("TAG", "開啟通信線程");
        byte[] buffer = new byte[1024];
        int bytes;
        in = null;
        try {
            in  = socket.getInputStream();
            out = socket.getOutputStream();
        } catch (Exception e) {
            Log.e("TAG", "獲取流出錯");
        }           
        while (isReading) {
            try {
                if ((bytes = in.read(buffer)) > 0) {

                    //讀取到的數(shù)據(jù)值
                    byte[] rece = new byte[bytes];
            
                }
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    Log.e("關(guān)閉流", "");
                    in.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
                    
                break;
            }
        }
        
        Log.e(TAG, "DataRead線程關(guān)閉");
    }
}

Socket通信線程在沒有斷開連接之前一直在Socket流中讀取數(shù)據(jù)。通常在與設(shè)備通信過程中不僅要讀取設(shè)備數(shù)據(jù),同時需要向設(shè)備寫入數(shù)據(jù),發(fā)送數(shù)據(jù)的方法如下:

//發(fā)送數(shù)據(jù)方法
public void sendData(byte[] cmd) {
    if (out != null) {
        try {
            out.write(cmd);
        } catch (IOException e) {
            Log.e(TAG, "寫入失敗");
            e.printStackTrace();
        }
    }else {
        Log.e(TAG, "建立連接失敗");
    }       
}

結(jié)語

通過以上流程可以基本完成與經(jīng)典藍(lán)牙設(shè)備的通信過程。本文采用的是手機(jī)端作為服務(wù)端,周邊藍(lán)牙設(shè)備作為客戶端的方式。若文章中存在錯誤,還請指出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,099評論 25 708
  • 公司的項目最近需要用到藍(lán)牙開發(fā)的相關(guān)內(nèi)容,因此特地查閱了Google官方文檔的內(nèi)容并進(jìn)行二次整理,希望能對需要學(xué)習(xí)...
    Chuckiefan閱讀 32,533評論 44 123
  • 藍(lán)牙 注:本文翻譯自https://developer.android.com/guide/topics/conn...
    RxCode閱讀 8,791評論 11 99
  • 前言 入職不久,也是剛剛接觸安卓開發(fā)。公司主要業(yè)務(wù)是嵌入式設(shè)備以及可穿戴設(shè)備。因此新人主要任務(wù)就是學(xué)習(xí)安卓藍(lán)牙開發(fā)...
    MrHorse1992閱讀 8,226評論 16 25
  • 在Swift語言中,訪問修飾符有五種,分別為fileprivate,private,internal,public...
    Michael_涵閱讀 359評論 0 0