android bluetooth——藍牙的開啓、搜索、配對與鏈接

android 的blt僅僅支持api 18 android4.3以上,有的功能甚至須要api 19 android4.4;
因此咱們在作blt項目以前必定要清楚可用的版本範圍。android

我要講述的是打開blt大門的操做。這些操做就是如何打開blt、如何搜索到其餘設備、如何配對選中設備、如何經過mac地址鏈接以前已經配對過的設備以及鏈接成功的兩個(或一對多個)設備如何通信。編程

在學習blt知識前要先搞清楚,blt是如何讓兩個設備之間通信的。咱們先來看一下基本步驟:api

1,打開blt。
——1)權限
——2)監聽設備是否打開blt
——3)操做,打開或者關閉blt數組

2,搜索附近設備&讓附近設備搜索到
——1)讓自身設備能夠被附近搜索到,時間最長300s;(隨着api的逐漸升級可能不止300s,作了必定的優化處理)
——2)搜索附近可鏈接的設備;過期的api在接口中回調搜索結果,新的api則要求用廣播接收搜索結果。(咱們這裏使用廣播接收結果)服務器

3,與目標設備配對
——1)對於目標設備進行配對,android系統自己會完成配對動做,而且將已經成功配對的設備的mac地址保存起來,以供下次進行自動配對使用。
——2)對進行配對過的設備自動配對。這個動做也是系統幫咱們完成的,咱們只須要根據系統給咱們的狀態來判斷這個是否已經配對就好了。app

4,與成功配對的設備進行鏈接
——1)若是要對成功配對的設備進行鏈接,則必須先創建服務器端。服務器端創建只有會線程阻塞等待客戶端的鏈接。
——2)確保創建了服務器端以後再創建客戶端。客戶端的鏈接過程也是線程阻塞的。知道鏈接成功後,服務器端收到消息,則表示配對設備已鏈接成功。socket

5,注意事項:
——1)配對成功過的設備無需再次配對,只須要從系統中拿到mac地址進行鏈接。這個根據系統返回的狀態值去判斷。
——2)搜索附近設備的操做是一個消耗內存的動做,咱們務必在鏈接設備‘以前‘中止搜索。
——3)設備的配對成功不表明鏈接成功。配對和鏈接是兩回事且配對在鏈接動做以前。
——4)當咱們的程序明確指出「不須要blt操做了」的時候,及時反註冊blt廣播,中止blt鏈接,註銷藍牙對象。而這個操做例如:程序退出、用戶無操做超時、邏輯不須要blt的鏈接了。學習

以上就是一個藍牙鏈接成功以後能夠正常傳輸信息的步驟。
接下來咱們再來看看相應的步驟在代碼邏輯中的表明。優化

1,權限。ui

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

2,得到藍牙管理對象。BluetoothManager的主要做用是從中獲得咱們須要的對象

//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
//首先獲取BluetoothManager
 BluetoothManager bluetoothManager=(BluetoothManager) context.getService(Context.BLUETOOTH_SERVICE);

3,得到藍牙適配器。藍牙適配器是咱們操做藍牙的主要對象,能夠從中得到配對過的藍牙集合,能夠得到藍牙傳輸對象等等

//獲取BluetoothAdapter
if (bluetoothManager != null)
    BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();

3,註冊廣播來接收藍牙配對信息。在藍牙開啓前調用

// 用BroadcastReceiver來取得搜索結果
        IntentFilter intent = new IntentFilter();
        intent.addAction(BluetoothDevice.ACTION_FOUND);//搜索發現設備
        intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//狀態改變
        intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//行動掃描模式改變了
        intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//動做狀態發生了變化
        context.registerReceiver(searchDevices, intent);

 /**
     * 藍牙接收廣播
     */
    private BroadcastReceiver searchDevices = new BroadcastReceiver() {
        //接收
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Bundle b = intent.getExtras();
            Object[] lstName = b.keySet().toArray();

            // 顯示全部收到的消息及其細節
            for (int i = 0; i < lstName.length; i++) {
                String keyName = lstName[i].toString();
                Log.e("bluetooth", keyName + ">>>" + String.valueOf(b.get(keyName)));
            }
            BluetoothDevice device;
            // 搜索發現設備時,取得設備的信息;注意,這裏有可能重複搜索同一設備
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                onRegisterBltReceiver.onBluetoothDevice(device);
            }
            //狀態改變時
            else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                switch (device.getBondState()) {
                    case BluetoothDevice.BOND_BONDING://正在配對
                        Log.d("BlueToothTestActivity", "正在配對......");
                        onRegisterBltReceiver.onBltIng(device);
                        break;
                    case BluetoothDevice.BOND_BONDED://配對結束
                        Log.d("BlueToothTestActivity", "完成配對");
                        onRegisterBltReceiver.onBltEnd(device);
                        break;
                    case BluetoothDevice.BOND_NONE://取消配對/未配對
                        Log.d("BlueToothTestActivity", "取消配對");
                        onRegisterBltReceiver.onBltNone(device);
                    default:
                        break;
                }
            }
        }
    };
  

