基於BluetoothKit-藍牙設備不可見,藍牙方法不可調,藍牙修改本機名字,藍牙連接監聽的問題

這兩天又在搞藍牙,藍牙傷我千百遍,我待藍牙如初戀。

有位朋友說,做個appdemo,來和他的藍牙模塊進行交互。我發現我對藍牙真的是連冰山一角都還沒了解完。說說我都遇到了什麼問題吧。

1.兩個手機都打開藍牙,如果離開設置藍牙界面,難麼你會發現你們都搜索不到彼此的設備。這不是你的錯,這是谷歌的一個坑。

因爲如下:

解決:

 //設置藍牙可以被其他搜索到 
 blue.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);    

 //設置藍牙可見的時間。0:是永久可見
 blue.setDiscoverableTimeout(0);//0:藍牙永久可見

這個時候又鬱悶了。BaseAdapter的源碼裏面setDiscoverableTimeout()和setScanMode()都被/*hide*/註釋着。啊,是不是想要藍牙現身有點曲折,我們想要用這兩個方法還是有辦法的。利用反射,調用這兩個方法。

添加如下代碼:


    public void setDiscoverableTimeout(BluetoothAdapter adapter,int timeout) {
        try {
            Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);
            setDiscoverableTimeout.setAccessible(true);
            Method setScanMode =BluetoothAdapter.class.getMethod("setScanMode", int.class,int.class);
            setScanMode.setAccessible(true);

            setDiscoverableTimeout.invoke(adapter, timeout);
            setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,timeout);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

這樣就可以調用setDiscoverableTimeout()和setScanMode()方法了,就可以讓其他設備掃描到我的設備了。

2.監聽手機藍牙是否被了另一個設備匹配。

我第一次用的是這個方法

    private BroadcastReceiver stateChangeRec=new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action=intent.getAction();
            if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){
                tv_bluetooth_status.setText("藍牙連接狀態:連接成功");
                Log.i("tang","連接成功");
                //do something
            }else if(action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)){
                tv_bluetooth_status.setText("藍牙連接狀態:連接失敗");
                Log.i("tang","連接失敗");
            }
        }
    };

我會發現,在另外一臺設備向我請求匹配的時候,就已經執行ACTION_BOND_STATE_CHANGED這個操作了。然後第一反應是絕對這個方法不適合我的情況。立刻重新找方法,

於是就得到了這個:

private BroadcastReceiver stateChangeRec=new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action=intent.getAction();
            if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
                    Log.i("tang","正在配對");
                } else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    Log.i("tang","完成配對");
                    tv_bluetooth_status.setText("藍牙連接狀態:連接成功");
                } else if (device.getBondState() == BluetoothDevice.BOND_NONE) {
                    Log.i("tang","取消配對");
                }
            }
          
        }
    };

這個就是在兩個設備已經匹配完成的一個完整步驟。老夫也是鬆了一口氣。

3.還有一個,那就是修改本機藍牙設備的名字。網上都說的是setName("xx")就好。我好你個香蕉巴拉。。。

bluetoothAdapter.enable();//設置藍牙爲可用

bluetoothAdapter.setName(deviceName);//寫入你想要設置的設備名字

setName其實是發送了一個廣播,我們還需要來進行接收處理,完成整個操作。(很多博主都沒說啊,坑死個人啊!)

這兒,我們需要發送一個改名字的廣播!向全世界宣佈,我的藍牙我做主!

IntentFilter mFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
mFilter.addAction(BluetoothAdapter.EXTRA_LOCAL_NAME);
registerReceiver(mReceiver, mFilter);

下面就是接受廣播操作

//藍牙廣播接收器。修改名字時會調用。
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            tv_current_name.setText("當前藍牙名字:"+bluetoothAdapter.getName());
            String action = intent.getAction();
            if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)){
                Toast.makeText(context,"成功", Toast.LENGTH_SHORT).show();
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
                for(int i=0; i<devices.size(); i++)
                {
                    BluetoothDevice device1 = (BluetoothDevice) devices.iterator().next();
                    Log.i("tang","2-2藍牙名字="+device1.getName());
                    //System.out.println(device1.getName());
                }
            }
            else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
                Toast.makeText(context,"finished", Toast.LENGTH_SHORT).show();
            }
        }
    };

