前言html
上次我詳細介紹瞭如何用筆記本搜索到藍牙模塊並與之通訊:http://www.cnblogs.com/zjutlitao/p/3886826.html,此次將介紹如何讓安卓手機的藍牙和該藍牙模塊進行通訊。java
簡單一步搞定android
參看:【只需簡單一步,android自帶的示例程序 BluetoothChat 變藍牙串口助手 :http://www.amobbs.com/thread-5426293-1-1.html】,只要把Android自帶的BluetoothChat例程稍微改一下就能搞定:數組
BluetoothChatService.java的第49行
private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
中的字符串不一樣,因而把他替換成藍牙串口服務 (SPP) 的 UUID
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");session
實現了搜索設備,連接設備,並和設備完成了通訊~[圖中我用串口助手經過藍牙模塊向外發送"litao"字符串,這時手機收到了數據;當用手機發送"yes"字符串時,串口收到了"yes"]異步
安卓藍牙詳解ide
藍牙主要涉及的操做有:一、開啓藍牙 二、關閉藍牙 三、能被搜到 四、獲取配對設備 五、數據傳輸【參考DLUTBruceZhang的專欄·部分摘抄:Android 通訊--藍牙】函數
一、藍牙設備-->藍牙設備主要包括本地設備和遠程設備,和他們相關的函數以下圖:this
1 // 獲取本地的藍牙適配器實例 2 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 3 if(adapter!=null) 4 { 5 if(!adapter.isEnabled()) 6 { 7 //經過這個方法來請求打開咱們的藍牙設備 8 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 9 startActivity(intent); 10 } 11 } 12 else 13 { 14 System.out.println("本地設備驅動異常!"); 15 }
二、搜索周邊設備startDiscovery()-->這裏startDiscovery是BluetoothAdapter的成員函數,其能夠執行一個異步方式得到周邊藍牙設備,由於是一個異步的方法因此咱們不須要考慮線程被阻塞問題,整個過程大約須要12秒時間,這時咱們能夠註冊一個 BroadcastReceiver 對象來接收查找到的藍牙設備信息,咱們經過Filter來過濾ACTION_FOUND這個 Intent動做以獲取每一個遠程設備的詳細信息,經過Intent字段EXTRA_DEVICE 和 EXTRA_CLASS能夠得到包含了每一個BluetoothDevice 對象和對象的該設備類型 BluetoothClass。spa
1 //實現一個BluetoothReciever繼承BroadcastReceiver來接收查找到的藍牙設備信息 2 private class BluetoothReciever extends BroadcastReceiver { 3 @Override 4 public void onReceive(Context context, Intent intent) { 5 // TODO Auto-generated method stub 6 String action = intent.getAction(); 7 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 8 BluetoothDevice device = intent 9 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 10 System.out.println(device.getAddress()); 11 } 12 } 13 } 14 //註冊這個Receiver 15 IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 16 bluetoothReceive = new BluetoothReciever(); 17 registerReceiver(bluetoothReceive, intentFilter); 18 //由於在註冊一個Receiver後,程序並不知道該什麼時候去回收它,因此須要咱們本身重寫Activity類的onDestroy()方法。 19 protected void onDestroy() { 20 // TODO Auto-generated method stub 21 unregisterReceiver(bluetoothReceive); 22 super.onDestroy(); 23 }
三、被周邊設備所發現-->若是須要用戶確認操做,不須要獲取底層藍牙服務實例,能夠經過一個Intent來傳遞ACTION_REQUEST_DISCOVERABLE參數, 這裏經過startActivity來請求開啓。
1 Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); 2 //50這個參數表明的是藍牙設備能在多少秒內被發現 3 discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 50); 4 startActivity(discoverableIntent);
四、配對-->經過下面的方法遍歷全部配對設備,一會咱們還會針對剛開始的例子進行詳細講解裏面的具體過程。
1 //經過getBondedDevices方法來獲取已經與本設備配對的設備 2 Set<BluetoothDevice> device= adapter.getBondedDevices(); 3 if(device.size()>0) 4 { 5 for(Iterator iterator=device.iterator();iterator.hasNext();) 6 { 7 BluetoothDevice bluetoothDevice=(BluetoothDevice)iterator.next(); 8 System.out.println(bluetoothDevice.getAddress()); 9 } 10 }
五、通訊-->藍牙的通訊過程和TCP的server和client相似,這裏就不詳細介紹了,我參考的這篇文章講的很詳細,若是不懂能夠參考下:http://blog.csdn.net/dlutbrucezhang/article/details/8955104#。此外,一會我還會針對具體的例子講解其流程。
六、權限-->固然,想使用藍牙還得再AndroidManifest.xml中添加上權限:
1 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 2 <uses-permission android:name="android.permission.BLUETOOTH" />
BluetoothChat例子分析
一、BluetoothChat.java-->以下圖在BluetoothChat中進行了得到藍牙設備、開啓藍牙設備、並啓動BluetoothChatService進行鏈接,而後用Handler mHandler進行接收從BluetoothCharService的消息並做出相應的處理。在這裏的list是爲了顯示藍牙設備的通話信息,一條一條顯示;edittext用來獲取本地輸入的消息,按回車或者send按鈕均可以觸發sendMessage進行發送消息;此外,因爲須要手動搜索藍牙設備並選擇要連接的藍牙設備,因此這裏重寫了菜單按鈕監聽,包括onCreateOptionsMenu和onOptionsItemSelected,在onOptionsItemSelected中對san和discoverable進行分別處理。
下面是onCreate中的獲取本地藍牙設備,並確認是否支持,若是不支持就退出程序。
1 // Get local Bluetooth adapter 2 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 3 4 // If the adapter is null, then Bluetooth is not supported 5 if (mBluetoothAdapter == null) { 6 Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); 7 finish(); 8 return; 9 }
下面是onStart中若是藍牙沒有打開就發送Intent意圖,請求打開藍牙,setupChat()將會被執行在onActivityResult中。
1 // If BT is not on, request that it be enabled. 2 // setupChat() will then be called during onActivityResult 3 if (!mBluetoothAdapter.isEnabled()) { 4 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 5 startActivityForResult(enableIntent, REQUEST_ENABLE_BT); 6 // Otherwise, setup the chat session 7 } else { 8 if (mChatService == null) setupChat(); 9 }
下面是onActivityResult函數,其中包括2個處理:①處理REQUEST_CONNECT_DEVICE請求鏈接設備;②處理REQUEST_ENABLE_BT請求打開藍牙
1 public void onActivityResult(int requestCode, int resultCode, Intent data) { 2 if(D) Log.d(TAG, "onActivityResult " + resultCode); 3 switch (requestCode) { 4 case REQUEST_CONNECT_DEVICE: 5 // When DeviceListActivity returns with a device to connect 6 if (resultCode == Activity.RESULT_OK) { 7 // Get the device MAC address 8 String address = data.getExtras() 9 .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); 10 // Get the BLuetoothDevice object 11 BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); 12 // Attempt to connect to the device 13 mChatService.connect(device); 14 } 15 break; 16 case REQUEST_ENABLE_BT: 17 // When the request to enable Bluetooth returns 18 if (resultCode == Activity.RESULT_OK) { 19 // Bluetooth is now enabled, so set up a chat session 20 setupChat(); 21 } else { 22 // User did not enable Bluetooth or an error occured 23 Log.d(TAG, "BT not enabled"); 24 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show(); 25 finish(); 26 } 27 } 28 }
在setupChat中對發送按鈕進行點擊事件,其實比較簡繁就是獲取editText中的內容而後調用sendMessage發送。如上圖所示,對editText的回車監聽也和這個相似,這裏就很少介紹了。
1 // Initialize the send button with a listener that for click events【發送按鈕及事件】 2 mSendButton = (Button) findViewById(R.id.button_send); 3 mSendButton.setOnClickListener(new OnClickListener() { 4 public void onClick(View v) { 5 // Send a message using content of the edit text widget【從textview中獲取字符而後調用sendMseeage發送出去】 6 TextView view = (TextView) findViewById(R.id.edit_text_out); 7 String message = view.getText().toString(); 8 sendMessage(message); 9 } 10 });
下面是重寫的菜單按鈕監聽,當選擇的是scan按鈕時,發送REQUEST_CONNECT_DEVICE意圖。當選擇discoverable按鈕時,則確認是否能被搜到。這裏須要特別說明下:這個菜單要在res/menu中寫。
1 public boolean onOptionsItemSelected(MenuItem item) { 2 switch (item.getItemId()) { 3 case R.id.scan: 4 // Launch the DeviceListActivity to see devices and do scan 5 Intent serverIntent = new Intent(this, DeviceListActivity.class); 6 startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); 7 return true; 8 case R.id.discoverable: 9 // Ensure this device is discoverable by others 10 ensureDiscoverable(); 11 return true; 12 } 13 return false; 14 }
二、BluetoothChatService-->上面說過在setupChat中最後實例並初始化BluetoothChatService進行藍牙鏈接,下面來大體分析下該類的構成及功能:該類主要實現鏈接藍牙的3個狀態,分別用3個線程來處理:①AcceptThread線程,就像service-client中的accept函數,一直等待,知道接受一個鏈接;②ConnectThread線程負責鏈接;③ConnectedThread線程負責和遠程藍牙設備進行通訊,並將收到和要發送的信息經過handles進行傳遞。這裏咱們主要分析鏈接以後的數據交換過程,對於等待鏈接、嘗試鏈接這裏不作詳解~
ConnectedThread線程主要負責數據收發,其把從遠端收來的數據和要發向遠端的數據都會經過handle發送給UI用於在list中顯示。對於收數據,這裏採用利用線程一直收,一旦收到數據就經過mHandler傳遞到BluetoothChat進行處理,對於發送數據,不必採用線程輪流發,而是直接一個函數,何時須要就直接調用發送就能夠。
1 // 利用線程一直收數據 2 // 將數據都是放在mHandler中,在BluetoothChat中對信息解析並處理 3 public void run() { 4 Log.i(TAG, "BEGIN mConnectedThread"); 5 byte[] buffer = new byte[1024]; 6 int bytes; 7 8 // Keep listening to the InputStream while connected 9 while (true) { 10 try { 11 // Read from the InputStream 12 // bytes是返回讀取的字符數量,其中數據存在buffer中 13 bytes = mmInStream.read(buffer); 14 String readMessage = new String(buffer, 0, bytes); 15 if (D) 16 Log.i(TAG, "read: " + bytes + " mes: " + readMessage); 17 // Send the obtained bytes to the UI Activity 18 mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, 19 -1, buffer).sendToTarget(); 20 } catch (IOException e) { 21 Log.e(TAG, "disconnected", e); 22 connectionLost(); 23 break; 24 } 25 } 26 }
三、DeviceListActivity-->這個類主要負責搜索藍牙設備並用列表顯示出來。那麼咱們首先來分析一下它所包含的2個list:可見這裏面並非一個list,其中pairedListView是曾經配對過的設備,這些設備可能如今沒有被搜到,可是也會顯示出來;newDevicesListView是新發現的藍牙設備,可是若是曾經有配對過的就不加入這個list中(因此,必定要弄清這兩個list不然你會很暈的!),他們採用同一個選項監聽:mDeviceClickListener,在他們共同的list點擊監聽中,當點擊某一個item時,程序得到item的string信息,而後轉換爲MAC地址,經過Intent傳到另外一個Activity中,進行相應處理。
1 // Find and set up the ListView for paired devices 2 ListView pairedListView = (ListView) findViewById(R.id.paired_devices); 3 pairedListView.setAdapter(mPairedDevicesArrayAdapter); 4 pairedListView.setOnItemClickListener(mDeviceClickListener); 5 6 // Find and set up the ListView for newly discovered devices 7 ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); 8 newDevicesListView.setAdapter(mNewDevicesArrayAdapter); 9 newDevicesListView.setOnItemClickListener(mDeviceClickListener);
1 // The on-click listener for all devices in the ListViews 2 //選擇list中的設備,而後經過inet傳出 3 private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { 4 public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) { 5 // Cancel discovery because it's costly and we're about to connect 6 mBtAdapter.cancelDiscovery(); 7 8 // Get the device MAC address, which is the last 17 chars in the View 9 String info = ((TextView) v).getText().toString(); 10 String address = info.substring(info.length() - 17); 11 12 // Create the result Intent and include the MAC address 13 Intent intent = new Intent(); 14 intent.putExtra(EXTRA_DEVICE_ADDRESS, address); 15 16 // Set result and finish this Activity 17 setResult(Activity.RESULT_OK, intent); 18 finish(); 19 } 20 };
對於scan按鈕,是用來啓動搜索藍牙設備的:從下面能夠看出該按鈕的點擊事件中,主要是調用了doDiscovery()函數,而在doDiscovery()函數中,主要進行的其實就只有一個藍牙設備自帶的成員函數:mBtAdapter.startDiscovery();這是你們可能有點摸不着頭腦,怎麼寫這一個函數就能得到藍牙設備了,那我搜索到的設備信息是在哪裏被保存的呢?我以爲是時候說一下藍牙搜索其餘設備的過程了!
1 // Initialize the button to perform device discovery 2 Button scanButton = (Button) findViewById(R.id.button_scan); 3 scanButton.setOnClickListener(new OnClickListener() { 4 public void onClick(View v) { 5 doDiscovery(); 6 v.setVisibility(View.GONE); 7 } 8 });
在【安卓藍牙詳解】的第2點我已經簡單介紹了startDiscovery()函數,這裏再結合該程序介紹一下吧!這裏startDiscovery是BluetoothAdapter的成員函數,其能夠執行一個異步方式得到周邊藍牙設備,由於是一個異步的方法因此咱們不須要考慮線程被阻塞問題,整個過程大約須要12秒時間,這時咱們能夠註冊一個 BroadcastReceiver 對象來接收查找到的藍牙設備信息,咱們經過Filter來過濾ACTION_FOUND這個 Intent動做以獲取每一個遠程設備的詳細信息,經過Intent字段EXTRA_DEVICE 和 EXTRA_CLASS能夠得到包含了每一個BluetoothDevice 對象和對象的該設備類型 BluetoothClass。所以下面咱們在onCreate後面註冊一個BroadcastReceiver 對象來接收查找到的藍牙設備信息,經過Filter來過濾ACTION_FOUND和ACTION_DISCOVERY_FINISHED這兩個Intent動做,來獲取每一個遠程設備的詳細信息。
1 // Register for broadcasts when a device is discovered 2 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 3 this.registerReceiver(mReceiver, filter); 4 5 // Register for broadcasts when discovery has finished 6 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 7 this.registerReceiver(mReceiver, filter); 8 9 // Get the local Bluetooth adapter 10 mBtAdapter = BluetoothAdapter.getDefaultAdapter();
下面就是實現的BroadcastReceiver,承接上面的在scan按鈕觸發doDiscovery()函數以後整個邏輯咱們不知道了的疑惑,如今咱們就明白了,當doDiscovery()被執行時,下面的BroadcastReceiver將可以收取在onCreate中闡明的ACTION_FOUND和ACTION_DISCOVERY_FINISHED這兩個Intent動做,以下當Intent是ACTION_FOUND時經過BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);得到藍牙設備,若是這個設備沒被配對過,則加入到newDevice數組。
1 // The BroadcastReceiver that listens for discovered devices and 2 // changes the title when discovery is finished 3 //【查找藍牙設備】 4 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 5 @Override 6 public void onReceive(Context context, Intent intent) { 7 String action = intent.getAction(); 8 // When discovery finds a device 9 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 10 // Get the BluetoothDevice object from the Intent 11 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 12 // If it's already paired, skip it, because it's been listed already 13 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { 14 mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); 15 } 16 // When discovery is finished, change the Activity title 17 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 18 setProgressBarIndeterminateVisibility(false); 19 setTitle(R.string.select_device); 20 if (mNewDevicesArrayAdapter.getCount() == 0) { 21 String noDevices = getResources().getText(R.string.none_found).toString(); 22 mNewDevicesArrayAdapter.add(noDevices); 23 } 24 } 25 } 26 };
最後在onCreate後還有點是將曾經配對過的藍牙設備加入pairedListView中顯示(再次提醒要區分好pairedListView和newDevicesListView這兩個list!)
1 // If there are paired devices, add each one to the ArrayAdapter 2 // 若是有藍牙設備就加入list中,這裏mPairedDevicesArrayAdapter是列表的數組 3 if (pairedDevices.size() > 0) { 4 findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); 5 for (BluetoothDevice device : pairedDevices) { 6 mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); 7 } 8 } else { 9 String noDevices = getResources().getText(R.string.none_paired).toString(); 10 mPairedDevicesArrayAdapter.add(noDevices); 11 }
相關連接
本文連接:http://www.cnblogs.com/zjutlitao/p/4231635.html
更多精彩:http://www.cnblogs.com/zjutlitao/p/
參考文章:http://blog.csdn.net/dlutbrucezhang/article/details/8955104#
參考文章:http://www.amobbs.com/thread-5426293-1-1.html
一些資料:http://pan.baidu.com/s/1kTzdIR1 zptn