4,反註冊廣播和清楚藍牙鏈接。在不須要使用藍牙的時候調用

/**
     * 反註冊廣播取消藍牙的配對
     *
     * @param context
     */
    public void unregisterReceiver(Context context) {
        context.unregisterReceiver(searchDevices);
        if (mBluetoothAdapter != null)
            mBluetoothAdapter.cancelDiscovery();
    }

   

6,搜索藍牙。搜索後添加到自定義的集合中以供自身程序的邏輯使用。注意:搜索到的結果會在廣播中回調過來,這裏不作回調。若使用過期的接口,則在當前邏輯裏回調結果。咱們這裏使用廣播,不建議使用過期的方法。

/**
     * 搜索藍牙設備
     * 經過調用BluetoothAdapter的startLeScan()搜索BLE設備。
     * 調用此方法時須要傳入 BluetoothAdapter.LeScanCallback參數。
     * 所以你須要實現 BluetoothAdapter.LeScanCallback接口,BLE設備的搜索結果將經過這個callback返回。
     * <p/>
     * 因爲搜索須要儘可能減小功耗,所以在實際使用時須要注意:
     * 一、當找到對應的設備後,當即中止掃描;
     * 二、不要循環搜索設備,爲每次搜索設置適合的時間限制。避免設備不在可用範圍的時候持續不停掃描,消耗電量。
     * <p/>
     * 若是你只須要搜索指定UUID的外設,你能夠調用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
     * 其中UUID數組指定你的應用程序所支持的GATT Services的UUID。
     * <p/>
     * 注意:搜索時,你只能搜索傳統藍牙設備或者BLE設備,二者徹底獨立,不可同時被搜索。
     */
    private boolean startSearthBltDevice(Context context) {
        //開始搜索設備,當搜索到一個設備的時候就應該將它添加到設備集合中,保存起來
        checkBleDevice(context);
        //若是當前發現了新的設備,則中止繼續掃描,當前掃描到的新設備會經過廣播推向新的邏輯
        if (getmBluetoothAdapter().isDiscovering())
            stopSearthBltDevice();
        Log.i("bluetooth", "本機藍牙地址:" + getmBluetoothAdapter().getAddress());
        //開始搜索
        mBluetoothAdapter.startDiscovery();
        //這裏的true並非表明搜索到了設備,而是表示搜索成功開始。
        return true;
    }

7,中止搜索藍牙設備

public boolean stopSearthBltDevice() {
        //暫停搜索設備
        if(mBluetoothAdapter!=null)
        return mBluetoothAdapter.cancelDiscovery();
    }

8,鏈接藍牙。注意:鏈接藍牙的前提是咱們已經創建好了藍牙服務器端。因此,這裏咱們先創建藍牙服務器
——1)先創建藍牙服務器端