這纔是一個ojbk的操作。

這裏面還有一個坑,真的是沒找到有博主說過這個問題。setName(“xxx”)這裏面的值,是有限制的!

廣播發送的字節好像是限制早<=60個字節,所以setName的值不是無限的,(如果你要廣播發送包含了名字)。60個字節,30個漢字,包含所發送的其他信息,setName的值,限制也是跟隨你所發送的信息多少而決定的。

當藍牙名字>限制的個數的時候,廣播發送會失敗,ErrorCode=18或者1,對於BLE藍牙的ErrorCode網上也沒有人說,也搜索不到。我也不知道爲啥。(有誰知道爲啥藍牙名字>5的時候廣播開啓會失敗,且ErrorCode=18/1,麻煩留言告知一聲,謝謝!)

4.還有個誤區。我在斷開藍牙的時候使用的是disconnet(),但是我的小夥伴用他的手機和我測試的時候說,藍牙沒有斷開啊。我說不可能啊,我又添加了close,雙保險,這下沒問題了吧。我小夥伴依然說,藍牙沒斷開啊,一看。他看的是“已配對設備”,我特麼操着鍵盤給他扔過去。

已配對,不代表藍牙沒斷開,只是說明兩個設備在下次連接的時候不需要再次配對了。大家測試的時候不要進入這個誤區了。

持續更新一波,我app已經做好。小夥伴說搜索不到,讓我把設備變成從設備。我這個藍牙小白,一臉蒙圈,然後又去百度。設備分爲主設備和從設備。

話不多說,到底如何把一個設備變成從設備呢。代碼敬上:

這是一個設置廣播,打開廣播的操作。

private void sendOutSheBei() {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            settings = new AdvertiseSettings.Builder()
                    .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) //廣播模式: 低功耗,平衡,低延遲
                    .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //發射功率級別: 極低,低,中,高
                    .setConnectable(true) //能否連接,廣播分爲可連接廣播和不可連接廣播
                    .build();

            //廣播數據(必須,廣播啓動就會發送)
            advertiseData = new AdvertiseData.Builder()
                    .setIncludeDeviceName(true) //包含藍牙名稱
                    .setIncludeTxPowerLevel(true) //包含發射功率級別
                    .addManufacturerData(1, new byte[]{23, 33}) //設備廠商數據,自定義
                    .build();
            //掃描響應數據(可選,當客戶端掃描時才發送)
            scanResponse = new AdvertiseData.Builder()
                    .addManufacturerData(2, new byte[]{66, 66}) //設備廠商數據,自定義
                    .addServiceUuid(new ParcelUuid(UUID_SERVICE)) //服務UUID
                    //      .addServiceData(new ParcelUuid(UUID_SERVICE), new byte[]{2}) //服務數據,自定義
                    .build();
            mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
        }

        mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback);
        if (mBluetoothLeAdvertiser == null) {
            Toast.makeText(this, "該設備不支持藍牙低功耗從設備通訊", Toast.LENGTH_SHORT).show();
            this.finish();
            return;
        }
    }

這裏面有一點需要注意,.setIncludeDeviceName(true) //包含藍牙名稱  我開始以爲這只是是否傳遞藍牙名字的一個設置,我給它修改爲.setIncludeDeviceName(false) ,別人就搜不到我的藍牙了。你要是想讓別人搜索到你的藍牙,那你就老老實實的寫爲true。

這上面的代碼還不夠,還需要一個監聽器,監聽我們的廣播是不是打開了。

// BLE廣播Callback
    private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            Log.i("tang","BLE廣播開啓成功");
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.i("tang","BLE廣播開啓失敗,錯誤碼"+errorCode);
        }
    };

