Android 經(jīng)典藍牙開發(fā)(二)

本文章經(jīng)典藍牙開發(fā)目錄:

1师郑、權(quán)限申請
2、開啟藍牙
3漫蛔、掃描藍牙
4嗜愈、配對藍牙
5、連接藍牙
6莽龟、通信(實現(xiàn)雙向通信)(我用的兩個手機通過藍牙進行通信的~)
7蠕嫁、關(guān)閉各種通信
Android 藍牙開發(fā) 經(jīng)典藍牙和低功耗藍牙(一)
Android 低功耗藍牙開發(fā)(三)

先看一串效果圖
小米LTE作為客戶端,小米5作為服務(wù)端
雙方配對

小米lte配對.png

小米5配對.png

客戶端向服務(wù)端發(fā)送數(shù)據(jù)
小米lte發(fā)送數(shù)據(jù).png

小米5接收數(shù)據(jù).png

服務(wù)端向客戶端回傳數(shù)據(jù)
小米5發(fā)送數(shù)據(jù).png

小米lte接收數(shù)據(jù).png

==================開始正文==============

哈哈哈哈

第一步毯盈,權(quán)限申請

1剃毒、Android6.0搜索周圍的藍牙設(shè)備,需要位置權(quán)限 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 其中的一個奶镶,并且將手機的位置服務(wù)(定位 GPS)打開迟赃。
清單文件:

  <!-- 使用藍牙的權(quán)限 -->
   <uses-permission android:name="android.permission.BLUETOOTH" />
   <!-- 掃描藍牙設(shè)備或者操作藍牙設(shè)置 -->
   <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
   <!--模糊定位權(quán)限,僅作用于6.0+-->
   <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
   <!--精準定位權(quán)限厂镇,僅作用于6.0+-->
   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

權(quán)限申請工具類:LocalUtils.java
1、這個類用來檢查GPS和定位權(quán)限左刽,回調(diào)在Activity里面捺信。

public class LocalUtils {

    //申請權(quán)限
    public static final int permission_LocationCode = 101;
    //打開gps定位
    public static final int open_GPSCode = 102;
    static String[] permissionsIndex;
    /**
     * 此方法用來檢查gps和定位權(quán)限,先檢查gps是否打開,在檢查是否有定位權(quán)限
     * @param activity 上下文對象
     * @param permissions 權(quán)限的名稱
     * @return
     */
    public static boolean checkLocalPermissiion(Activity activity, String[] permissions) {
        permissionsIndex = permissions;
        if (checkGPSIsOpen(activity)) {
            return checkPermissions(activity);
        } else {
            Toast.makeText(activity, "需要打開GPS", Toast.LENGTH_SHORT).show();
            goToOpenGPS(activity);
        }
        return false;
    }

    /**
     * 檢查GPS是否打開
     *
     */
    public static boolean checkGPSIsOpen(Activity activity) {
        LocationManager locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
        if (locationManager == null)
            return false;
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }


    /**
     * 檢查權(quán)限并申請權(quán)限
     */
    public static boolean checkPermissions(final Activity activity) {
        List<String> permissionDeniedList = new ArrayList<>();
        for (String permission : permissionsIndex) {
            int permissionCheck = ContextCompat.checkSelfPermission(activity, permission);
            if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                permissionDeniedList.add(permission);
            }
        }
        if (!permissionDeniedList.isEmpty()) {
            String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
            ActivityCompat.requestPermissions(activity, deniedPermissions, permission_LocationCode);
        }
        return false;
    }


    /**
     * 去手機設(shè)置打開GPS
     *
     * @param activity
     */
    public static void goToOpenGPS(Activity activity) {
        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        activity.startActivityForResult(intent, open_GPSCode);

    }
}

