藍牙通信第1篇:搜索藍牙設備


一:注意事項

      1:android6.0使用藍牙時,需要開啟gps定位權限,不然無法搜索其它藍牙設備。

二:權限

      1:權限配置

<!--允許程序連接到已配對的藍牙設備-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 允許程序發現和配對藍牙設備 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!--android 6.0 涉及到的權限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- 在SDCard中創建與刪除文件的權限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard寫入數據的權限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

 

    2:動態權限代碼

    由於需要用到存儲卡,定位等,android6.0以上需要代碼動態設置。

   a)獲取定位設置

if (Build.VERSION.SDK_INT >= 23) {
     boolean isLocat = isLocationOpen(getApplicationContext());
     Toast.makeText(mContext, "isLo:" + isLocat, Toast.LENGTH_LONG).show();
            //開啟位置服務,支持獲取ble藍牙掃描結果
     if (!isLocat) {
        Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        startActivityForResult(enableLocate, 1);
     }
}

 /**
     * 判斷位置信息是否開啟
     *
     * @param context
     * @return
     */
    private static boolean isLocationOpen(final Context context) {
        LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        //gps定位
        boolean isGpsProvider = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        //網絡定位
        boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        return isGpsProvider || isNetWorkProvider;
    }

 

b)存儲卡權限設置

if (Build.VERSION.SDK_INT >= 23) {
            int write = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            int read = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
            //動態請求讀寫sd卡權限
            if (write != PackageManager.PERMISSION_GRANTED || read != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SD_CARD);
            }
}

然后通過onRequestPermissionsResult()方法獲取動態權限的結果:

 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode){
            case SD_CARD:
                if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                  //允許訪問

                }else{
                    Toast.makeText(mContext,"您拒絕了程序訪問存儲卡",Toast.LENGTH_LONG).show();
                }
                break;
            case COARES_LOCATION:
                break;
        }
    }

 三:藍牙搜索

android.bluetooth.BluetoothAdapter 是藍牙開發用得比較多,並且比較重要的一個類,可以設備藍牙名稱,打開,關閉,搜索等常規操作。

  1 藍牙打開,以及搜索

     藍牙打開和關閉信息使用BluetoothAdapter.ACTION_STATE_CHANGED去接收廣播

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.setName("blueTestPhone"); 
//判斷藍牙是否打開
boolean originalBluetooth = (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
if (originalBluetooth) {
mBluetoothAdapter.startDiscovery();
} else if (originalBluetooth == false) {
mBluetoothAdapter.enable();
}

藍牙打開后,我們可以獲取設備的藍牙信息

 StringBuilder sb = new StringBuilder();
 //獲取本機藍牙名稱
 String name = mBluetoothAdapter.getName();
//獲取本機藍牙地址
 String address = mBluetoothAdapter.getAddress();

搜索完成后,通過BluetoothDevice.ACTION_FOUND廣播去接收結果,廣播代碼如下(注意:可能出現設備搜索不到的情況,設備需要開啟允許周圍設備搜索,或者通過程序來控制允許搜索的時間范圍)

  /*確保藍牙被發現,在榮耀8手機上,設置了還是默認的2分鍾,所以以下幾句代碼程序中沒有,*/
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//設置可見狀態的持續時間為300秒,但是最多是300秒
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE_BLUETOOTH);

