您的位置:首页 > 移动开发

简单梳理android蓝牙配对通讯、app层实现蓝牙静默配对、ClsUtils类

2016-08-11 18:36 495 查看
最近做蓝牙通讯,需要app层静默连接通讯,记录近几天的心酸摸索。

1.搜索蓝牙设备

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {//不支持蓝牙或者蓝牙不可用
Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
} else {
if (!bluetoothAdapter.isDiscovering()) bluetoothAdapter.startDiscovery();
}


蓝牙发现新设备会发出广播:BluetoothDevice.ACTION_FOUND

蓝牙搜索结束会发出广播:BluetoothAdapter.ACTION_DISCOVERY_FINISHED

//自定义蓝牙搜索状态广播接收器,找到设备后更新自己的列表

private class BlueDeviceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_FOUND)) {//发现新设备
BluetoothDevice btd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (!deviceList.contains(btd.getName() + "\n" + btd.getAddress())) {
deviceList.add(btd.getName() + "\n" + btd.getAddress());
deviceAdapter.notifyDataSetChanged();
}
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
//搜索结束
}
}
}
}


注意添加权限:尖括号输入不了,囧。

uses-permission android:name=”android.permission.BLUETOOTH”

uses-permission android:name=”android.permission.BLUETOOTH_ADMIN”

如果是android6.0系统需要额外添加权限,否则无法搜索设备:

uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”

uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”