在Activity里面回調(diào):


    //GPS
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case LocalUtils.open_GPSCode://檢查是否手機打開定位
                if (LocalUtils.checkGPSIsOpen(this)) {
                    LocalUtils.checkLocalPermissiion(this, permissions);//檢查定位權(quán)限
                } else {
                    LocalUtils.goToOpenGPS(this);//打開GPS
                }
                break;
        }
    }

    /**
     * 權(quán)限回調(diào)
     */
    @Override
    public final void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case LocalUtils.permission_LocationCode://是否打開允許定位權(quán)限
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            initClassica();//初始化經(jīng)典藍牙
                        }
                    }
                }
                break;
        }
    }
哈哈哈.gif

ps:第一步是比較簡單的哦~~看看就明白了

第二步迄靠,開啟藍牙

1秒咨、獲取BluetoothAdapter
 獲取BluetoothAdapter兩種方式:
方式一:

  BluetoothManager systemService = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
  BluetoothAdapter bluetoothAdapter = systemService.getAdapter();

方式二:

 BluetoothAdapter bluetoothAdapter =BluetoothAdapter.getDefaultAdapter();

2、判斷設(shè)備是否支持藍牙
 如果 BluetoothAdapter.getDefaultAdapter()==null掌挚,不支持雨席,否則支持。
3吠式、開啟藍牙

 /**
    * 自動打開藍牙(同步)
    * 這個方法打開藍牙會彈出提示
    * 需要在onActivityResult 方法中判斷resultCode == RESULT_OK  true為成功
    */
   public void openBlueSync(Activity activity, int requestCode) {
       Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
       activity.startActivityForResult(intent, requestCode);
   }
哈哈哈.jpg

ps:第二步陡厘,嗯嗯嗯....我就開始百度了~~~

第三步,掃描藍牙

1特占、掃描藍牙

/**
        * 掃描的方法 返回true 掃描成功
        * 通過接收廣播獲取掃描到的設(shè)備
        * @return
        */
       public boolean scanBlueTooth() {
           //當前是否在掃描糙置,如果是就取消當前的掃描,重新掃描
           if (bluetoothAdapter.isDiscovering()) {
               bluetoothAdapter.cancelDiscovery();
           }
           //此方法是個異步操作是目,一般搜索12秒
           return bluetoothAdapter.startDiscovery();
       }

2谤饭、取消掃描

      /**
         * 取消掃描藍牙
         * @return true 為取消成功
         */
        public boolean cancelScanBule() {
            return bluetoothAdapter.cancelDiscovery();
        }

3、廣播接收掃描的結(jié)果
  系統(tǒng)自動回發(fā)送廣播懊纳,告訴我們揉抵,掃描出來的藍牙設(shè)備。
先定義接口嗤疯,來回調(diào)廣播中的結(jié)果(在activity接收結(jié)果的哦~~~)
ClientCallBack.java

public interface ClientCallBack {
    //開始掃描
    void onScanStarted();

    //掃描結(jié)束
    void onScanFinished();

    //掃描中
    void onScanning(BluetoothDevice device);

    //配對請求
    void onBondRequest();

    //配對成功
    void onBondSuccess(BluetoothDevice device);

    //正在配對
    void onBonding(BluetoothDevice device);

    //配對失敗
    void onBondFail(BluetoothDevice device);

    //連接成功
    void onConnectSuccess();

    //連接失敗
    void onConnectFail(String errorMsg);

    //連接關(guān)閉
    void onConnectClose();
}

BlueReceiver.java

public class BlueReceiver extends BroadcastReceiver {
  private String pin = "0000";  //此處為你要連接的藍牙設(shè)備的初始密鑰功舀,一般為1234或0000
  private static final String TAG = "mcy_Receiver";
  private ClientCallBack callBack;

  public BlueReceiver(ClientCallBack callBack) {
      this.callBack = callBack;
  }

  public void setCallBack(ClientCallBack callBack){
      this.callBack=callBack;
  }

