2017.10.20 以前参加一个大三学长的创业项目,作一个智能的车锁App,用到嵌入式等技术,App须要蓝牙、实时位置等技术,故查了几篇相关技术文章,以此参考!
//先说说如何开启蓝牙设备和设置可见时间:
private void search() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (!adapter.isEnabled()) {
adapter.enable();
}
Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600为蓝牙设备可见时间
startActivity(enable);
Intent searchIntent = new Intent(this, ComminuteActivity.class);
startActivity(searchIntent);
}
首先,须要得到一个BluetoothAdapter,能够经过getDefaultAdapter()得到系统默认的蓝牙适配器,固然咱们也能够本身指定,但这个真心没有必要,至少我是不须要的。而后咱们检查手机的蓝牙是否打开,若是没有,经过enable()方法打开。接着咱们再设置手机蓝牙设备的可见,可见时间能够自定义。
完成这些必要的设置后,咱们就能够正式开始与蓝牙模块进行通讯了:html
public class ComminuteActivity extends Activity {
private BluetoothReceiver receiver;
private BluetoothAdapter bluetoothAdapter;
private List<String> devices;
private List<BluetoothDevice> deviceList;
private Bluetooth client;
private final String lockName = "BOLUTEK";
private String message = "000001";
private ListView listView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_layout);
listView = (ListView) this.findViewById(R.id.list);
deviceList = new ArrayList<BluetoothDevice>();
devices = new ArrayList<String>();
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter.startDiscovery();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
setContentView(R.layout.connect_layout);
BluetoothDevice device = deviceList.get(position);
client = new Bluetooth(device, handler);
try {
client.connect(message);
} catch (Exception e) {
Log.e("TAG", e.toString());
}
}
});
}
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Bluetooth.CONNECT_FAILED:
Toast.makeText(ComminuteActivity.this, "链接失败", Toast.LENGTH_LONG).show();
try {
client.connect(message);
} catch (Exception e) {
Log.e("TAG", e.toString());
}
break;
case Bluetooth.CONNECT_SUCCESS:
Toast.makeText(ComminuteActivity.this, "链接成功", Toast.LENGTH_LONG).show();
break;
case Bluetooth.READ_FAILED:
Toast.makeText(ComminuteActivity.this, "读取失败", Toast.LENGTH_LONG).show();
break;
case Bluetooth.WRITE_FAILED:
Toast.makeText(ComminuteActivity.this, "写入失败", Toast.LENGTH_LONG).show();
break;
case Bluetooth.DATA:
Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show();
break;
}
}
};
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isLock(device)) {
devices.add(device.getName());
}
deviceList.add(device);
}
showDevices();
}
}
private boolean isLock(BluetoothDevice device) {
boolean isLockName = (device.getName()).equals(lockName);
boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
return isLockName && isSingleDevice;
}
private void showDevices() {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
devices);
listView.setAdapter(adapter);
}
}
要想与任何蓝牙模块进行通讯,首先得搜到该设备:java
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isLock(device)) {
devices.add(device.getName());
}
deviceList.add(device);
}
showDevices();
}
}
在这以前,咱们得先调用一个方法:android
bluetoothAdapter.startDiscovery();
startDiscovery()方法是一个异步方法,它会对其余蓝牙设备进行搜索,持续时间为12秒。搜索过程实际上是在System Service中进行,咱们能够经过cancelDiscovery()方法来中止这个搜索。在系统搜索蓝牙设备的过程当中,系统可能会发送如下三个广播:ACTION_DISCOVERY_START(开始搜索),ACTION_DISCOVERY_FINISHED(搜索结束)和ACTION_FOUND(找到设备)。ACTION_FOUND这个才是咱们想要的,这个Intent中包含两个extra fields:EXTRA_DEVICE和EXTRA_CLASS,包含的分别是BluetoothDevice和BluetoothClass,BluetoothDevice中的EXTRA_DEVICE就是咱们搜索到的设备对象。 确认搜索到设备后,咱们能够从获得的BluetoothDevice对象中得到设备的名称和地址。git
在android中使用广播须要咱们注册,这里也不例外:程序员
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
广播注册后须要咱们撤销,这个能够放在这里进行:github
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
这样在Activity结束的时候就会自动撤销该广播,而不须要咱们手动执行。
我这里使用一个ListView来显示搜索到的蓝牙设备,但由于须要只限定一个蓝牙设备,因此这里进行了检查,检查该设备是不是咱们的目标设备,若是是,就添加。固然,为了防止重复添加,有必要增长这么一句:编程
boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
搜索到该设备后,咱们就要对该设备进行链接。服务器
public void connect(final String message) {
Thread thread = new Thread(new Runnable() {
public void run() {
BluetoothSocket tmp = null;
Method method;
try {
method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
tmp = (BluetoothSocket) method.invoke(device, 1);
} catch (Exception e) {
setState(CONNECT_FAILED);
Log.e("TAG", e.toString());
}
socket = tmp;
try {
socket.connect();
isConnect = true;
} catch (Exception e) {
setState(CONNECT_FAILED);
Log.e("TAG", e.toString());
}
链接设备以前须要UUID,所谓的UUID,就是用来进行配对的,全称是Universally Unique Identifier,是一个128位的字符串ID,用于进行惟一标识。网上的例子,包括谷歌的例子,它们的UUID都是说能用可是我用不了的,都会报出这样的错误:异步
Service discovery failed socket
缘由多是做为惟一标识的UUID没有发挥做用,因此,我就利用反射的原理,让设备本身提供UUID。
这个错误在咱们把手机既当作客户端有当作服务端的时候,一样也有可能出现,由于做为服务器的时候,咱们须要的也是同一个UUID:
mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
做为客户端是这样的:
device.createRfcommSocketToServiceRecord(MY_UUID);
当两个UUID想同时创建Rfcomm的通道时,咱们的选择都是在两个线程中分别实现,可是忽略了一件最重要的事情:同一个时间只能充当一个角色!因此,解决这个问题的方法就是在咱们相链接的设备上也安装一样的应用程序,谁先发起链接谁就是客户端,但我这里是蓝牙模块啊!!怎么能安装个人应用程序呢!!解决办法就在下面的通讯中。
链接设备以前还有一件事必须确保:
bluetoothAdapter.cancelDiscovery();
这是为了停掉搜索设备,不然链接可能会变得很是慢而且容易失败。
有关于Socket的编程都须要咱们设置一些状态值来标识通讯的状态,以方便咱们调错,并且链接应该放在一个线程中进行,要让该线程与咱们程序的主线程进行通讯,咱们须要使用Handle,关于Handle的使用,能够参考个人另外一篇博客http://www.cnblogs.com/wenjiang/p/3180324.html,这里很少讲。
在使用Socket中,我注意到一个方法:isConnect(),它返回的是布尔值,可是根本就不须要使用到这个方法,Socket的链接若是没有报错,说明是已经链接上了。
在谷歌提供的例子中,咱们能够看到谷歌的程序员的程序水平很高,一些好的编码习惯咱们能够学习一下,像是在try..catch中才定义的变量,咱们应该在try...catch以前声明一个临时变量,而后再在try...catch后赋值给咱们真正要使用的变量。这种作法的好处就是:若是咱们直接就是使用真正的变量,当出现异常的时候,该变量的使用就会出现问题,并且很难进行排查,若是是临时变量,我么能够经过检查变量的值来肯定是不是赋值时出错。
谷歌的例子中最大的感想就是满满的异常检查,但也是由于这个,致使它的可读性不高。java的异常处理机制有时候对于代码的阅读真的不是一件舒服的事情,能避免就尽可能避免。
若是链接没有问题,咱们就能够和蓝牙模块进行通讯:
if (isConnect) {
try {
OutputStream outStream = socket.getOutputStream();
outStream.write(getHexBytes(message));
} catch (IOException e) {
setState(WRITE_FAILED);
Log.e("TAG", e.toString());
}
try {
InputStream inputStream = socket.getInputStream();
int data;
while (true) {
try {
data = inputStream.read();
Message msg = handler.obtainMessage();
msg.what = DATA;
msg.arg1 = data;
handler.sendMessage(msg);
} catch (IOException e) {
setState(READ_FAILED);
Log.e("TAG", e.toString());
break;
}
}
} catch (IOException e) {
setState(WRITE_FAILED);
Log.e("TAG", e.toString());
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
Log.e("TAG", e.toString());
}
}
}
}
这里包括写入和读取,用法和基本的Socket是同样的,可是写入的时候,须要将字符串转化为16进制:
private byte[] getHexBytes(String message) {
int len = message.length() / 2;
char[] chars = message.toCharArray();
String[] hexStr = new String[len];
byte[] bytes = new byte[len];
for (int i = 0, j = 0; j < len; i += 2, j++) {
hexStr[j] = "" + chars[i] + chars[i + 1];
bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
}
return bytes;
}
固然,这里只是将手机当作客户端,可是接收蓝牙模块发送过来的信息是没有必要特地建立服务端的,咱们只要一个不断监听并读取对方消息的循环就行。
很简单的程序就能实现像是蓝牙串口助手的功能,因为是项目的代码,不能贴完整的代码,可是基本上都在上面了,你们能够参考一下。要想使用蓝牙,相应的权限也是必不可少的:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
这篇博客出自:http://www.cnblogs.com/wenjiang/p/3200138.html
项目能够借鉴,可是打开项目稍微有些问题
代码放在github上:https://github.com/wenjiang/Bluetooth2.git
在此基础上,能够用Android studio 来尝试修改代码创建蓝牙通讯!
后续项目建成上传至github上。