/**
     * 這個操做應該放在子線程中,由於存在線程阻塞的問題
     */
    public void run(Handler handler) {
        //服務器端的bltsocket須要傳入uuid和一個獨立存在的字符串,以便驗證,一般使用包名的形式
        BluetoothServerSocket  bluetoothServerSocket = tmBluetoothAdapter.listenUsingRfcommWithServiceRecord("com.bluetooth.demo", BltContant.SPP_UUID);
        while (true) {
            try {
                //注意,當accept()返回BluetoothSocket時,socket已經鏈接了,所以不該該調用connect方法。
                //這裏會線程阻塞,直到有藍牙設備連接進來纔會往下走
                socket = getBluetoothServerSocket().accept();
                if (socket != null) {
                    BltAppliaction.bluetoothSocket = socket;
                    //回調結果通知
                    Message message = new Message();
                    message.what = 3;
                    message.obj = socket.getRemoteDevice();
                    handler.sendMessage(message);
                    //若是你的藍牙設備只是一對一的鏈接,則執行如下代碼
                    getBluetoothServerSocket().close();
                    //若是你的藍牙設備是一對多的,則應該調用break;跳出循環
                    //break;
                }
            } catch (IOException e) {
                try {
                    getBluetoothServerSocket().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                break;
            }
        }
    }
  

——2)在藍牙服務器創建以後,再進行鏈接藍牙的操做。

 /**
     * 嘗試鏈接一個設備,子線程中完成,由於會線程阻塞
     *
     * @param btDev 藍牙設備對象
     * @param handler 結果回調事件
     * @return
     */
    private void connect(BluetoothDevice btDev, Handler handler) {
        try {
            //經過和服務器協商的uuid來進行鏈接
            mBluetoothSocket = btDev.createRfcommSocketToServiceRecord(BltContant.SPP_UUID);
            if (mBluetoothSocket != null)
                //全局只有一個bluetooth,因此咱們能夠將這個socket對象保存在appliaction中
                BltAppliaction.bluetoothSocket = mBluetoothSocket;
            //經過反射獲得bltSocket對象,與uuid進行鏈接獲得的結果同樣,但這裏不提倡用反射的方法
            //mBluetoothSocket = (BluetoothSocket) btDev.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(btDev, 1);
            Log.d("blueTooth", "開始鏈接...");
            //在創建以前調用
            if (getmBluetoothAdapter().isDiscovering())
                //中止搜索
                getmBluetoothAdapter().cancelDiscovery();
            //若是當前socket處於非鏈接狀態則調用鏈接
            if (!getmBluetoothSocket().isConnected()) {
                //你應當確保在調用connect()時設備沒有執行搜索設備的操做。
                // 若是搜索設備也在同時進行,那麼將會顯著地下降鏈接速率,並很大程度上會鏈接失敗。
                getmBluetoothSocket().connect();
            }
            Log.d("blueTooth", "已經連接");
            if (handler == null) return;
            //結果回調
            Message message = new Message();
            message.what = 4;
            message.obj = btDev;
            handler.sendMessage(message);
        } catch (Exception e) {
            Log.e("blueTooth", "...連接失敗");
            try {
                getmBluetoothSocket().close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

9,自動鏈接以往配對成功的設備。注意:鏈接的前提是服務器端已開啓,若沒開啓則進行8.1的操做

/**
     * 嘗試配對和鏈接
     *
     * @param btDev
     */
    public void createBond(BluetoothDevice btDev, Handler handler) {
        if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
            //若是這個設備取消了配對,則嘗試配對
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                btDev.createBond();
            }
        } else if (btDev.getBondState() == BluetoothDevice.BOND_BONDED) {
            //若是這個設備已經配對完成,則嘗試鏈接
            connect(btDev, handler);
        }
    }
 

 /**
     * 得到系統保存的配對成功過的設備,並嘗試鏈接
     */
    public void getBltList() {
        if (getmBluetoothAdapter() == null) return;
        //得到已配對的遠程藍牙設備的集合
        Set<BluetoothDevice> devices = getmBluetoothAdapter().getBondedDevices();
        if (devices.size() > 0) {
            for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) {
                BluetoothDevice device = it.next();
                //自動鏈接已有藍牙設備
                createBond(device, null);
            }
        }
    }

注意:外界只須要調用getBltList();方法便可進行自動鏈接。

10,輸入mac地址自動鏈接設備。前提是系統原來鏈接過該地址。

 /**
     * 輸入mac地址進行自動配對
     * 前提是系統保存了該地址的對象
     *
     * @param address
     */
    public void autoConnect(String address, Handler handler) {
        if (getmBluetoothAdapter().isDiscovering()) getmBluetoothAdapter().cancelDiscovery();
        BluetoothDevice btDev = getmBluetoothAdapter().getRemoteDevice(address);
        connect(btDev, handler);
    }

11,藍牙鏈接狀態。用於咱們判斷該藍牙設備是出於:未配對仍是配對未鏈接仍是已鏈接狀態

public String bltStatus(int status) {
        String a = "未知狀態";
        switch (status) {
            case BluetoothDevice.BOND_BONDING:
                a = "鏈接中";
                break;
            case BluetoothDevice.BOND_BONDED:
                a = "鏈接完成";
                break;
            case BluetoothDevice.BOND_NONE:
                a = "未鏈接/取消鏈接";
                break;
        }
        return a;
    }

 

11,藍牙點擊事件。包括藍牙的打開,關閉,被搜索,斷開鏈接

/**
     * 藍牙操做事件
     *
     * @param context
     * @param status
     */
    public void clickBlt(Context context, int status) {
        switch (status) {
            case BltContant.BLUE_TOOTH_SEARTH://搜索藍牙設備,在BroadcastReceiver顯示結果
                startSearthBltDevice(context);
                break;
            case BltContant.BLUE_TOOTH_OPEN://本機藍牙啓用
                if (getmBluetoothAdapter() != null)
                    getmBluetoothAdapter().enable();//啓用
                break;
            case BltContant.BLUE_TOOTH_CLOSE://本機藍牙禁用
                if (getmBluetoothAdapter() != null)
                    getmBluetoothAdapter().disable();//禁用
                break;
            case BltContant.BLUE_TOOTH_MY_SEARTH://本機藍牙能夠在300s內被搜索到
                Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                context.startActivity(discoverableIntent);
                break;
            case BltContant.BLUE_TOOTH_CLEAR://本機藍牙關閉當前鏈接
                try {
                    if (getmBluetoothSocket() != null)
                        getmBluetoothSocket().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
        }
    }

 

到此,藍牙從打開到鏈接的方法都寫完了。最後咱們來總結一下。
當咱們得到到了bluetoothsocket對象的時候,咱們就能夠像使用socket編程同樣,讓兩個藍牙之間傳輸數據。甚至能夠在程序內部監聽藍牙耳機的暫停/播放/音量鍵等的點擊事件。

具體的藍牙操做,我將放在demo裏供你們學習。
demo下載  

相關文章
相關標籤/搜索