  //廣播接收器,當遠程藍牙設(shè)備被發(fā)現(xiàn)時身弊,回調(diào)函數(shù)onReceiver()會被執(zhí)行
  @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(callBack==null){
          return;
      }
      switch (action) {
          case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
              callBack.onScanStarted();
              break;
          case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
              callBack.onScanFinished();
              break;
          case BluetoothDevice.ACTION_FOUND:
              callBack.onScanning(device);
              break;
          case BluetoothDevice.ACTION_PAIRING_REQUEST:
                  callBack.onBondRequest();
              break;
          case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
              switch (device.getBondState()) {
                  case BluetoothDevice.BOND_NONE:
                      callBack.onBondFail(device);
                      break;
                  case BluetoothDevice.BOND_BONDING:
                      callBack.onBonding(device);
                      break;
                  case BluetoothDevice.BOND_BONDED:
                      callBack.onBondSuccess(device);
                      break;
              }
              break;
      }
  }

}

為了方便統(tǒng)一管理辟汰,把藍牙的操作寫在服務(wù)里面,ClassicsBlueToothService.java阱佛,因此帖汞,把廣播注冊在服務(wù)里面,生命周期跟著服務(wù)走~~

public class ClassicsBlueToothService extends Service {
    private IntentFilter filter;
    private BlueReceiver pinBlueReceiver;
    @Override
    public void onCreate() {
        super.onCreate();
        //獲取藍牙適配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        //注冊廣播
        //藍牙廣播,系統(tǒng)自動發(fā)送廣播,只要設(shè)置制定的 action 即可
        filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//開始掃描
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//結(jié)束掃描
        filter.addAction(BluetoothDevice.ACTION_FOUND);//發(fā)現(xiàn)設(shè)備
        filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);//發(fā)起配對請求
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//配對狀態(tài)
        pinBlueReceiver = new BlueReceiver(blueCallBack);

        registerReceiver(pinBlueReceiver, filter);//注冊廣播
    }

   @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(pinBlueReceiver);
    }
       ........還有很多代碼凑术,就不全部拿出來了翩蘸,去github上看吧
}
委屈.png

ps:第三步的廣播和服務(wù)的結(jié)合使用搬出來了,好久沒用了淮逊,又從頭過一遍催首,才寫出來的~~~

第四步,配對藍牙

配對:

  /**
         * 配對(配對成功與失敗通過廣播返回)
         * P古簟@扇巍!彈出配對框
         *
         * @param device
         */
        public void pinBlueTooth(BluetoothDevice device) {
            if (device == null) {
                Log.e("mcy", "設(shè)備不可以為空");
                return;
            }
            //配對之前把掃描關(guān)閉
            if (bluetoothAdapter.isDiscovering()) {
                bluetoothAdapter.cancelDiscovery();
            }
            //判斷設(shè)備是否配對备籽,沒有配對在配舶治,配對了就不需要配了
            if (device.getBondState() == BluetoothDevice.BOND_NONE) {//BOND_NONE 沒有配對狀態(tài)
                Log.d("mcy", "attemp to bond:" + device.getName());
                try {
                    boolean returnValue = device.createBond();
                    Log.e("是否配對成功:", "" + returnValue);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Log.d("mcy", "配對失敗");

                }
            }
        }

取消配對:


        /**
         * 取消配對(取消配對成功與失敗通過廣播返回 也就是配對失旝蕹ぁ)
         *
         * @param device
         */
        public void cancelPinBuleTooth(BluetoothDevice device) {
            if (device == null) {
                Log.d("mcy", "設(shè)備不可以為空");
                return;
            }
            //判斷設(shè)備是否配對婆排,沒有配對就不用取消了
            if (device.getBondState() != BluetoothDevice.BOND_NONE) {
                Log.d("mcy", "配對--" + 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.d("mcy", "取消配對失敗");
                }
            }
        }
開心.jpg

ps:第四步僚祷,嗯嗯嗯酷师,還是比較好看的,只是操作藍牙

第五步惜浅,連接藍牙

這里說一下 uuid 的問題瘫辩,要想進行通信,客戶端和服務(wù)端必須一致坛悉,否則連接失敗伐厌,因為我用的手機進行的通訊,所有吹散,我寫了雙向通信弧械,既可以寫也可以讀。
連接藍牙
注意:在連接藍牙的同時空民,開啟一個子線程刃唐,用來寫入數(shù)據(jù)!=缧;ⅰ!

  /**
         * @param uuid   用戶指定的 uuid 浊猾,可隨意寫抖甘,只要格式對就行
         * @param device 連接的設(shè)備
         */
        public void connectionBlueTooth(String uuid, BluetoothDevice device) {
            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
                if (bluetoothSocket != null && !bluetoothSocket.isConnected()) {
                    sendDataThread = new ClientThread(bluetoothSocket, blueCallBack);
                    sendDataThread.start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

ClientThread.java

public class ClientThread extends Thread {

    private static final String TAG = "mcy";
    private ClientCallBack callBack;
    private BluetoothSocket bluetoothSocket = null;
    private Handler handler;
    private OutputStream outputStream;

    public ClientThread(BluetoothSocket bluetoothSocket, ClientCallBack callBack) {
        this.callBack = callBack;
        this.bluetoothSocket = bluetoothSocket;
    }

    /**
     * 寫數(shù)據(jù)
     *
     * @param data
     */

    public void write(byte[] data) {
        Message message = new Message();
        message.obj = data;
        handler.sendMessage(message);
    }

    /**
     * 關(guān)閉各種連接連接
     */
    public void closeSocket() {
        try {
            outputStream.close();
            bluetoothSocket.close();
            callBack.onConnectClose();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            bluetoothSocket.connect();
            outputStream = bluetoothSocket.getOutputStream();//讀取需要發(fā)送的的數(shù)據(jù)
            Looper.prepare();
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    try {
                        byte[] data = (byte[]) msg.obj;
                        outputStream.write(data);
                        outputStream.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            callBack.onConnectSuccess();
            Looper.loop();
        } catch (IOException e) {
            callBack.onConnectFail(e.getMessage());
        }
    }
}
變少

ps:第五步,有難度葫慎,光這個uuid衔彻,我就研究了好久,到底是個什么玩意偷办,哼艰额、、椒涯、頭大日漸變少了~~

第六步柄沮,通信(實現(xiàn)雙向通信)

1、客戶端發(fā)送數(shù)據(jù)

 /**
         * 寫數(shù)據(jù)
         */
        public void sendData(byte[] data) {
            if (sendDataThread != null) {
                sendDataThread.write(data);
            }
        }

2废岂、客戶端接收數(shù)據(jù)

/**
         * 讀數(shù)據(jù)
         */
        public void readListern(String name, String uuid, ServiceCallback callBack) {
            acceptThread = new AcceptThread(name, uuid, bluetoothAdapter, callBack);
            acceptThread.start();
        }

定義一個接口祖搓,來監(jiān)聽,收到數(shù)據(jù)的狀態(tài)

public interface ServiceCallback {
    //連接成功
    void onConnectSuccess();

    //連接失敗
    void onConnectFail(String errorMsg);

    //連接關(guān)閉
    void onConnectClose();

    //接收到的數(shù)據(jù)
    void onResultMessage(byte[] data);
}

開啟一個子線程,用來監(jiān)聽服務(wù)端發(fā)過來的數(shù)據(jù)

public class AcceptThread extends Thread {
    private static final String TAG = "mcy";
    private ServiceCallback callBack;
    private BluetoothServerSocket serverSocket = null;
    private InputStream inputStream;
    private BufferedInputStream bufferedInputStream;
    private byte[] data;

    public AcceptThread(String name, String uuid, BluetoothAdapter bluetoothAdapter, ServiceCallback callBack) {
        this.callBack = callBack;
        try {
            serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(name, UUID.fromString(uuid));

        } catch (IOException e) {
            e.printStackTrace();
            callBack.onConnectFail(e.getMessage());
        }
    }

    /**
     * 關(guān)閉各種流和通信的socket
     */
    public void closeSocket() {
        try {
            inputStream.close();
            bufferedInputStream.close();
            serverSocket.close();
            callBack.onConnectClose();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            final BluetoothSocket bluetoothSocket = serverSocket.accept();
            callBack.onConnectSuccess();
            inputStream = bluetoothSocket.getInputStream();//獲取服務(wù)端發(fā)來的消息
            bufferedInputStream = new BufferedInputStream(inputStream);
            while (true) {
                int available =0;
                wh:
lable==0){
                    available=inputStream.available();
                }
                data = new byte[available];
                bufferedInputStream.read(data);
                callBack.onResultMessage(data);//回傳數(shù)據(jù)
            }
        } catch (IOException e) {
            callBack.onConnectFail(e.getMessage());
            e.printStackTrace();
        }
    }
}
哈哈哈.jpeg

ps:第六步湖苞,昨晚這一步拯欧,基本上做完了~~頭發(fā)沒了、袒啼、哈扮、

第七步纬纪,關(guān)閉各種通信

在第五步和第六步的子線程里均有關(guān)閉通信的方法

      /**
         * 寫數(shù)據(jù)斷開連接
         *
         * @return
         */
        public void cancleConnecion() {
            sendDataThread.closeSocket();
        }
        /**
         * 寫數(shù)據(jù)斷開連接
         *
         * @return
         */
        public void cancleServiceConnecion() {
            acceptThread.closeSocket();
        }

好了蚓再,放大招滑肉,請看activity和service

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "mcy";
    private TextView textView;
    private TextView textView2;
    private TextView textView3;
    private EditText editTxt;
    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
    private final int openBTCode = 100;
    // ----------------經(jīng)典藍牙------------------
    private ClassicsBlueToothService.ClassicaBlueToothBind classicaBTBind;
    private ServiceConnection classicaConnection;
    private List<BluetoothDevice> devicesList = new ArrayList<>();
    private ClientCallBack blueCallBack;
    private String text = "";


    private String uuid = "6db14d27-04f1-4df8-98ca-356dfc16ee43";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //檢查權(quán)限   >6.0以上版本需要動態(tài)的申請定位權(quán)限,< 6.0 清單文件聲明了即可
        if (LocalUtils.checkLocalPermissiion(this, permissions)) {
            initClassica();
        }

    }


    private void initView() {
        textView = findViewById(R.id.textView);
        textView2 = findViewById(R.id.textView2);
        textView3 = findViewById(R.id.textView3);
        editTxt = findViewById(R.id.editTxt);
        findViewById(R.id.button5).setOnClickListener(this);
        findViewById(R.id.button11).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button5://經(jīng)典--寫數(shù)據(jù)
                text = editTxt.getText().toString();
                classicaBTBind.sendData(text.getBytes());
                break;
            case R.id.button11://低功耗--發(fā)送數(shù)據(jù)
                break;

        }
    }

   
    private void initClassica() {
        blueCallBack = new ClientCallBack() {
            @Override
            public void onScanStarted() {
                Log.e("mcy", "開始掃描");

            }

            @Override
            public void onScanFinished() {
                Log.e("mcy", "結(jié)束掃描");
            }

            @Override
            public void onScanning(BluetoothDevice device) {
                Log.e("mcy", "掃描到設(shè)備-->" + device.getName());
                if (!devicesList.contains(device)) {
                    //將設(shè)備加入列表數(shù)據(jù)中
                    devicesList.add(device);
                }
                textView.setText(textView.getText() + "\n" + device.getName());
                Log.e(TAG, "" + device.getName());
                //已配對的藍牙
                if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//BOND_BONDED 已經(jīng)配對狀態(tài)
                    textView2.setText(textView2.getText() + "\n" + device.getName());
                } else {
                    classicaBTBind.pinBlueTooth(devicesList.get(0));
                }

            }

            @Override
            public void onBondRequest() {
                Log.e("mcy", "開始配對");

            }

            @Override
            public void onBondFail(BluetoothDevice device) {
                Log.e("mcy", "取消配對");
            }

            @Override
            public void onBonding(BluetoothDevice device) {
                Log.e("mcy", "配對中");
            }

            @Override
            public void onBondSuccess(BluetoothDevice device) {
                Log.e("mcy", "配對成功");
                classicaBTBind.connectionBlueTooth(uuid, device);
                //registReadListener(); //測試使用,雙端通訊

            }

            @Override
            public void onConnectSuccess() {
                Log.e("mcy", "連接成功");
            }

            @Override
            public void onConnectFail(String errorMsg) {
                Log.e("mcy", "連接失敗" + errorMsg);
            }

            @Override
            public void onConnectClose() {
                Log.e("mcy", "連接關(guān)閉");

            }
        };
        classicaConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                classicaBTBind = ((ClassicsBlueToothService.ClassicaBlueToothBind) service);
                if (blueCallBack != null) {
                    classicaBTBind.setBlueCallback(blueCallBack);//設(shè)置廣播監(jiān)聽
                }
                if (classicaBTBind.getAdapter() != null) {
                    //判斷藍牙是否開啟
                    if (!classicaBTBind.getAdapter().isEnabled()) {
                        //打開藍牙
                        openBlueSync(MainActivity.this, openBTCode);
                    } else {
                        //========================開始執(zhí)行工作=============================
                        classicaBTBind.scanBlueTooth();//掃描藍牙
                        registReadListener();//注冊讀數(shù)據(jù)事件
                    }
                } else {
                    Toast.makeText(MainActivity.this, "此設(shè)備不支持藍牙", Toast.LENGTH_SHORT).show();
                }


            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                classicaBTBind = null;

            }
        };
        bindService(new Intent(this, ClassicsBlueToothService.class), classicaConnection, BIND_AUTO_CREATE);
    }

    //經(jīng)典藍牙注冊讀數(shù)據(jù)事件
    private void registReadListener() {
        classicaBTBind.readListern("Demo", uuid, new ServiceCallback() {

            @Override
            public void onConnectSuccess() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("mcy", "讀數(shù)據(jù)連接成功~");
                    }
                });
            }

            @Override
            public void onConnectFail(final String errorMsg) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("mcy", "讀數(shù)據(jù)連接失敗" + errorMsg);
                    }
                });

            }

            @Override
            public void onConnectClose() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("mcy", "讀數(shù)據(jù)連接關(guān)閉");

                    }
                });
            }

            @Override
            public void onResultMessage(final byte[] data) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView3.setText(new String(data));
                    }
                });
            }
        });
    }

    /**
     * 自動打開藍牙(同步)
     * 這個方法打開藍牙會彈出提示
     * 需要在onActivityResult 方法中判斷resultCode == RESULT_OK  true為成功
     */
    public void openBlueSync(Activity activity, int requestCode) {
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        activity.startActivityForResult(intent, requestCode);
    }


    //GPS
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case LocalUtils.open_GPSCode://檢查是否手機打開定位
                if (LocalUtils.checkGPSIsOpen(this)) {
                    LocalUtils.checkLocalPermissiion(this, permissions);
                } else {
                    LocalUtils.goToOpenGPS(this);
                }
                break;
        }
    }

    /**
     * 權(quán)限回調(diào)
     */
    @Override
    public final void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case LocalUtils.permission_LocationCode://是否打開允許定位權(quán)限
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            initClassica();//初始化經(jīng)典藍牙
                        }
                    }
                }
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(classicaConnection);
    }
}

ClassicsBlueToothService.java:

public class ClassicsBlueToothService extends Service {
    private IntentFilter filter;
    private BlueReceiver pinBlueReceiver;
    private BluetoothAdapter bluetoothAdapter;
    private ClientThread sendDataThread;
    private BluetoothSocket bluetoothSocket;
    private ClientCallBack blueCallBack;
    private AcceptThread acceptThread;

    @Override
    public void onCreate() {
        super.onCreate();
        //獲取藍牙適配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        //注冊廣播
        //藍牙廣播,系統(tǒng)自動發(fā)送廣播,只要設(shè)置制定的 action 即可
        filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//開始掃描
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//結(jié)束掃描
        filter.addAction(BluetoothDevice.ACTION_FOUND);//發(fā)現(xiàn)設(shè)備
        filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);//發(fā)起配對請求
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//配對狀態(tài)
        pinBlueReceiver = new BlueReceiver(blueCallBack);

        registerReceiver(pinBlueReceiver, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(pinBlueReceiver);
    }

    public class ClassicaBlueToothBind extends Binder {

        public BluetoothAdapter getAdapter() {
            return bluetoothAdapter;
        }

        public void setBlueCallback(ClientCallBack callback) {
            ClassicsBlueToothService.this.blueCallBack = callback;
            pinBlueReceiver.setCallBack(callback);
        }

        /**
         * 掃描的方法 返回true 掃描成功
         * 通過接收廣播獲取掃描到的設(shè)備
         *
         * @return
         */
        public boolean scanBlueTooth() {
            //當前是否在掃描,如果是就取消當前的掃描摘仅,重新掃描
            if (bluetoothAdapter.isDiscovering()) {
                bluetoothAdapter.cancelDiscovery();
            }
            //此方法是個異步操作靶庙,一般搜索12秒
            return bluetoothAdapter.startDiscovery();
        }

        /**
         * 取消掃描藍牙
         *
         * @return true 為取消成功
         */
        public boolean cancelScanBule() {
            return bluetoothAdapter.cancelDiscovery();
        }

        /**
         * 配對(配對成功與失敗通過廣播返回)
         * !M奘簟六荒!彈出配對框
         *
         * @param device
         */
        public void pinBlueTooth(BluetoothDevice device) {
            if (device == null) {
                Log.e("mcy", "設(shè)備不可以為空");
                return;
            }
            //配對之前把掃描關(guān)閉
            if (bluetoothAdapter.isDiscovering()) {
                bluetoothAdapter.cancelDiscovery();
            }
            //判斷設(shè)備是否配對,沒有配對在配矾端,配對了就不需要配了
            if (device.getBondState() == BluetoothDevice.BOND_NONE) {//BOND_NONE 沒有配對狀態(tài)
                Log.d("mcy", "attemp to bond:" + device.getName());
                try {
                    boolean returnValue = device.createBond();
                    Log.e("是否配對成功:", "" + returnValue);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Log.d("mcy", "配對失敗");

                }
            }
        }

        /**
         * 取消配對(取消配對成功與失敗通過廣播返回 也就是配對失斕突鳌)
         *
         * @param device
         */
        public void cancelPinBuleTooth(BluetoothDevice device) {
            if (device == null) {
                Log.d("mcy", "設(shè)備不可以為空");
                return;
            }
            //判斷設(shè)備是否配對,沒有配對就不用取消了
            if (device.getBondState() != BluetoothDevice.BOND_NONE) {
                Log.d("mcy", "配對--" + 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.d("mcy", "取消配對失敗");
                }
            }
        }

        /**
         * @param uuid   用戶指定的 uuid 秩铆,可隨意寫砚亭,只要格式對就行,客戶端和服務(wù)端保持一致即可殴玛。
         * @param device 連接的設(shè)備
         */



        public void connectionBlueTooth(String uuid, BluetoothDevice device) {
            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
                if (bluetoothSocket != null && !bluetoothSocket.isConnected()) {
                    sendDataThread = new ClientThread(bluetoothSocket, blueCallBack);
                    sendDataThread.start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 寫數(shù)據(jù)
         */
        public void sendData(byte[] data) {
            if (sendDataThread != null) {
                sendDataThread.write(data);
            }
        }

        /**
         * 寫數(shù)據(jù)斷開連接
         *
         * @return
         */
        public void cancleConnecion() {
            sendDataThread.closeSocket();
        }


        /**
         * 讀數(shù)據(jù)
         */
        public void readListern(String name, String uuid, ServiceCallback callBack) {
            acceptThread = new AcceptThread(name, uuid, bluetoothAdapter, callBack);
            acceptThread.start();
        }

        /**
         * 寫數(shù)據(jù)斷開連接
         *
         * @return
         */
        public void cancleServiceConnecion() {
            acceptThread.closeSocket();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new ClassicaBlueToothBind();
    }
}

github捅膘,地址 : https://github.com/Mchunyan/BlueToothTest

-----------------------The End-----------------

下一篇寫,低功耗藍牙開發(fā)~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滚粟,一起剝皮案震驚了整個濱河市寻仗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凡壤,老刑警劉巖署尤,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亚侠,居然都是意外死亡曹体,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門盖奈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來混坞,“玉大人,你說我怎么就攤上這事钢坦【吭校” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵爹凹,是天一觀的道長厨诸。 經(jīng)常有香客問我,道長禾酱,這世上最難降的妖魔是什么微酬? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任绘趋,我火速辦了婚禮,結(jié)果婚禮上颗管,老公的妹妹穿的比我還像新娘陷遮。我一直安慰自己,他們只是感情好垦江,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布帽馋。 她就那樣靜靜地躺著,像睡著了一般比吭。 火紅的嫁衣襯著肌膚如雪绽族。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天衩藤,我揣著相機與錄音吧慢,去河邊找鬼。 笑死赏表,一個胖子當著我的面吹牛检诗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播底哗,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岁诉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跋选?” 一聲冷哼從身側(cè)響起涕癣,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎前标,沒想到半個月后坠韩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡炼列,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年只搁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俭尖。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡氢惋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稽犁,到底是詐尸還是另有隱情焰望,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布已亥,位于F島的核電站熊赖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏虑椎。R本人自食惡果不足惜震鹉,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一俱笛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧传趾,春花似錦迎膜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽零抬。三九已至镊讼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間平夜,已是汗流浹背蝶棋。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忽妒,地道東北人玩裙。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像段直,于是被迫代替她去往敵國和親吃溅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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

  • Guide to BluetoothSecurity原文 本出版物可免費從以下網(wǎng)址獲得:https://doi.o...
    公子小水閱讀 7,996評論 0 6
  • Android平臺支持藍牙網(wǎng)絡(luò)協(xié)議棧鸯檬,實現(xiàn)藍牙設(shè)備之間數(shù)據(jù)的無線傳輸决侈。本文檔描述了怎樣利用android平臺提供的...
    Camming閱讀 3,319評論 0 3
  • 最近項目使用藍牙,之前并沒有接觸喧务,還是發(fā)現(xiàn)了很多坑赖歌,查閱了很多資料,說的迷迷糊糊功茴,今天特查看官方文檔庐冯。 說下遇到的...
    King9527閱讀 1,794評論 0 1
  • 公司的項目最近需要用到藍牙開發(fā)的相關(guān)內(nèi)容,因此特地查閱了Google官方文檔的內(nèi)容并進行二次整理坎穿,希望能對需要學(xué)習(xí)...
    Chuckiefan閱讀 32,459評論 44 123
  • 藍牙 注:本文翻譯自https://developer.android.com/guide/topics/conn...
    RxCode閱讀 8,677評論 11 99