前几天又完成了一个安卓端传统蓝牙串口通信小工具
因为写博客的时候,蓝牙硬件已经寄回给客户了,所以无法截屏,只能看下面调试过程中拍的照片了。
因为好久没做安卓开发了,开发过程中还是查了些资料的,其中有:自定义actionbar,recyclerview,eventbus,swiperefreshlayout的使用,当然还有最重要的蓝牙通信相关API。
部分关键代码如下,DeviceCommandManager.java(蓝牙连接及命令发送):
package com.meiyuan.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.text.TextUtils;
import android.util.Log;
import com.meiyuan.bluetooth.listener.BluetoothConnectCallback;
import com.meiyuan.bluetooth.listener.DeviceMessageReader;
import com.meiyuan.bluetooth.model.MessageWrap;
import org.greenrobot.eventbus.EventBus;
import java.io.IOException;
public class DeviceCommandManager implements BluetoothConnectCallback {
private String TAG="cmd";
private BluetoothSocket socket;
private BluetoothDevice device;
private String lastCommand;
private int state=0;//0是未连接 1正在链接 2已连接
private static DeviceCommandManager instance = new DeviceCommandManager();
private BluetoothConnectThread currentThread;
private DeviceCommandManager() {
}
public static DeviceCommandManager getInstance() {
return instance;
}
public int getState(){
return state;
}
public String getCurrentMac(){
if(getState()==2&&device!=null){
return device.getAddress();
}
return null;
}
public void ConnectDevice(BluetoothDevice device){
try {
if (getState() == 2 && this.device != null && this.device.getAddress().equals(device.getAddress())) {
MessageWrap message = new MessageWrap(MessageWrap.message_device_connect_repeat, null);
EventBus.getDefault().post(message);
return;
} else if (getState() == 2) {//当前已有链接
Log.d("cmd", "当前已有链接,先断开");
disConnect(this.device.getAddress());
try {
if (currentThread != null) {
currentThread.cancel();
if (currentThread.isAlive()) {
currentThread.interrupt();
}
}
} catch (Exception e) {
} finally {
currentThread = null;
}
}
MessageWrap message = new MessageWrap(MessageWrap.message_device_connecting, null);
EventBus.getDefault().post(message);
this.device = device;
state = 1;
currentThread = new BluetoothConnectThread(device, this);
currentThread.start();
}catch (Exception e){
MessageWrap message = new MessageWrap(MessageWrap.message_exception, e.getMessage());
EventBus.getDefault().post(message);
}
}
public void disConnect(String mac){
if(device!=null&&mac.equals(device.getAddress())) {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
socket = null;
}
}
device = null;
state = 0;
}
}
public void sendCommand(String cmd){
if(state==2) {
lastCommand = cmd;
Log.d("cmd","发送命令:"+cmd);
MessageWrap message = new MessageWrap(MessageWrap.message_wait_for_device, "请稍后...");
EventBus.getDefault().post(message);
try {
socket.getOutputStream().write(cmd.getBytes("utf-8"));
socket.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
}
}else{
MessageWrap message=new MessageWrap(MessageWrap.message_device_disconnect);
EventBus.getDefault().post(message);
Log.e("cmd","链接已断开");
}
}
public void sendLastCommand(){
sendCommand(lastCommand);
}
@Override
public void connectSuccess(BluetoothSocket socket) {
try {
this.socket=socket;
state=2;
String deviceName=device.getName();
if(TextUtils.isEmpty(deviceName)){
deviceName="N/A";
}
MessageWrap message=new MessageWrap(MessageWrap.message_device_connected,deviceName);
EventBus.getDefault().post(message);
Log.d(TAG,"蓝牙链接成功");
try {
new DeviceMessageReader(socket.getInputStream()).start();
} catch (IOException e) {
e.printStackTrace();
}
}catch (Exception ex){
MessageWrap message = new MessageWrap(MessageWrap.message_exception, ex.getMessage());
EventBus.getDefault().post(message);
}
}
@Override
public void connectFailed(String errorMsg) {
state=0;
Log.e(TAG,"蓝牙链接失败:"+errorMsg);
MessageWrap message=new MessageWrap(MessageWrap.message_device_connect_error,"设备连接失败,请检查设备是否开启并在附近?");
EventBus.getDefault().post(message);
}
@Override
public void connectCancel() {
state=0;
Log.d(TAG,"蓝牙链接关闭");
}
}
DeviceMessageReader.java(消息接收)
package com.meiyuan.bluetooth.listener;
import android.text.TextUtils;
import android.util.Log;
import com.meiyuan.bluetooth.model.MessageWrap;
import org.greenrobot.eventbus.EventBus;
import java.io.IOException;
import java.io.InputStream;
public class DeviceMessageReader extends Thread{
private InputStream inputStream;
private boolean read=true;
public DeviceMessageReader(InputStream inputStream){
this.inputStream=inputStream;
}
@Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;//定义长度变量
while (read){
try {
if ((bytes = inputStream.read(buffer)) != 0) {
byte[] buf_data = new byte[bytes];
for (int i = 0; i < bytes; i++){
buf_data[i]=buffer[i];
}
String msg=new String(buf_data);
Log.d("cmd","收到蓝牙消息:"+msg);
MessageWrap message=new MessageWrap(MessageWrap.message_device_return,msg);
EventBus.getDefault().post(message);
}
}catch (Exception e){
e.printStackTrace();
if(!TextUtils.isEmpty(e.getMessage())&&e.getMessage().indexOf("socket closed")!=-1){
read=false;
}
}
}
try {
if(inputStream!=null)
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
整个APP做起来其实还是很简单的,主要有些细节有点繁琐而已,比如要检测手机支持不支持蓝牙,蓝牙有没开启,其中有个坑,必须打开定位才能搜索附近蓝牙设备,一开始卡在这里整整一天,还以为程序哪里弄错了呢。
/**
* 判断定位服务是否开启
*
* @param
* @return true 表示开启
*/
public static boolean isLocationEnabled(Context context) {
int locationMode = 0;
String locationProviders;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
return false;
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
} else {
locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
/**
* 提示用户去开启定位服务
**/
public static void toOpenGPS(Activity activity) {
new AlertDialog.Builder(activity)
.setTitle("提示")
.setMessage("手机定位服务未开启,无法发现附近蓝牙设备,是否前往开启?")
.setNegativeButton("取消",null)
.setPositiveButton("去开启", (dialogInterface,i)-> {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
activity.startActivityForResult(intent,REQUEST_SERVICE_OPEN_LOCATION);
dialogInterface.dismiss();
}
)
.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SERVICE_OPEN_LOCATION) {
if(!isLocationEnabled(this)){
toOpenGPS(this);
}else{
checkBlueTooth();
}
}else if(requestCode == REQUEST_ENABLE_BT){
checkBlueTooth();
}
}
另外在项目进入测试阶段的时候,为防止客户拿到APP后跑路,在APP里简单加了个服务器验证机制,有兴趣的童鞋可以下载源码看下,创作不易,有偿分享。
源码下载:下载源码