[安卓] 11、串口蓝牙·将软硬结合进行到底

 

 

前言

上次我详细介绍了如何用笔记本搜索到蓝牙模块并与之通信:http://www.cnblogs.com/zjutlitao/p/3886826.html,这次将介绍如何让安卓手机的蓝牙和该蓝牙模块进行通信。

简单一步搞定

参看:【只需简单一步,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");

    

实现了搜索设备,链接设备,并和设备完成了通信~[图中我用串口助手通过蓝牙模块向外发送"litao"字符串,这时手机收到了数据;当用手机发送"yes"字符串时,串口收到了"yes"]

安卓蓝牙详解

蓝牙主要涉及的操作有:1、开启蓝牙 2、关闭蓝牙 3、能被搜到 4、获取配对设备 5、数据传输【参考DLUTBruceZhang的专栏·部分摘抄Android 通信--蓝牙

1、蓝牙设备-->蓝牙设备主要包括本地设备和远程设备,和他们相关的函数如下图:

 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  }

2、搜索周边设备startDiscovery()-->这里startDiscovery是BluetoothAdapter的成员函数,其可以执行一个异步方式获得周边蓝牙设备,因为是一个异步的方法所以我们不需要考虑线程被阻塞问题,整个过程大约需要12秒时间,这时我们可以注册一个 BroadcastReceiver 对象来接收查找到的蓝牙设备信息,我们通过Filter来过滤ACTION_FOUND这个 Intent动作以获取每个远程设备的详细信息,通过Intent字段EXTRA_DEVICE 和 EXTRA_CLASS可以获得包含了每个BluetoothDevice 对象和对象的该设备类型 BluetoothClass。

 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 }

3、被周边设备所发现-->如果需要用户确认操作,不需要获取底层蓝牙服务实例,可以通过一个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);

4、配对-->通过下面的方法遍历所有配对设备,一会我们还会针对刚开始的例子进行详细讲解里面的具体过程。

 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 }

5、通信-->蓝牙的通信过程和TCP的server和client类似,这里就不详细介绍了,我参考的这篇文章讲的很详细,如果不懂可以参考下:http://blog.csdn.net/dlutbrucezhang/article/details/8955104#。此外,一会我还会针对具体的例子讲解其流程。

6、权限-->当然,想使用蓝牙还得再AndroidManifest.xml中添加上权限:

1 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
2 <uses-permission android:name="android.permission.BLUETOOTH" />

BluetoothChat例子分析

1、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 }

2、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 }

3、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

  

posted @ 2015-01-18 17:23  beautifulzzzz  阅读(5375)  评论(2编辑  收藏  举报