我們一定還想要和其他設備進行信息交互,哪裏監聽?第一段代碼裏面就有一個監聽回調,好的,現在敬上回調監聽代碼:

 // BLE服務端Callback
    private BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            Log.i("tang", "從設備連接狀態="+String.format(status == 0 ? (newState == 2 ? "與[%s]連接成功" : "與[%s]連接斷開") : ("與[%s]連接出錯,錯誤碼:" + status)));

            if(newState==2){
                //連接成功
                tv_bluetooth_status.setText("藍牙連接狀態:連接成功");
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        tv_bluetooth_status.setText("藍牙連接狀態:連接中斷");
                        Toast.makeText(mContext, "藍牙斷開", Toast.LENGTH_SHORT).show();
                        mBluetoothLe.disconnect();
                        mBluetoothLe.close();
                    }
                }, getPostTime(deviceTime));
            }else{
                tv_bluetooth_status.setText("藍牙連接狀態:連接中斷");
            }
        }

        @Override
        public void onServiceAdded(int status, BluetoothGattService service) {
            Log.i("tang", "從設備連接狀態="+String.format(status == 0 ? "添加服務[%s]成功" : "添加服務[%s]失敗,錯誤碼:" + status, service.getUuid()));
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            String response = "CHAR_" + (int) (Math.random() * 100); //模擬數據
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes());// 響應客戶端
            Log.i("tang","客戶端讀取Characteristic[" + characteristic.getUuid() + "]:\n" + response);
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {
            // 獲取客戶端發過來的數據
            String requestStr = new String(requestBytes);
            Log.i("tang","客戶端寫入Characteristic[" + characteristic.getUuid() + "]:\n" + requestStr);
        }

        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            String response = "DESC_" + (int) (Math.random() * 100); //模擬數據
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes()); // 響應客戶端
            Log.i("tang","客戶端讀取Descriptor[" + descriptor.getUuid() + "]:\n" + response);
        }

        @Override
        public void onDescriptorWriteRequest(final BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            // 獲取客戶端發過來的數據
            String valueStr = Arrays.toString(value);
            mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);// 響應客戶端
          

            // 簡單模擬通知客戶端Characteristic變化
            if (Arrays.toString(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE).equals(valueStr)) { //是否開啓通知
                final BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            SystemClock.sleep(3000);
                            String response = "CHAR_" + (int) (Math.random() * 100); //模擬數據
                            characteristic.setValue(response);
                            mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
                            Log.i("tang","通知客戶端改變Characteristic[" + characteristic.getUuid() + "]:\n" + response);
                            //logTv("通知客戶端改變Characteristic[" + characteristic.getUuid() + "]:\n" + response);
                        }
                    }
                }).start();
            }
        }

        @Override
        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
            Log.i("tang",String.format("onExecuteWrite:%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, execute));

        @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            Log.i("tang",String.format("onNotificationSent:%s,%s,%s", device.getName(), device.getAddress(), status));
        }

        @Override
        public void onMtuChanged(BluetoothDevice device, int mtu) {
            Log.i("tang",String.format("onMtuChanged:%s,%s,%s", device.getName(), device.getAddress(), mtu));
        }

    };

我做完了這些事之後,我發現,還有坑。我每次修改藍牙本機名字之後,前幾次沒啥事兒,小夥伴突然給我說,不行了。一下回到解放前的名字了。我思考啊,思考啊。。。我覺得應該是發送廣播裏面設置名字的鍋,它發廣播只有一次,但是名字早給我發出去了,那我之後設置的不是都是打水漂?但是我仍然沒把這一點想通,但是時間不等人啊,壓力驅使我死馬當活馬醫啊。

然後我就在每次修改名字的時候,發送一次廣播,斷開前一次的廣播,重新發一次廣播。很雞肋,但是卻有效,要是有小夥伴知道怎麼處理,麻煩給我說一下。哎~但願天堂沒有bug。

不多就是這樣,我遇到的就是這些坑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章