zoukankan      html  css  js  c++  java
  • [安卓] 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() + "
    " + 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() + "
    " + 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

      

  • 相关阅读:
    HDU 6197 array array array nlogn求最长子序列 思维
    HDU 6198 number number number 矩阵快速幂 找规律
    HDU 6195 cable cable cable 思维
    urumuqi 网络赛 H skiing DP
    urumqi 网络赛 E Half-consecutive Numbers 数论
    urumqi 网络赛 C Coconut 水题
    UVA 122 Trees on the level 二叉树 广搜
    POJ3468【线段树lazy操作】
    线段树萌新讲解+一道水题【一点一滴】
    hdoj5792 【树状数组】【未完待续】
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/4231635.html
Copyright © 2011-2022 走看看