转载需说明出处: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);
看到这个转换方式,熟悉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