Android安卓的蓝牙数据传输
想要开发一个通过蓝牙传输数据的安卓应用程序,理所当然你需要从安卓开发者的蓝牙页面开始,那里面详尽描述了所有必需的步骤,包括设备搜寻、配对、客户端/服务器套接字和RFCOMM通道等。
嘿嘿别急,在开始讨论套接字和线程编程之前还需要先执行一个基础的蓝牙操作,让我们来考虑一个取巧的做法,基于安卓的一个最重要的特性:设备的默认蓝牙应用提供的由一个给定应用发送用户到另一个应用。这样做将由安卓系统会替我们完成所有底层工作。
import android.bluetooth.BluetoothAdapter; //... // 函数中代码 // 检查设备是否支持蓝牙
<!--EndFragment-->BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); if (btAdapter == null) { // 设备不支持蓝牙 // 告诉用户我们无能为力 }
以上是我们需要执行的第一次检查。完成之后,让我们看看它如何用我们的应用启动蓝牙。
在过去的一篇关于手机短信编程的文章里,我们谈到关于隐式意图(Implicit Intent )允许我们指定希望系统处理的动作。 然后安卓会显示一个单选列表,列出所有能够完成我们要求的应用。下面是一段示例代码:
// 启动Android选择器 Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file_to_transfer) ); //... startActivity(intent);
在这段代码中,我们要求安卓系统发送一个文本文件。然后系统会显示所有已安装能够处理这一意图的活动列表,如下图所示:
我们可以看到,这个基础蓝牙应用在这个列表的最上端。我们可以点击它,当然也可以让用户选择其他活动去完成传送。我想,如果我们增加一点用户友好体验,可以自动启动那个蓝牙应用,而不是简单地丢出一张列表甩在用户脸上,把它和其他不必要的应用鱼龙混杂在一起让用户玩找茬……要怎么做呢?
一个办法是使用安卓的PackageManager,如下面代码所示:
// 打开对应的列表 PackageManager pm = getPackageManager(); List appsList = pm.queryIntentActivities( intent, 0); if(appsList.size() > 0 { // 处理 }
PackageManger中的queryIntentActivities()方法返回一个列表,其中内容正是我们前面看到的那张图中的所有选项,用迭代器遍历这个列表每个ResolveInfo对象里封装着我们需要的信息:
// 选择蓝牙 String packageName = null; String className = null; boolean found = false; for(ResolveInfo info: appsList){ packageName = info.activityInfo.packageName; if( packageName.equals("com.android.bluetooth")){ className = info.activityInfo.name; found = true; break;// 找到,结束查找 } } if(! found){ Toast.makeText(this, R.string.blu_notfound_inlist, Toast.LENGTH_SHORT).show(); // 退出 }
这段代码之后,我们拥有了自动调用蓝牙活动的必要信息:
// 设置启动蓝牙intent intent.setClassName(packageName, className); startActivity(intent);
我们所做的是使用包和其对应的类(在前面检索到的)。我们会对上面的代码很好奇,可能不知道“com.android.bluetooth”包的类名。如果我们将其打印出来会看到这些:“com.broadcom.bt.app.opp.OppLauncherActivity“OPP代表对象推送模式,是允许无线共享文件的系统组件。
看起来功德圆满了,但为了上述所有代码真正能够使用,蓝牙不仅仅需要设备支持也要由用户在设置里启用。那么我们想要做的第一件事情之一,就是要求用户启用蓝牙一段时间(有多长?我们认为必要的那么长,在这里是300秒):
import android.bluetooth.BluetoothAdapter; //... // 设备可被发现持续时间
inal int DISCOVER_DURATION = 300; // 请求码<span style="font-family: Calibri;">(request code),</span><span style="font-family: 宋体;">必须大于0</span>
<!--EndFragment-->private static final int REQUEST_BLU = 1; //... public void enableBlu(){ // 启动设备搜索——<span style="font-family: Calibri;"> </span><span style="font-family: 宋体;">这会自动启用蓝牙</span>
<!--EndFragment-->Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, DISCOVER_DURATION ); startActivityForResult(discoveryIntent, REQUEST_BLU); }
一旦我们指定我们希望从我们的活动得到一个返回值,调用startActivityForResult()用户会看到下面的对话框:
现在每当活动结束后会返回请求码,即我们已送出的第二参数:REQUEST_BLU,同时返回数据和我们的主活动通过onActivityResult()方法返回的结果码。我们知道请求码,因此必须核对。至于结果代码?很简单:如果用户回答“否”,上述要求的权限(或如果出现一个错误),结果码将会是RESULT_CANCELED。相反地如果用户接受,蓝牙文档指出结果码等于这设备处于可被发现状态的(即DISCOVER_DURATION,即300)的持续时间。
处理上述蓝牙对话框的代码如下:
// startActivityForResult执行完成... protected void onActivityResult (int requestCode, int resultCode, Intent data) { if (resultCode == DISCOVER_DURATION && requestCode == REQUEST_BLU) { // 这里编写你的代码 } else{ // 取消或出错 Toast.makeText(this, R.string.blu_cancelled, Toast.LENGTH_SHORT).show(); } }
以下是流程图,基本上解释了我们在做什么:
大功告成了吗?差不多是。最后,我们需要在Mainfest文件里请求蓝牙权限:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
好了,一切准备妥当。为了测试这一切,我们需要使用至少两款安卓设备,一个用于文件发送(安装我们的应用程序),其他接收设备支持BT。无图无真相,下面是几张屏幕截图。首先是负责发送的那台手机:
然后是对应的接收手机: