[安卓] 十一、串口藍牙·將軟硬結合進行到底

 

 

前言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

相關文章
相關標籤/搜索