从源码角度分析android蓝牙设备如何互联()

要须心地收汗马,孔孟行世目杲杲。这篇文章主要讲述从源码角度分析android蓝牙设备如何互联?相关的知识,希望能为你提供帮助。
转载需说明出处:http://blog.csdn.net/andywuchuanlong/article/details/51509229
最近公司需要用到专门的蓝牙设备去连接机器人,由于之前也没有接触过蓝牙,所以就在网上搜寻大把的资料,到最后还是没有什么所获,基本上所有的代码都是用不了的,蓝牙始终是连接不成功。但幸好的是android系统中的setting就附带了蓝牙连接的功能,所以研究下setting还是阔以的。
从android3.0开始,蓝牙的api就提供了对蓝牙profile的支持,比如a2dp profile用来在蓝牙设备之间实现高质量的声音传输,inputDevice profile实现蓝牙输入设备功能,pan profile实现蓝牙个人局域网功能,health profile用来与支持蓝牙健康设备进行通信等。这些profile在原生的setting源代码中已经定义好了。

// android.bluetooth.BluetoothProfile.java public interface BluetoothProfile { ..... /** * Headset and Handsfree profile */ public static final int HEADSET = 1; /** * A2DP profile. */ public static final int A2DP = 2; /** * Health Profile */ public static final int HEALTH = 3; /** * Input Device Profile * @hide */ public static final int INPUT_DEVICE = 4; /** * PAN Profile * @hide */ public static final int PAN = 5; /** * PBAP * @hide */ public static final int PBAP = 6; /** * GATT */ static public final int GATT = 7; /** * GATT_SERVER */ static public final int GATT_SERVER = 8; /** * MAP Profile * @hide */ ..... }

网上的大多数资料都是说利用BluetoothServerSocket 来连接的,但是我发现我们的设备压根就不起作用,总是出现socket连接超时。所以只能从setting源码去入手,我们的这个蓝牙设备是充当着一个输入设备角色,所以就必须要去看inputDevice profile是如何实现连接的功能了。下面就具体介绍一个输入类型的蓝牙设备如何连接指定的设备。BluetoothInputDevice 的核心代码如下,
// android.bluetooth.BluetoothInputDevice.java public final class BluetoothInputDevice implements BluetoothProfile {// 用来接收蓝牙连状态的广播action,连接中、连接成功、断开连接等状态 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; // 获取蓝牙输入设备服务,用来操作连接、断开、发送数据等操作 private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothInputDevice.Stub.asInterface(service); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); } } public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE); } } };// 连接设备 public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mService != null & & isEnabled() & & isValidDevice(device)) { // 如果service不为空,并且蓝牙是打开的,并且要连接的设备是有效的设备 try { return mService.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; }//断开设备 public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null & & isEnabled() & & isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; }// 获取已经连接成功的设备列表 public List< BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mService != null & & isEnabled()) { try { return mService.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList< BluetoothDevice> (); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList< BluetoothDevice> (); }// 获取某个蓝牙设备的连接状态 public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null & & isEnabled() & & isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; }// 发送指定的数据到指定的蓝牙设备 public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); if (mService != null & & isEnabled() & & isValidDevice(device)) { try { return mService.sendData(device, report); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } }

蓝牙连接前如何判断某个设备是否是有效的呢?那肯定是判断mac地址啦。如下:
private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; // 这里便是检查mac地址的有效性 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; return false; }

// 校验蓝牙设备的地址, 例如 00:43:A8:23:10:F0 public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { return false; } for (int i = 0; i < ADDRESS_LENGTH; i++) { char c = address.charAt(i); switch (i % 3) { case 0: case 1: if ((c > = ‘0‘ & & c < = ‘9‘) || (c > = ‘A‘ & & c < = ‘F‘)) { // hex character, OK break; } return false; case 2: if (c == ‘:‘) { break; // OK } return false; } } return true; }

蓝牙的连接、断开、发送数据等操作均要依赖这个mService来进行操作,那么mService是什么东东?
这个mService是在ServiceConnection 的onServiceConnected回调方法中获取的:
mService = IBluetoothInputDevice.Stub.asInterface(service);

【从源码角度分析android蓝牙设备如何互联()】看到这个转换方式,熟悉aidl的同学们肯定知道这个是啥意思啦。所以只要mConnection连接成功,就会返回一个IBluetoothInputDevice代理对象。在BluetoothInputDevice构造方法中:
BluetoothInputDevice(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { Log.e(TAG,"",e); } } // dobind,顾名思义肯定是bind某个东西 doBind(); }

boolean doBind() { Intent intent = new Intent(IBluetoothInputDevice.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindService(intent, mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); return false; } return true; }

doBind方法中绑定了service,那么这个service绑定之后,就会回调mConnection的onServiceConnected,至此我们已经知道这个操纵蓝牙连接和断开的mService是如何获得的了。
不过还别慌,还有个东西没有介绍,那就是这个onServiceConnected方法中的mServiceListener 是啥东东?这个mServiceListener是通过构造方法中传入的,它通过自身的onServiceConnected方法将
BluetoothInputDevice.this传递给了他的构建者。
if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); }

下面就看下是谁实例化了这个BluetoothInputDevice。莫非是 BluetoothAdapter ? BluetoothAdapter 是所有蓝牙对象交互的入口,其本身提供了蓝牙相关一系列的api,如提供profile的调用、蓝牙设备的扫描等。其中有个重要的方法:
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,int profile) { if (context == null || listener == null) return false; if (profile == BluetoothProfile.HEADSET) { BluetoothHeadset headset = new BluetoothHeadset(context, listener); return true; } else if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); return true; } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; } else { return false; } }

原来BluetoothInputDevice是在这里实例化的,所以我们就可以通过这个api来实例化一个BluetoothInputDevice,然后实例化一个ServiceListener 获取BluetoothInputDevice的代理对象,下面是实现代码:
private void initProfile(){ try { mLocalAdapter.getProfileProxy(mContext, new android.bluetooth.BluetoothProfile.ServiceListener() { @Override public void onServiceDisconnected(int profile) { Log.e(TAG, "onServiceDisconnected : "+profile); removeBound(); } @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { Log.e(TAG, "onServiceConnected : "+profile); hidInputService = proxy; } },PROFILE_HID); } catch (Exception e) { Log.e(TAG, "createDevice connect 1: "+e.getLocalizedMessage()); } }

获得到BluetoothInputDevice代理对象后就好办啦,可以开始连接等操作了。但是首先还是得进行蓝牙设备的扫描、发现设备、再配对。
下面是相关的代码,同学可以自行研究:
private void createBond() { try { if (mBluetoothDevice != null & & deviceName.equals(mBluetoothDevice.getName())) { boolean createBond = mBluetoothDevice.createBond(); Log.e(TAG, "createBond result" + createBond); } } catch (Exception e) { } }private void connect() { setPriority(); try { Method connect = hidInputService.getClass().getDeclaredMethod("connect", BluetoothDevice.class); connect.setAccessible(true); Boolean invoke = (Boolean) connect.invoke(hidInputService, mBluetoothDevice); Log.e(TAG, "connect: " + invoke); } catch (Exception e) { Log.e(TAG, "connect: " + e.getLocalizedMessage()); } }public void disconnectDevice(final String deviceName){ List< BluetoothDevice> connectedDevices = hidInputService.getConnectedDevices(); for(BluetoothDevice bluetoothDevice : connectedDevices){ if(bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED){ mBluetoothDevice = bluetoothDevice; Log.e(TAG, "disconnecting"); removeBound(); disConnect(); closeProfile(); } } }private void disConnect() { try { Method connect = hidInputService.getClass().getDeclaredMethod("disconnect", BluetoothDevice.class); connect.setAccessible(true); Boolean invoke = (Boolean) connect.invoke(hidInputService, mBluetoothDevice); Log.e(TAG, " disconnect: " + invoke); } catch (Exception e) { Log.e(TAG, " disconnect: " + e.getLocalizedMessage()); } }

同学如果研究到这里,说明你对这个蓝牙连接有一定的了解啦,下片文章继续带你研究蓝牙相关的源代码以及其中用到的设计模式!
蓝牙相关连接代码下载地址:http://download.csdn.net/detail/andywuchuanlong/9639446
转载需说明出处:http://blog.csdn.net/andywuchuanlong/article/details/51509229




    推荐阅读