2.发起配对请求

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!bluetoothAdapter.checkBluetoothAddress(deviceAddress)) {//检查是否是有效的蓝牙地址
Toast.makeText(LauncherDemoActivity.this, "蓝牙设备地址无效", Toast.LENGTH_SHORT).show();
return false;
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
if (device != null) {
Boolean returnValue = null;
try {
returnValue = ClsUtils.createBond(BluetoothDevice.class, device);
} catch (Exception e) {
e.printStackTrace();
}
if (returnValue) {//发起配对成功,并不代表配对成功,因为可能被拒绝
return true;
}
}


3.自定义蓝牙配对状态广播接收器,当有自己想要的设备配对时,就可以进行下一步socket通讯了

class MyActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
BluetoothDevice.ERROR);
if (bondState == BluetoothDevice.BOND_BONDED) {//有新的配对设备
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices();
BluetoothDevice device1 = null;
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
if(device.getAddress().equals("55:44:33:22:11:00")) {//如果连接的设备中有特定的设备则发起socket连接请求
device1 = device;
break;
}
}
}
if(device1!=null) {//发起socket连接请求
try2ConnectDevice(device1);
}
}
}
}
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothSocket mmSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
this.device = device;
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
return;
}    new CommunicateThread(mmSocket).start();
// 其中CommunicateThread,即和服务器交互线程如下:
//客户端交互线程
class CommnicateThread extends Thread {
private final BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
private DataOutputStream dataOutputStream;
private DataInputStream dataInputStream;
public CommnicateThread(BluetoothSocket socket) {
mmSocket = socket;
// Get the BluetoothSocket input and output streams
try {
mmInStream = mmSocket.getInputStream();
mmOutStream = mmSocket.getOutputStream();
dataOutputStream = new DataOutputStream(mmOutStream);
dataInputStream = new DataInputStream(mmInStream);
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
}
public void run() {
// Keep listening to the InputStream while connected
while (true) {
try {
int type = dataInputStream.readByte();
//                    System.out.println(type);
} catch (IOException e) {
e.printStackTrace();
cancel();
connectionLost();
break;
}
}
}
//发送一个比特数组
public void write(byte[] buffer) {
try {
dataOutputStream.write(buffer);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
//发送一个int整型数据
public void writeInt(int i) {
try {
dataOutputStream.writeInt(i);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
//发送一个byte
public void writeByte(byte b) {
try {
dataOutputStream.writeByte(b);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
if (mmSocket != null)
mmSocket.close();
if(dataInputStream!=null)dataInputStream.close();
if(dataOutputStream!=null)dataOutputStream.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}


4.服务器等待socket接入的代码,UUID需要和客户端一直,且是固定的貌似

BluetoothServerSocket bluetoothServerSocket;
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter==null){
Log.d("BluetoothServerService","设备不支持蓝牙");
return;
}
try {
bluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord("ServerListener", MY_UUID);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 阻塞式等待接入线程
*/

class AcceptThread extends Thread {
@Override
public void run() {
setName("acceptThread");
BluetoothSocket socket = null;
if (bluetoothServerSocket == null) return;
setState(DISCONNECT);
// Listen to the server socket if we're not connected
while (mState != CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = bluetoothServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "accept() failed", e);
break;
}

// If a connection was accepted
if (socket != null) {
setState(CONNECTED);
commnicateThread = new CommnicateThread(socket);
commnicateThread.start();
}
}
}
public void cancel() {
try {
if (null != bluetoothServerSocket) {
bluetoothServerSocket.close();
}
} catch (IOException e) {
Log.e(TAG, "close() of server failed", e);
}
}
}
//服务器端的交互线程
class CommnicateThread extends Thread {
private final BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
private DataInputStream dataInputStream;
private DataOutputStream dataOutputStream;
public CommnicateThread(BluetoothSocket socket) {
mmSocket = socket;

// Get the BluetoothSocket input and output streams
try {
mmInStream = mmSocket.getInputStream();
mmOutStream = mmSocket.getOutputStream();
dataInputStream = new DataInputStream(mmInStream);
dataOutputStream = new DataOutputStream(mmOutStream);
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}

}

public void run() {

// Keep listening to the InputStream while connected
while (true) {
try {
int type = dataInputStream.readByte();
if (type == 1) {
short length = dataInputStream.readShort();
int what = dataInputStream.readByte();
System.out.println("1号类型:指令");
sendOrderBroadcast(what);
} else if (type == 2) {
System.out.println("2号类型:javabean");
byte[] data;
int flag;
synchronized (this) {
flag = dataInputStream.readInt();
int length = dataInputStream.readInt();
data = new byte[length];
dataInputStream.readFully(data);
}
System.out.println(data.length);
InstantNotificationItem i = (InstantNotificationItem) ByteUtil.toObject(data);
if (i == null) System.out.println("null");
else {
sendNotificationChangeBroadcast(i, flag);
}
}
writeAbyte((byte) 1);//回执给客户端的字符,暂时没有使用

} catch (IOException e) {
Log.e(TAG, "连接中断", e);
cancel();
connectionLost();
break;
}
}
}

/**
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void writeAbyte(byte what){

try {
dataOutputStream.writeByte(what);
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancel() {
try {
if (mmSocket != null)
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}


**

附:自定义蓝牙配对弹框、自动连接

**

公司要求在app层自定义蓝牙配对弹框,自动连接,网上搜了半天,没有很好的代码,今天把自己写的代码分享出来。话不多说,原理很简单,就是那个配对和取消的弹框是通过广播弹出来的,我们需要做的就是,自定义广播将其拦截,做自己的逻辑判断就可以了。下边是自动匹配所有蓝牙请求的代码,记得设置最大优先级哦,将IntentFilter的优先级或者静态注册的话android:priority=”2147483647”。自定义透明activity当做弹出框,点击某一个按钮的时候再device.setPairingConfirmation(true),简单吧。

public class BluetoothConnectActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
try {
// 广播接收者的优先者为最高 收到广播后 停止广播向下传递
// TODO: 16/8/9 判断device是否是自己的设备,是的话拦截,否则不处理。
device.setPairingConfirmation(true);
abortBroadcast();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}


**

附录2:使用反射调用私有方法的ClsUtils.java文件

**

import android.bluetooth.BluetoothDevice;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClsUtils {

/**
* 与设备配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean createBond(Class btClass, BluetoothDevice btDevice)
throws Exception {

Method createBondMethod = btClass.getMethod("createBond");
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 btClass, BluetoothDevice btDevice)
throws Exception {

Method removeBondMethod = btClass.getMethod("removeBond");
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("setPin",
new Class[]
{
byte[].class
});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
new Object[]
{
str.getBytes()
});
Log.e("returnValue", "" + returnValue);

} 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 true;

}

static public boolean setPassKey(Class btClass, BluetoothDevice btDevice,
String str) throws Exception {

try {

Method removeBondMethod = btClass.getDeclaredMethod("setPasskey",
new Class[]
{
byte[].class
});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
new Object[]
{
str.getBytes()
});
Log.e("returnValue", "" + returnValue);

} 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 true;

}

// 取消用户输入
static public boolean cancelPairingUserInput(Class btClass,
BluetoothDevice device)

throws Exception {

Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
// cancelBondProcess()
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();

}

// 取消配对
static public boolean cancelBondProcess(Class btClass,
BluetoothDevice device)

throws Exception {

Method createBondMethod = btClass.getMethod("cancelBondProcess");
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();

}

/**
*
* @param clsShow
*/
static public void printAllInform(Class clsShow) {

try {

// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i < hideMethod.length; i++) {

Log.e("method name", hideMethod[i].getName() + ";and the i is:"
+ i);

}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++) {

Log.e("Field name", 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();

}

}

public static boolean isWantedMac(String macStr) {
if (macStr == null || macStr.isEmpty()) return false;
if (macStr.equals("55:44:33:22:11:00")) {
return true;
} else return false;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: