標籤:+= 伺服器 turn sim 執行 基礎上 設定 getname 部落格
2017.10.20 之前參加一個大三學長的創業項目,做一個智能的車鎖App,用到嵌入式等技術,App需要藍芽、即時位置等技術,故查了幾篇相關技術文章,以此參考! //先說說如何開啟藍牙裝置和設定可見時間:
private void search() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (!adapter.isEnabled()) { adapter.enable(); } Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600為藍牙裝置可見時間 startActivity(enable); Intent searchIntent = new Intent(this, ComminuteActivity.class); startActivity(searchIntent); }
首先,需要獲得一個BluetoothAdapter,可以通過getDefaultAdapter()獲得系統預設的藍芽適配器,當然我們也可以自己指定,但這個真心沒有必要,至少我是不需要的。然後我們檢查手機的藍芽是否開啟,如果沒有,通過enable()方法開啟。接著我們再設定手機藍牙裝置的可見,可見時間可以自訂。
完成這些必要的設定後,我們就可以正式開始與藍芽模組進行通訊了:
public class ComminuteActivity extends Activity { private BluetoothReceiver receiver; private BluetoothAdapter bluetoothAdapter; private List<String> devices; private List<BluetoothDevice> deviceList; private Bluetooth client; private final String lockName = "BOLUTEK"; private String message = "000001"; private ListView listView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.search_layout); listView = (ListView) this.findViewById(R.id.list); deviceList = new ArrayList<BluetoothDevice>(); devices = new ArrayList<String>(); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothAdapter.startDiscovery(); IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); receiver = new BluetoothReceiver(); registerReceiver(receiver, filter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { setContentView(R.layout.connect_layout); BluetoothDevice device = deviceList.get(position); client = new Bluetooth(device, handler); try { client.connect(message); } catch (Exception e) { Log.e("TAG", e.toString()); } } }); } @Override protected void onDestroy() { unregisterReceiver(receiver); super.onDestroy(); } private final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case Bluetooth.CONNECT_FAILED: Toast.makeText(ComminuteActivity.this, "串連失敗", Toast.LENGTH_LONG).show(); try { client.connect(message); } catch (Exception e) { Log.e("TAG", e.toString()); } break; case Bluetooth.CONNECT_SUCCESS: Toast.makeText(ComminuteActivity.this, "串連成功", Toast.LENGTH_LONG).show(); break; case Bluetooth.READ_FAILED: Toast.makeText(ComminuteActivity.this, "讀取失敗", Toast.LENGTH_LONG).show(); break; case Bluetooth.WRITE_FAILED: Toast.makeText(ComminuteActivity.this, "寫入失敗", Toast.LENGTH_LONG).show(); break; case Bluetooth.DATA: Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show(); break; } } }; private class BluetoothReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (isLock(device)) { devices.add(device.getName()); } deviceList.add(device); } showDevices(); } } private boolean isLock(BluetoothDevice device) { boolean isLockName = (device.getName()).equals(lockName); boolean isSingleDevice = devices.indexOf(device.getName()) == -1; return isLockName && isSingleDevice; } private void showDevices() { ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, devices); listView.setAdapter(adapter); }}
要想與任何藍芽模組進行通訊,首先得搜到該裝置:
private class BluetoothReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (isLock(device)) { devices.add(device.getName()); } deviceList.add(device); } showDevices(); } }
在這之前,我們得先調用一個方法:
bluetoothAdapter.startDiscovery();
startDiscovery()方法是一個非同步方法呼叫,它會對其他藍牙裝置進行搜尋,期間為12秒。搜尋過程其實是在System Service中進行,我們可以通過cancelDiscovery()方法來停止這個搜尋。在系統搜尋藍牙裝置的過程中,系統可能會發送以下三個廣播:ACTION_DISCOVERY_START(開始搜尋),ACTION_DISCOVERY_FINISHED(搜尋結束)和ACTION_FOUND(找到裝置)。ACTION_FOUND這個才是我們想要的,這個Intent中包含兩個extra fields:EXTRA_DEVICE和EXTRA_CLASS,包含的分別是BluetoothDevice和BluetoothClass,BluetoothDevice中的EXTRA_DEVICE就是我們搜尋到的裝置對象。 確認搜尋到裝置後,我們可以從得到的BluetoothDevice對象中獲得裝置的名稱和地址。
在android中使用廣播需要我們註冊,這裡也不例外:
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); receiver = new BluetoothReceiver(); registerReceiver(receiver, filter);
廣播註冊後需要我們撤銷,這個可以放在這裡進行:
@Override protected void onDestroy() { unregisterReceiver(receiver); super.onDestroy(); }
這樣在Activity結束的時候就會自動撤銷該廣播,而不需要我們手動執行。
我這裡使用一個ListView來顯示搜尋到的藍牙裝置,但因為需要只限定一個藍牙裝置,所以這裡進行了檢查,檢查該裝置是否是我們的目標裝置,如果是,就添加。當然,為了防止重複添加,有必要增加這麼一句:
boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
搜尋到該裝置後,我們就要對該裝置進行串連。
public void connect(final String message) { Thread thread = new Thread(new Runnable() { 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()); } socket = tmp; try { socket.connect(); isConnect = true; } catch (Exception e) { setState(CONNECT_FAILED); Log.e("TAG", e.toString()); }
串連裝置之前需要UUID,所謂的UUID,就是用來進行配對的,全稱是Universally Unique Identifier,是一個128位的字串ID,用於進行唯一標識。網上的例子,包括Google的例子,它們的UUID都是說能用但是我用不了的,都會報出這樣的錯誤:
Service discovery failed
原因可能是作為唯一標識的UUID沒有發揮作用,所以,我就利用反射的原理,讓裝置自己提供UUID。
這個錯誤在我們把手機既當做用戶端有當做服務端的時候,同樣也有可能出現,因為作為伺服器的時候,我們需要的也是同一個UUID:
mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
作為用戶端是這樣的:
device.createRfcommSocketToServiceRecord(MY_UUID);
當兩個UUID想同時建立Rfcomm的通道時,我們的選擇都是在兩個線程中分別實現,但是忽略了一件最重要的事情:同一個時間只能充當一個角色!所以,解決這個問題的方法就是在我們相串連的裝置上也安裝同樣的應用程式,誰先發起串連誰就是用戶端,但我這裡是藍芽模組啊!!怎麼能安裝我的應用程式呢!!解決辦法就在下面的通訊中。
串連裝置之前還有一件事必須確保:
bluetoothAdapter.cancelDiscovery();
這是為了停掉搜尋裝置,否則串連可能會變得非常慢並且容易失敗。
有關於Socket的編程都需要我們設定一些狀態值來標識通訊的狀態,以方便我們調錯,而且串連應該放在一個線程中進行,要讓該線程與我們程式的主線程進行通訊,我們需要使用Handle,關於Handle的使用,可以參考我的另一篇部落格http://www.cnblogs.com/wenjiang/p/3180324.html,這裡不多講。
在使用Socket中,我注意到一個方法:isConnect(),它返回的是布爾值,但是根本就不需要使用到這個方法,Socket的串連如果沒有報錯,說明是已經串連上了。
在Google提供的例子中,我們可以看到Google的程式員的程式水平很高,一些好的編碼習慣我們可以學習一下,像是在try..catch中才定義的變數,我們應該在try...catch之前聲明一個臨時變數,然後再在try...catch後賦值給我們真正要使用的變數。這種做法的好處就是:如果我們直接就是使用真正的變數,當出現異常的時候,該變數的使用就會出現問題,而且很難進行排查,如果是臨時變數,我麼可以通過檢查變數的值來確定是否是賦值時出錯。
Google的例子中最大的感想就是滿滿的異常檢查,但也是因為這個,導致它的可讀性不高。java的異常處理機制有時候對於代碼的閱讀真的不是一件舒服的事情,能避免就盡量避免。
如果串連沒有問題,我們就可以和藍芽模組進行通訊:
if (isConnect) { try { OutputStream outStream = socket.getOutputStream(); outStream.write(getHexBytes(message)); } catch (IOException e) { setState(WRITE_FAILED); Log.e("TAG", e.toString()); } try { InputStream inputStream = socket.getInputStream(); int data; while (true) { try { data = inputStream.read(); Message msg = handler.obtainMessage(); msg.what = DATA; msg.arg1 = data; handler.sendMessage(msg); } catch (IOException e) { setState(READ_FAILED); Log.e("TAG", e.toString()); break; } } } catch (IOException e) { setState(WRITE_FAILED); Log.e("TAG", e.toString()); } } if (socket != null) { try { socket.close(); } catch (IOException e) { Log.e("TAG", e.toString()); } } } }
這裡包括寫入和讀取,用法和基本的Socket是一樣的,但是寫入的時候,需要將字串轉化為16進位:
private byte[] getHexBytes(String message) { int len = message.length() / 2; char[] chars = message.toCharArray(); String[] hexStr = new String[len]; byte[] bytes = new byte[len]; for (int i = 0, j = 0; j < len; i += 2, j++) { hexStr[j] = "" + chars[i] + chars[i + 1]; bytes[j] = (byte) Integer.parseInt(hexStr[j], 16); } return bytes; }
當然,這裡只是將手機當做用戶端,但是接收藍芽模組發送過來的資訊是沒有必要特意建立服務端的,我們只要一個不斷監聽並讀取對方訊息的迴圈就行。
很簡單的程式就能實現像是藍芽串口助手的功能,由於是項目的代碼,不能貼完整的代碼,但是基本上都在上面了,大家可以參考一下。要想使用藍芽,相應的許可權也是必不可少的:
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
這篇部落格出自:http://www.cnblogs.com/wenjiang/p/3200138.html
項目可以借鑒,但是開啟項目稍微有些問題
代碼放在github上:https://github.com/wenjiang/Bluetooth2.git
在此基礎上,可以用Android studio 來嘗試修改代碼建立藍芽通訊!
後續項目建成上傳至github上。
android 藍芽開發---與藍芽模組進行通訊 基於eclipse項目