Bluetooth 蓝牙搜索、配对连接
<p>[android 蓝牙搜索、配对连接](<a href="http://www.2cto.com/kf/201502/374785.html">http://www.2cto.com/kf/201502/374785.html</a> "android 蓝牙搜索、配对连接")
[ Android中如何实现蓝牙的配对与连接](<a href="http://blog.csdn.net/u012910985/article/details/50387551">http://blog.csdn.net/u012910985/article/details/50387551</a> " Android中如何实现蓝牙的配对与连接")</p>
<h1>android 蓝牙搜索、配对连接</h1>
<p>蓝牙协议可以实现一个蓝牙设备和6到8个蓝牙设备进行通信。
1、蓝牙搜索的实现</p>
<p>利用蓝牙的发现和完成动作动态注册广播接受者获得蓝牙设备。</p>
<p>第一步,获得蓝牙适配器</p>
<pre><code>BluetoothAdapter mBtAdapter= BluetoothAdapter.getDefaultAdapter();
// 判断蓝牙是否打开
if (!mAdapter.isEnabled()) {
mAdapter.enable();
}</code></pre>
<p>第二步动态注册蓝牙搜索广播接收者</p>
<pre><code>// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);</code></pre>
<p>并且可以利用意图过滤器设置广播的优先级</p>
<pre><code>filter.setPriority(Integer.MAX_VALUE);</code></pre>
<p>对应的广播接收者:</p>
<pre><code>// The BroadcastReceiver that listens for discovered devices and
// changes the title when discovery is finished
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + &quot;\n&quot; + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
String noDevices = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevices);
}
}
}
};</code></pre>
<p>或者利用发现和完成动作定义两个广播接受者,在完成的动作中注销广播接收者。
关键代码如下:</p>
<pre><code>/**
* 接收器 当搜索蓝牙设备完成时调用
*/
private BroadcastReceiver _foundReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 将结果添加到列表中
_devices.add(device);
DeviceInfo info = new DeviceInfo();
info.setmDeviceName(device.getName());
info.setmDeviceMacAddr(device.getAddress());
infos.add(info);
info = null;
// 显示列表
showDevices();
}
};
private BroadcastReceiver _discoveryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 卸载注册的接收器
unregisterReceiver(_foundReceiver);
unregisterReceiver(this);
_discoveryFinished = true;
}
};</code></pre>
<p>这样便完成蓝牙的搜索了。</p>
<p>2、蓝牙配对</p>
<p>蓝牙要想通信目前是必须要先配对才能连接的。</p>
<p>蓝牙配对的api是hide的。但是api19可以直接调用蓝牙设备的配对方法。</p>
<p>所以配对都是利用反射的方法。这里有一个强大的工具类可以直接拿来使用,如下:</p>
<pre><code>package net.oschina_pdp.app.Utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
public class ClsUtils {
public ClsUtils() {
// TODO Auto-generated constructor stub
}
/**
* 与设备配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean createBond(Class&lt; ? extends BluetoothDevice&gt; btClass, BluetoothDevice btDevice)
throws Exception
{
Method createBondMethod = btClass.getMethod(&quot;createBond&quot;);
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
/**
* 与设备解除配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean removeBond(Class&lt;? extends BluetoothDevice&gt; btClass, BluetoothDevice btDevice)
throws Exception
{
Method removeBondMethod = btClass.getMethod(&quot;removeBond&quot;);
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
static public boolean setPin(Class btClass, BluetoothDevice btDevice,
String str) throws Exception
{
try
{
Method removeBondMethod = btClass.getDeclaredMethod(&quot;setPin&quot;,
new Class[]
{byte[].class});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
new Object[]
{str.getBytes()});
Log.e(&quot;returnValue设置密码&quot;, &quot;&quot; + returnValue.booleanValue());
return returnValue.booleanValue();
}
catch (SecurityException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
// 取消用户输入
static public boolean cancelPairingUserInput(Class&lt;?&gt; btClass,
BluetoothDevice device)
throws Exception
{
Method createBondMethod = btClass.getMethod(&quot;cancelPairingUserInput&quot;);
cancelBondProcess(btClass,device) ;
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
Log.i(&quot;取消对话框&quot;,&quot;cancelPairingUserInput&quot;+returnValue.booleanValue());
return returnValue.booleanValue();
}
// 取消配对
static public boolean cancelBondProcess(Class&lt;?&gt; btClass,
BluetoothDevice device)
throws Exception
{
Method createBondMethod = btClass.getMethod(&quot;cancelBondProcess&quot;);
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();
}
/**
*
* @param clsShow
*/
static public void printAllInform(Class&lt;?&gt; clsShow)
{
try
{
// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i &lt; hideMethod.length; i++)
{
Log.e(&quot;method name&quot;, hideMethod[i].getName() + &quot;;and the i is:&quot;
+ i);
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i &lt; allFields.length; i++)
{
Log.e(&quot;Field name&quot;, allFields[i].getName());
}
}
catch (SecurityException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static public boolean pair(String strAddr, String strPsw)
{
boolean result = false;
BluetoothAdapter bluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
bluetoothAdapter.cancelDiscovery();
if (!bluetoothAdapter.isEnabled())
{
bluetoothAdapter.enable();
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(strAddr);
if (device.getBondState() != BluetoothDevice.BOND_BONDED)
{
try
{
Log.d(&quot;mylog&quot;, &quot;NOT BOND_BONDED&quot;);
boolean flag1=ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
boolean flag2=ClsUtils.createBond(device.getClass(), device);
// remoteDevice = device; // 配对完毕就把这个设备对象传给全局的remoteDevice
result = true;
}
catch (Exception e)
{
// TODO Auto-generated catch block
Log.d(&quot;mylog&quot;, &quot;setPiN failed!&quot;);
e.printStackTrace();
} //
}
else
{
Log.d(&quot;mylog&quot;, &quot;HAS BOND_BONDED&quot;);
try
{
ClsUtils.removeBond(device.getClass(), device);
//ClsUtils.createBond(device.getClass(), device);
boolean flag1= ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
boolean flag2=ClsUtils.createBond(device.getClass(), device);
// remoteDevice = device; // 如果绑定成功,就直接把这个设备对象传给全局的remoteDevice
result = true;
}
catch (Exception e)
{
// TODO Auto-generated catch block
Log.d(&quot;mylog&quot;, &quot;setPiN failed!&quot;);
e.printStackTrace();
}
}
return result;
}
}</code></pre>
<p>蓝牙配对的关键代码:</p>
<pre><code>flag3= ClsUtils.createBond(device.getClass(), device);</code></pre>
<p>其中device是蓝牙设备。在配对的时候会有一个配对广播,可以自定义一个广播接受者获取配对广播,然后在这个广播接收者里设置pin值,取消确定对话框,实现自动配对。关键代码如下:</p>
<pre><code>mReceiver=new ParingReceiver(device);
IntentFilter filter=new IntentFilter();
filter.addAction( BluetoothDevice.ACTION_PAIRING_REQUEST);
filter.setPriority(Integer.MAX_VALUE);
registerReceiver(mReceiver, filter);
private class ParingReceived extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice btDevice=mAdapter.getRemoteDevice(&quot;EC:89:F5:98:46:f9&quot;);
try {
setPin(btDevice.getClass(),btDevice,&quot;000000&quot;);
cancelPairingUserInput(btDevice.getClass(), btDevice);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}</code></pre>
<p>在我的4.2系统上是没有效果的。找了一个上午的资料;网上给出了两种解决方法:(1)修改setting 系统源码,(2)模拟点击事件。</p>
<p>蓝牙配对完成后就可以连接通信了。</p>
<p>3、蓝牙通信</p>
<p>蓝牙同时的本质是蓝牙套接字,一个主动发起连接的的设备做客户端,一个监听连接的设备做服务端,类似sokcet网络编程,利用多线程,读取数据流就可完成蓝牙通信。</p>
<p>如下是蓝牙串口通信的关键代码:</p>
<pre><code>/**
* 建立连接并通信
*
* @param btDev
* @return
*/
private boolean connect(BluetoothDevice btDev) {
boolean flag = false;
try {
/*if(btDev.fetchUuidsWithSdp()){
btDev.getUuids();
}*/
//建立连接
mSocket = btDev
.createRfcommSocketToServiceRecord(UUID.fromString(&quot;00001101-0000-1000-8000-00805F9B34FB&quot;));
mSocket.connect();
mOutputStream = mSocket.getOutputStream();
mInputStream = mSocket.getInputStream();
mOutputStream.write(&quot;StartOnNet\n&quot;.getBytes());
mOutputStream.flush();
flag = true;
} catch (Exception e) {
}</code></pre>
<p>蓝牙服务端代码
private class ServerThread implements Runnable {</p>
<pre><code> private InputStream mInputStream;
private OutputStream mOutputStream;
public ServerThread() {
}
@Override
public void run() {
try {
while (true) {
mBluetoothServerSocket = mAdapter
.listenUsingRfcommWithServiceRecord(
&quot;btspp&quot;,
UUID.fromString(&quot;00001101-0000-1000-8000-00805F9B34FB&quot;));
Log.i(&quot;服务端线程运行&quot;, &quot;蓝牙服务端线程开始&quot;);
Log.i(&quot;服务端线程运行&quot;, &quot;蓝牙服务端线程阻塞中&quot;);
mBluetoothSocket = mBluetoothServerSocket.accept();
if (mBluetoothSocket != null) {
break;
}
}
Log.i(&quot;服务端线程运行&quot;, &quot;蓝牙服务端线程&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&quot;);
mInputStream = mBluetoothSocket.getInputStream();
mOutputStream = mBluetoothSocket.getOutputStream();
byte[] data = getSocketResult(mInputStream);
String tempString = new String(data);
Log.i(&quot;蓝牙服务端监听str&quot;, tempString);
// 向客户端发送数据
if (tempString.equals(&quot;StartOnNet\n&quot;)) {
mOutputStream.write(&quot;haha&quot;.getBytes());
mOutputStream.flush();
if(!isServiceRunning(&quot;com.yqq.endClient3.service.GpsInfoCollectionService&quot;,BluethoothServer.this)){
// 开启GPS收集服务
gpsService= new Intent(BluethoothServer.this,
GpsInfoCollectionService.class);
Log.i(&quot;蓝牙服务端监听&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&quot;, &quot;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&quot;);
startService(gpsService);
}
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (mInputStream != null) {
try {
mInputStream.close();
mInputStream = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (mInputStream != null) {
try {
mOutputStream.close();
mOutputStream = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (mBluetoothSocket != null) {
try {
mBluetoothSocket.close();
mBluetoothSocket = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (mBluetoothServerSocket != null) {
try {
mBluetoothServerSocket.close();
mBluetoothServerSocket = null;
Looper.prepare();
Message message = Message.obtain();
message.what = 0x123456;
mHandler.sendMessage(message);
Looper.loop();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}</code></pre>