由於前段時間,需要藍牙操作一些東西。但是沒有寫過藍牙連接與藍牙操作等一系列的東西。
然后就去網上找找大家寫的。到現在。已經結束了這個藍牙的開發。
安卓端藍牙操作。主要需要如下類
BluetoothAdapter 通過名字,可以了解到這是一個藍牙適配器的類。可以獲取到藍牙的狀態。開關,。掃描等一系列的常規操作
BluetoothSocket 這個呢,是一個藍牙的socket。不做過多的介紹。和普通socket一樣
BluetoothDevice 這個類是獲取遠程設備的類。
有了上面3個類。藍牙的基本操作就可以實現了。
當然。,藍牙的操作需要廣播來支持、。下面是具體的代碼。
藍牙相關廣播
Action值 說明
ACTION_STATE_CHANGED 藍牙狀態值發生改變
ACTION_SCAN_MODE_CHANGED 藍牙掃描狀態(SCAN_MODE)發生改變
ACTION_DISCOVERY_STARTED 藍牙掃描過程開始
ACTION_DISCOVERY_FINISHED 藍牙掃描過程結束
ACTION_LOCAL_NAME_CHANGED 藍牙設備Name發生改變
ACTION_REQUEST_DISCOVERABLE 請求用戶選擇是否使該藍牙能被掃描
PS:如果藍牙沒有開啟,用戶點擊確定后,會首先開啟藍牙,繼而設置藍牙能被掃描。
ACTION_REQUEST_ENABLE 請求用戶選擇是否打開藍牙
ACTION_FOUND (該常量字段位於BluetoothDevice類中)
說明:藍牙掃描時,掃描到任一遠程藍牙設備時,會發送此廣播。
private BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {//開始掃描藍牙時候的廣播 mDeviceList.clear(); mAdapter.notifyDataSetChanged(); } if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //這個廣播是掃描結束,藍牙發送的廣播 if (progressDialog!=null){ progressDialog.cancel(); progressDialog=null; } showToast("搜素完成"); } if (BluetoothDevice.ACTION_FOUND.equals(action)) { //當掃描到藍牙設備時,會發送這個廣播 mDeviceList.add((BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); mAdapter.notifyDataSetChanged(); } if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) { //藍牙掃描狀態(SCAN_MODE)發生改變 if (intent.getIntExtra("android.bluetooth.adapter.extra.SCAN_MODE", 0) == 23) { } } if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { //綁定時候的廣播 BluetoothDevice remoteDevice = (BluetoothDevice) intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE"); if (remoteDevice == null) { showToast("no device"); return; } int status = intent.getIntExtra("android.bluetooth.device.extra.BOND_STATE", 0); if (status == 12) { showToast("配對完成 " + remoteDevice.getName()); blueToothUtils.searchDevices(); showToast("刷新列表中 "); } else if (status == 11) { showToast("正在配對 " + remoteDevice.getName()); } else if (status == 10) { showToast("配對失敗 " + remoteDevice.getName()); } } } };
//設置要監聽的廣播。
void initView(){
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
mContext.registerReceiver(mReceiver, filter);
}
上面的設置完成以后
編寫一個藍牙工具類進行使用。方便以后的開發。
在一開始。網上找了一些藍牙的操作工具類。但是多多少少都有寫瑕疵。
大多數都是2個手機通信。無法和藍牙單片機模塊通信。
具體的藍牙操作類如下,這個工具類都是我在網上的大多數工具類中攢在一起的。里面有些方法也沒用。我也就不刪了。在接收數據的時候。如果是單片機發過來的話,數據會接收不完全。這里,將原來類方法更改了一下。、大家在接收數據時,盡量選擇一個用不到的數字或者字母等,來當做結束位。進行判斷、工具類里面的注釋基本都有。
在網上找資料時,發現2個工具類,一個如下,看着比較簡單,大家容易懂,還有一個。都是英文,后面翻譯了一下,也放在文章最后吧。其實思路都一樣。喜歡那個,用那個
工具類如下:
public class BlueToothUtils { private static final String TAG = "BlueToothUtils"; private Context mContext; public static BlueToothUtils sInstance; private BluetoothAdapter mBA; private SharedPreferences sp; // UUID.randomUUID()隨機獲取UUID private final UUID MY_UUID = UUID .fromString("00001101-0000-1000-8000-00805F9B34FB"); // 連接對象的名稱 private final String NAME = "Chaoba"; // 這里本身即是服務端也是客戶端,需要如下類 private BluetoothSocket mSocket; private BluetoothDevice mOldDevice; private BluetoothDevice mCurDevice; // 輸出流_客戶端需要往服務端輸出 private OutputStream os; private InputStream inputStream; //線程類的實例 private AcceptThread ac; private Handler mhandler; private Handler handler2; private BlueToothUtils.Readtask readtask; public static synchronized BlueToothUtils getInstance() { if (sInstance == null) { sInstance = new BlueToothUtils(); } return sInstance; } public BlueToothUtils() { mBA = BluetoothAdapter.getDefaultAdapter(); ac = new AcceptThread(); } public void setContext(Context context) { this.mContext = context; } public void setHandler(Handler handler){ this.mhandler=handler; } public void setHandler2(Handler handler2){ this.handler2=handler2; } public BluetoothAdapter getBA() { return mBA; } public AcceptThread getAc() { return ac; } public BluetoothDevice getCurDevice() { return mCurDevice; } /** * 判斷是否打開藍牙 * * @return */ public boolean isEnabled() { if (mBA.isEnabled()) { return true; } return false; } /** * 搜索設備 */ public void searchDevices() { // 判斷是否在搜索,如果在搜索,就取消搜索 if (mBA.isDiscovering()) { mBA.cancelDiscovery(); } // 開始搜索 mBA.startDiscovery(); Log.e(TAG, "正在搜索..."); } /** * 獲取已經配對的設備 * * @return */ public List<BluetoothDevice> getBondedDevices() { List<BluetoothDevice> devices = new ArrayList<>(); Set<BluetoothDevice> pairedDevices = mBA.getBondedDevices(); // 判斷是否有配對過的設備 if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { devices.add(device); Log.e(TAG, "BondedDevice:" + device.getName()); } } return devices; } /** * 與設備配對 * * @param device */ public void createBond(BluetoothDevice device) { try { Method createBondMethod = BluetoothDevice.class.getMethod("createBond"); createBondMethod.invoke(device); } catch (Exception e) { e.printStackTrace(); } } /** * 與設備解除配對 * * @param device */ public void removeBond(BluetoothDevice device) { try { Method removeBondMethod = device.getClass().getMethod("removeBond"); removeBondMethod.invoke(device); } catch (Exception e) { e.printStackTrace(); } } /** * * @param device * @param str 設置PIN碼 * @return */ public boolean setPin(BluetoothDevice device, String str) { try { Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class}); Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{str.getBytes()}); Log.e("returnValue", "" + returnValue); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return true; } /** * 取消用戶輸入 */ public boolean cancelPairingUserInput(BluetoothDevice device) { Boolean returnValue = false; try { Method createBondMethod = device.getClass().getMethod("cancelPairingUserInput"); returnValue = (Boolean) createBondMethod.invoke(device); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // cancelBondProcess() return returnValue.booleanValue(); } /** * 取消配對 */ public boolean cancelBondProcess(BluetoothDevice device) { Boolean returnValue = null; try { Method createBondMethod = device.getClass().getMethod("cancelBondProcess"); returnValue = (Boolean) createBondMethod.invoke(device); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return returnValue.booleanValue(); } /** * @param strAddr * @param strPsw * @return */ public boolean pair(String strAddr, String strPsw) { boolean result = false; mBA.cancelDiscovery(); if (!mBA.isEnabled()) { mBA.enable(); } if (!BluetoothAdapter.checkBluetoothAddress(strAddr)) { // 檢查藍牙地址是否有效 Log.d("mylog", "devAdd un effient!"); } BluetoothDevice device = mBA.getRemoteDevice(strAddr); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { Log.d("mylog", "NOT BOND_BONDED"); try { setPin(device, strPsw); // 手機和藍牙采集器配對 createBond(device); result = true; } catch (Exception e) { Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } // } else { Log.d("mylog", "HAS BOND_BONDED"); try { createBond(device); setPin(device, strPsw); // 手機和藍牙采集器配對 createBond(device); result = true; } catch (Exception e) { Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } } return result; } /** * 獲取device.getClass()這個類中的所有Method * * @param clsShow */ public void printAllInform(Class clsShow) { try { // 取得所有方法 Method[] hideMethod = clsShow.getMethods(); int i = 0; for (; i < hideMethod.length; i++) { Log.e("method name", hideMethod[i].getName() + ";and the i is:" + i); } // 取得所有常量 Field[] allFields = clsShow.getFields(); for (i = 0; i < allFields.length; i++) { Log.e("Field name", allFields[i].getName()); } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 打開藍牙 */ public void openBlueTooth() { if (!mBA.isEnabled()) { // 彈出對話框提示用戶是后打開 /*Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent, 1);*/ // 不做提示,強行打開 mBA.enable(); showToast("打開藍牙"); } else { showToast("藍牙已打開"); } } /** * 關閉藍牙 */ public void closeBlueTooth() { mBA.disable(); showToast("關閉藍牙"); } /** * 彈出Toast窗口 * * @param message */ private void showToast(String message) { if (mContext != null) { Toast.makeText(mContext, message, Toast.LENGTH_LONG).show(); } else { Log.e(TAG, "message:" + message); } } /** * 主動連接藍牙 * * @param device */ public void connectDevice(BluetoothDevice device) { // 判斷是否在搜索,如果在搜索,就取消搜索 if (mBA.isDiscovering()) { mBA.cancelDiscovery(); } try { // 獲得遠程設備 if (mCurDevice == null || mCurDevice != mOldDevice) { mCurDevice = mBA.getRemoteDevice(device.getAddress()); mOldDevice = mCurDevice; Log.e(TAG, "device:" + mCurDevice); // sp.edit().putString("device","device:"+mCurDevice).commit(); mSocket = mCurDevice.createRfcommSocketToServiceRecord(MY_UUID); // 連接 // mSocket.connect(); // boolean connected = mSocket.isConnected(); // if (connected){ // // // 獲得輸出流 // os = mSocket.getOutputStream(); // inputStream=mSocket.getInputStream(); // while (true){ // readDataFromServer(); // } // } new Thread(new Runnable() { @Override public void run() { try { mSocket.connect(); os = mSocket.getOutputStream(); mhandler.sendEmptyMessage(10); readtask = new BlueToothUtils.Readtask(); //連接成功后開啟讀取數據的線程 readtask.start(); mContext.getSharedPreferences("demo",Context.MODE_PRIVATE).edit().putString("device","已連接").commit(); } catch (IOException e) { mhandler.sendEmptyMessage(11); e.printStackTrace(); } } }).start(); } // 如果成功獲得輸出流 } catch (IOException e) { e.printStackTrace(); mhandler.sendEmptyMessage(11); } } public static final int CONNECT_FAILED=1; public static final int CONNECT_SUCCESS=5; public static final int READ_FAILED=2; public static final int WRITE_FAILED=3; public static final int DATA=4; private boolean isConnect=false; /** *開辟連接線程任務 */ public void connectTow(final BluetoothDevice device){ Thread thread = new Thread(new Runnable() { @Override public void run() { BluetoothSocket tmp = null; Method method; try { method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class}); tmp = (BluetoothSocket) method.invoke(device, 1); } catch (Exception e) { // setState(CONNECT_FAILED); Log.e("TAG", e.toString()); } mSocket = tmp; try { mSocket.connect(); isConnect = true; // setState(CONNECT_SUCCESS); readtask = new BlueToothUtils.Readtask(); //連接成功后開啟讀取數據的線程 readtask.start(); } catch (Exception e) { Log.e("TAG", e.toString()); } } }); new Thread(thread).start(); } /** *開辟線程讀任務 */ public class Readtask extends Thread{ @Override public void run(){ // byte[] buffer = new byte[9999]; // int bytes; // InputStream inputStream ; //建立輸入流讀取數據 // while (true) { // try { // inputStream = mSocket.getInputStream(); // if ((bytes = inputStream.read(buffer)) > 0) { // byte[] buf_data= new byte[bytes]; // for (int i = 0; i < bytes; i++) { // buf_data[i] = buffer[i]; // } // String s = new String(buf_data); // // setState(1,s); // // } // } catch (IOException e) { // // Log.e("TAG", e.toString()); // break; // } // } byte[] tt = new byte[100]; try { int ch; inputStream = mSocket.getInputStream(); int i; while (true) { synchronized (this) { i=0; while ((ch=inputStream.read())!='#'){ if (ch!=-1){ tt[i]=(byte)ch; i++; } } if (tt.length > 0) { String s = new String(tt, "GBK"); System.out.println(s); setState(1,s); }else { System.out.println(111); } } } } catch (IOException e) { e.printStackTrace(); Message message=new Message(); message.obj="連接已斷開,請重新連接"; handler.sendMessage(message); } if (mSocket != null) { try { mSocket.close(); } catch (IOException e) { Log.e("TAG", e.toString()); } } } public void cancel() { try { mSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } /** *開辟線程寫任務 */ public class WriteTask extends Thread{ private String srt; public WriteTask(String str){ this.srt=str; } @Override public void run(){ OutputStream outputStream=null; byte[] st=srt.getBytes(); try{ outputStream=mSocket.getOutputStream(); outputStream.write(st); }catch (Exception e){ e.printStackTrace(); } } } private void setState(int mes,String str){ Message message=new Message(); message.what=mes; message.obj=str; handler2.sendMessage(message); } /** * 傳輸數據 * * @param message */ public void write(String message) { try { if (os != null) { os.write(message.getBytes("GBK")); } Log.e(TAG, "write:" + message); handler2.sendEmptyMessage(3); } catch (IOException e) { e.printStackTrace(); } } /** * Stop all threads */ public synchronized void stops() { if (readtask != null) {readtask.cancel(); readtask = null;} Message message=new Message(); message.obj="關閉所有線程"; handler.sendMessage(message); } public void initSP(){ sp=mContext.getSharedPreferences("demo",Context.MODE_PRIVATE); } // 服務端,需要監聽客戶端的線程類 private Handler handler = new Handler() { public void handleMessage(Message msg) { showToast(String.valueOf(msg.obj)); Log.e(TAG, "服務端:" + msg.obj); super.handleMessage(msg); } }; // 線程服務類 public class AcceptThread extends Thread { private BluetoothServerSocket serverSocket; private BluetoothSocket socket; // 輸入 輸出流 private OutputStream os; private InputStream is; public AcceptThread() { try { serverSocket = mBA.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { // 截獲客戶端的藍牙消息 try { socket = serverSocket.accept(); // 如果阻塞了,就會一直停留在這里 is = socket.getInputStream(); os = socket.getOutputStream(); while (true) { synchronized (this) { byte[] tt = new byte[is.available()]; if (tt.length > 0) { is.read(tt, 0, tt.length); Message msg = new Message(); msg.obj = new String(tt, "GBK"); Log.e(TAG, "客戶端:" + msg.obj); handler.sendMessage(msg); } } } } catch (Exception e) { e.printStackTrace(); } } } }
第二個工具類
/** *建立和管理與其他設備的BT連接。 *它有一個線程來偵聽傳入的連接,一個線程 *用於與設備連接,以及用於執行數據的線程 *連接時的傳輸。 */ public class BTService { // 表示當前連接狀態的常量 public static final int STATE_NONE = 0; // 我們什么也不做 public static final int STATE_LISTEN = 1; // 現在監聽傳入的連接 public static final int STATE_CONNECTING = 2; // 現在啟動輸出連接 public static final int STATE_CONNECTED = 3; // 現在連接到遠程設備 private static final String TAG = "BTConnection"; // 創建服務器套接字時SDP記錄的名稱 private static final String MY_NAME = "BTConnection"; //引用:[來自Android SDK文檔] //如果您正在連接藍牙串行板,則嘗試使用眾所周知的 //SPP UUID 000 000 110~00 0 0 0 0 0 0 0 0 0 0 0 5 5 F9B34 FB。 // 但是,如果您正在連接Android對等體,那么請生成您自己的 // ///獨特的UUID。 private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // 成員字段 private final BluetoothAdapter mAdapter; private final Handler mHandler; private AcceptThread mAcceptThread; private ConnectThread mConnThread; private ConnectedThread mConnectedThread; private int mState; private final Context mContext; public static BTService sInstance; /** * Constructor. Prepares a new BT session. * * @param handler A Handler to send message back to the UI Activity. * 構造函數。准備新的BT會話。 * * @param handler一個Handler,用於將消息發送回UI Activity。 */ public BTService(Context context, Handler handler) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mState = STATE_NONE; mHandler = handler; mContext = context; } /** * 判斷是否打開藍牙 * * @return */ public boolean isEnabled() { if (mAdapter.isEnabled()) { return true; } return false; } /** * 搜索設備 */ public void searchDevices() { // 判斷是否在搜索,如果在搜索,就取消搜索 if (mAdapter.isDiscovering()) { mAdapter.cancelDiscovery(); } // 開始搜索 mAdapter.startDiscovery(); Log.e(TAG, "正在搜索..."); } /** * 獲取已經配對的設備 * * @return */ public List<BluetoothDevice> getBondedDevices() { List<BluetoothDevice> devices = new ArrayList<>(); Set<BluetoothDevice> pairedDevices = mAdapter.getBondedDevices(); // 判斷是否有配對過的設備 if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { devices.add(device); Log.e(TAG, "BondedDevice:" + device.getName()); } } return devices; } /** * 與設備配對 * * @param device */ public void createBond(BluetoothDevice device) { try { Method createBondMethod = BluetoothDevice.class.getMethod("createBond"); createBondMethod.invoke(device); } catch (Exception e) { e.printStackTrace(); } } /** * 與設備解除配對 * * @param device */ public void removeBond(BluetoothDevice device) { try { Method removeBondMethod = device.getClass().getMethod("removeBond"); removeBondMethod.invoke(device); } catch (Exception e) { e.printStackTrace(); } } /** * * @param device * @param str 設置PIN碼 * @return */ public boolean setPin(BluetoothDevice device, String str) { try { Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class}); Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{str.getBytes()}); Log.e("returnValue", "" + returnValue); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return true; } /** * 取消用戶輸入 */ public boolean cancelPairingUserInput(BluetoothDevice device) { Boolean returnValue = false; try { Method createBondMethod = device.getClass().getMethod("cancelPairingUserInput"); returnValue = (Boolean) createBondMethod.invoke(device); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // cancelBondProcess() return returnValue.booleanValue(); } /** * 取消配對 */ public boolean cancelBondProcess(BluetoothDevice device) { Boolean returnValue = null; try { Method createBondMethod = device.getClass().getMethod("cancelBondProcess"); returnValue = (Boolean) createBondMethod.invoke(device); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return returnValue.booleanValue(); } /** * @param strAddr * @param strPsw * @return */ public boolean pair(String strAddr, String strPsw) { boolean result = false; mAdapter.cancelDiscovery(); if (!mAdapter.isEnabled()) { mAdapter.enable(); } if (!BluetoothAdapter.checkBluetoothAddress(strAddr)) { // 檢查藍牙地址是否有效 Log.d("mylog", "devAdd un effient!"); } BluetoothDevice device = mAdapter.getRemoteDevice(strAddr); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { Log.d("mylog", "NOT BOND_BONDED"); try { setPin(device, strPsw); // 手機和藍牙采集器配對 createBond(device); result = true; } catch (Exception e) { Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } // } else { Log.d("mylog", "HAS BOND_BONDED"); try { createBond(device); setPin(device, strPsw); // 手機和藍牙采集器配對 createBond(device); result = true; } catch (Exception e) { Log.d("mylog", "setPiN failed!"); e.printStackTrace(); } } return result; } /** * 獲取device.getClass()這個類中的所有Method * * @param clsShow */ public void printAllInform(Class clsShow) { try { // 取得所有方法 Method[] hideMethod = clsShow.getMethods(); int i = 0; for (; i < hideMethod.length; i++) { Log.e("method name", hideMethod[i].getName() + ";and the i is:" + i); } // 取得所有常量 Field[] allFields = clsShow.getFields(); for (i = 0; i < allFields.length; i++) { Log.e("Field name", allFields[i].getName()); } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 打開藍牙 */ public void openBlueTooth() { if (!mAdapter.isEnabled()) { // 彈出對話框提示用戶是后打開 /*Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent, 1);*/ // 不做提示,強行打開 mAdapter.enable(); showToast("打開藍牙"); } else { showToast("藍牙已打開"); } } /** * 關閉藍牙 */ public void closeBlueTooth() { mAdapter.disable(); showToast("關閉藍牙"); } /** * 彈出Toast窗口 * * @param message */ private void showToast(String message) { if (mContext != null) { Toast.makeText(mContext, message, Toast.LENGTH_LONG).show(); } else { Log.e(TAG, "message:" + message); } } public static synchronized BTService getInstance(Context context, Handler handler) { if (sInstance == null) { sInstance = new BTService(context,handler); } return sInstance; } public BluetoothAdapter getBA() { return mAdapter; } /** * Return the current connection state. * * @return mState current connection state * 返回當前連接狀態。 * * @return mState當前連接狀態 */ public synchronized int getState() { return mState; } /** * Set the current state of BT connection. * * @param state An integer defining the current connection state * * *設置BT連接的當前狀態。 * * @param state定義當前連接狀態的整數 */ private synchronized void setState(int state) { Log.d(TAG, "setState() " + mState + "-> " + state); mState = state; // Give the new state to the Handler so the UI Activity can update //將新狀態提供給處理程序,以便UI活動可以更新 mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); } /** * Start the BT service. Specifically start AcceptThread to begin a * session in listening (server) mode. Called by the Activity onResume() * *啟動BT服務。具體來說,啟動AcceptThread以在偵聽(服務器)模式下開始*會話。由Activity onResume()調用 */ public synchronized void start() { Log.d(TAG, "start"); // Cancel any thread attempting to make a connection //取消任何嘗試建立連接的線程 if (mConnThread != null) { mConnThread.cancel(); mConnThread = null; } // Cancel any thread currently running a connection //取消當前正在運行連接的任何線程 if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } setState(STATE_LISTEN); // Start the thread to listen on a BluetoothServerSocket //啟動線程以偵聽BluetoothServerSocket if (mAcceptThread == null) { mAcceptThread = new AcceptThread(); mAcceptThread.start(); } } /** * Start the ConnThread to initiate a connection to a remote device. *啟動ConnThread以啟動與遠程設備的連接。 * @param device The BluetoothDevice to connect */ public synchronized void connect(BluetoothDevice device) { Log.d(TAG, "Connect to: " + device); // Cancel any thread attempting to make a connection //取消任何嘗試建立連接的線程 if (mState == STATE_CONNECTING) { if (mConnThread != null) { mConnThread.cancel(); mConnThread = null; } } // Cancel any thread currently running a connection //取消當前正在運行連接的任何線程 if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } // Start the thread to connect with the given device //啟動線程以連接給定設備 mConnThread = new ConnectThread(device); mConnThread.start(); setState(STATE_CONNECTING); } /** * 啟動ConnectedThread以開始管理藍牙連接。 * * @param socket 連接所在的BluetoothSocket * @param device 已連接的BluetoothDevice */ public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) { Log.d(TAG, "Connected"); // 取消完成連接的線程 if (mConnThread != null) { mConnThread.cancel(); mConnThread = null; } //取消當前正在運行連接的任何線程 if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } //取消接受線程,因為我們只想連接到一個設備 if (mAcceptThread != null) { mAcceptThread.cancel(); mAcceptThread = null; } //啟動線程來管理連接並執行傳輸 mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); // 將已連接設備的名稱發送回UI活動 Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME); Bundle bundle = new Bundle(); bundle.putString(Constants.DEVICE_NAME, device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); setState(STATE_CONNECTED); } /** * 停止所有線程。 */ public synchronized void stop() { Log.d(TAG, "stop"); if (mConnThread != null) { mConnThread.cancel(); mConnThread = null; } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } if (mAcceptThread != null) { mAcceptThread.cancel(); mAcceptThread = null; } setState(STATE_NONE); } /** * 以不同步的方式寫入ConnectedThread。 * * @param content 要寫入的字節 * @see */ public void write(String content) { //創建臨時對象 ConnectedThread r; //同步ConnectedThread的副本 synchronized (this) { if (mState != STATE_CONNECTED) { Log.d(TAG, "State: " + getState()); return; } r = mConnectedThread; } //執行寫入不同步 r.write(content); } /** * 指示連接嘗試失敗並通知UI活動。 */ private void connectionError(String errMsg) { Log.e(TAG, "Connection Error:" + errMsg); // 將失敗消息發送回活動 Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(Constants.TOAST, errMsg); msg.setData(bundle); mHandler.sendMessage(msg); // 啟動服務以重新啟動偵聽模式 BTService.this.start(); } /** *此線程在偵聽傳入連接時運行。它表現得很好 *像服務器端客戶端。它會一直運行,直到接受連接 *(或直到取消)。 */ private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; public AcceptThread() { //使用稍后分配給mmServerSocket的臨時對象 //因為mmServerSocket是最終的 BluetoothServerSocket tmp = null; try { tmp = mAdapter.listenUsingRfcommWithServiceRecord(MY_NAME, MY_UUID); } catch (IOException e) { Log.e(TAG, "套接字聽不通", e); } mmServerSocket = tmp; } public void run() { Log.d(TAG, "套接字開始"); BluetoothSocket socket = null; // Listen to the server socket if we're not connected while (mState != STATE_CONNECTED) { try { //這是一個阻止調用,只會返回 //成功連接或異常 socket = mmServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "套接字accept()失敗", e); break; } //如果接受了連接 if (socket != null) { synchronized (BTService.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: //情況正常啟動連接的線程。 connected(socket, socket.getRemoteDevice()); break; case STATE_NONE: case STATE_CONNECTED: //未准備好或已連接。終止新套接字。 try { socket.close(); } catch (IOException e) { Log.e(TAG, "無法關閉不需要的套接字", e); } break; } } } } } public void cancel() { Log.d(TAG, "套接字取消"); try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "服務器的套接字close()失敗", e); } } } /***此線程在嘗試進行傳出連接時運行 *帶有設備。它直接通過;連接 *成功或失敗。 */ private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; private ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; //獲取與之連接的BluetoothSocket //給出了BluetoothDevice try { // device.fetchUuidsWithSdp(); // ParcelUuid[] uuids = device.getUuids(); // for (ParcelUuid u : uuids) { // Log.d(TAG, u.getUuid().toString()); // } tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket create() failed", e); } mmSocket = tmp; } public void run() { try { mmSocket.connect(); } catch (IOException e) { e.printStackTrace(); try { mmSocket.close(); } catch (IOException e1) { e1.printStackTrace(); } connectionError("連接失敗"); return; } //重置ConnectThread,因為我們已經完成了 synchronized (BTService.this) { mConnThread = null; } //啟動連接的線程 connected(mmSocket, mmDevice); } public void cancel() { Log.d(TAG, "Socket cancel"); try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "Socket close() of server failed", e); } } } /** *此線程在與遠程設備連接期間運行。 *它處理所有傳入和傳出傳輸。 */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; private ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "create ConnectedThread"); mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; //獲取BluetoothSocket輸入和輸出流 try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.d(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[512]; byte[] ret = null; //連接時繼續收聽inputStream while (true) { try { //從InputStream中讀取 int bytes = mmInStream.read(buffer); Log.e(TAG, "length: " + bytes); for (int i = 0; i < bytes; i++) { if ((buffer[i] & 0xFF) == 255) { ret = Arrays.copyOfRange(buffer, 0, i + 1); break; } } Log.e(TAG, "data: " + ret); //將獲取的字節發送到UI活動 mHandler.obtainMessage(Constants.MESSAGE_READ, -1, -1, ret).sendToTarget(); } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionError("連接失敗"); //啟動服務以重新啟動偵聽模式 BTService.this.start(); break; } } } /** * 寫入已連接的OutStream。 * * @param content The bytes to write */ public void write(String content) { try { mmOutStream.write(content.getBytes()); // 將發送的消息共享回UI活動 mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, content).sendToTarget(); } catch (IOException e) { Log.e(TAG, "E寫入期間的xception", e); } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "connect()的連接套接字失敗", e); } } }