private
void initSearchBroadcast() { IntentFilter intentFilter = new IntentFilter(); //發現設備 intentFilter.addAction(BluetoothDevice.ACTION_FOUND); //設備配對狀態改變 intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //藍牙設備狀態改變 intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //開始掃描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //結束掃描 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //其它設備請求配對 intentFilter.addAction(ACTION_PAIRING_REQUEST); //intentFilter.addAction(BluetoothAdapter.CONNECTION_STATE_CHANGED); registerReceiver(bluetoothReceiver, intentFilter); } private BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Logger.e(TAG + "mBluetoothReceiver action =" + action); try { if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {//開始掃描 setProgressBarIndeterminateVisibility(true); log1.setText("正在掃描設備,請稍候..."); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//結束掃描 Logger.e(TAG + "設備搜索完畢"); setProgressBarIndeterminateVisibility(false); log1.setText("掃描完成"); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); scanStatus = false; } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {//發現設備 findDevice(intent); } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//藍牙配對狀態的廣播 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Logger.e(TAG + device.getName() + "藍牙配對廣播:" + device.getBondState()); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDING: Logger.e(TAG + device.getName() + "藍牙配對廣播 正在配對......"); break; case BluetoothDevice.BOND_BONDED: Logger.e(TAG + device.getName() + "藍牙配對廣播 完成配對,本機自動配對"); bondDevices.add(device); unbondDevices.remove(device); bondAdapter.notifyDataSetChanged(); unbondAdapter.notifyDataSetChanged(); break; case BluetoothDevice.BOND_NONE: Logger.e(TAG + device.getName() + "藍牙配對廣播 取消配對"); unbondDevices.add(device); bondDevices.remove(device); unbondAdapter.notifyDataSetChanged(); bondAdapter.notifyDataSetChanged(); default: break; } } else if (action.equals(ACTION_PAIRING_REQUEST)) {//其它設備藍牙配對請求 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當前的配對的狀態 try { String path = Environment.getExternalStorageDirectory() + "/blueTest/"; String deviceName = btDevice.getName(); Logger.e(TAG + "藍牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); //1.確認配對,高版本無效,藍牙配對不是zuk的問題,而是安卓6.0的bug,凡是遇到藍牙適配問題的,請同時打開藍牙和定位,再去配對,基本90%都沒有問題了。 Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true); //2.終止有序廣播,如果沒有將廣播終止,則會出現一個一閃而過的配對框。 abortBroadcast(); //3.調用setPin方法進行配對... boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show(); } } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {//藍牙開關狀態 // BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int statue = mBluetoothAdapter.getState(); switch (statue) { case BluetoothAdapter.STATE_OFF: Logger.e("藍牙狀態:,藍牙關閉"); ClsUtils.closeDiscoverableTimeout(mBluetoothAdapter); break; case BluetoothAdapter.STATE_ON: Logger.e("藍牙狀態:,藍牙打開"); ClsUtils.setDiscoverableTimeout(1000 * 60, mBluetoothAdapter); scanBluetooth(); break; case BluetoothAdapter.STATE_TURNING_OFF: Logger.e("藍牙狀態:,藍牙正在關閉"); mBluetoothAdapter.cancelDiscovery(); break; case BluetoothAdapter.STATE_TURNING_ON: Logger.e("藍牙狀態:,藍牙正在打開"); break; } } } catch (Exception e) { e.printStackTrace(); } } };

//發現設備的代碼如下
 private void findDevice(Intent intent) throws Exception{
//獲取到設備對象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String str = device.getName() + "|" + device.getAddress();
Logger.e("掃描到設備:" + str);
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//判斷當前設備地址下的device是否已經配對
if (!bondDevices.contains(device)) {
bondDevices.add(device);
}
} else {
if (!unbondDevices.contains(device)) {
unbondDevices.add(device);
}
if (device.getName().equals(TEST_DEVICE_NAME)) {
boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
Logger.i(TAG + " bondStatus:" + bondStatus);
}
}
Log.e("error", "搜索完畢,准備刷新!");
bondAdapter.notifyDataSetChanged();
unbondAdapter.notifyDataSetChanged();
}


   

四:藍牙配對

   正常情況下,藍牙匹配需要彈出一個匹配確認框,如下圖,但我想實現的是,匹配其中一方,不能手動點擊配對,因為發起藍牙連接的設備是android設備,是不能觸摸的,所以就要通過程序來解決這個問題,特別聲明:(測試的android設備,版本為5.x,並且已經root,沒有root的設備,或者不是android5.x不清楚能否實現自動匹配,因為我只有這個測試設備)。

1 當我們搜索到目標手機的藍牙后,android設備主動發起連接請求,代碼如下

 if (device.getName().equals(TEST_DEVICE_NAME)) {
                boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
                Logger.i(TAG + " bondStatus:" + bondStatus);
 }
//發起藍牙匹配請求 
public boolean createBond(Class btClass, BluetoothDevice btDevice)
            throws Exception {
        Method createBondMethod = btClass.getMethod("createBond");
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
        return returnValue.booleanValue();
}

2 當被匹配方點擊配對后,系統會通過BluetoothDevice.ACTION_BOND_STATE_CHANGED廣播告訴android設備,此時android設備就可以自動確認,通過這個流程來完成整個藍牙的配對,具體代碼如下

 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //當前的配對的狀態
                    try {
                        String path = Environment.getExternalStorageDirectory() + "/blueTest/";
                        String deviceName = btDevice.getName();
                        Logger.e(TAG + "藍牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); 
                        if(deviceName.equals(TEST_DEVICE_NAME)){//TEST_DEVICE_NAME  為被匹配藍牙設備的名稱,自己手動定義
                            Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
                            abortBroadcast();
                            boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD);
                        }
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show();
                    }

//確認配對
public Object setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm) throws Exception {
        Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class);
       Object object =  setPairingConfirmation.invoke(device, isConfirm);
         return object;
    }

//配對需要調用的方法
public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,
                                 String str) throws Exception {
        try {
            Method removeBondMethod = btClass.getDeclaredMethod("setPin",
                    new Class[]
                            {byte[].class});
            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
                    new Object[]
                            {str.getBytes()});
            Log.e("returnValue", "" + returnValue);
        } catch (SecurityException e) {
            // throw new RuntimeException(e.getMessage());
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // throw new RuntimeException(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return true;

    }                        

 到目前為止,藍牙權限,以及動態權限,藍牙的打開,關閉,搜索,以及自動配對(特別聲明:自動配對的android設備,版本為5.x,並且已經root,沒有root的設備,或者不是android5.x不清楚能否實現自動匹配,因為我只有這個測試設備)。)代碼至此結束

一章:藍牙通信第2篇:建立通信和發送文字消息,文件消息
 
demo代碼下載:github





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM