由于最近项目需要连接蓝牙,类似于智能手环连接蓝牙模块,网上找了些教程,有一些零零散散的程序,自己于是也写了一个能用,然后我发现谷歌有一个蓝牙的sample,一对比突然发现自己写的好low,不严谨。于是就直接 谷歌 例程,修改了部分。首先当初写的时候我有以下几个疑问。
1.如何实现又蓝牙又能接受数据,又能发送数据,这样不会冲突嘛?
2.UUID是啥,蓝牙模块的uuid又是啥?
3.网上程序有客户端又有服务器的蓝牙程序,那我app用的是哪个程序那,蓝牙模块那边不是app不能写程序又怎样?
4.app不断接受数据,我这边怎么判断为一整段数据,并拿来解析。因为在inputSteam.read()这里如果蓝牙没有数据收到的话会出现阻塞的情况,并且一段数据可能多次读取,并不会一次读取完
答案在文章底部揭晓,想直接看答案直接拉到最后。有耐心的可以先看下文,首先我们新建一个BluetoothService类,看看构造器先
public BluetoothChatService(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;//蓝牙状态
mHandler = handler;//用来更新ui
}
上面蓝牙状态分为3种,分别用来判断当前状态
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
接下来就是connect()方法了。也就是启动连接
/**
* Start the ConnectThread to initiate a connection to a remote device.
*
* @param device The BluetoothDevice to connect
* @param secure Socket Security type - Secure (true) , Insecure (false)
*/
public synchronized void connect(BluetoothDevice device, boolean secure) {
// 取消之前线程,假设之前有打开过线程
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to connect with the given device
mConnectThread = new ConnectThread(device, secure);//连接线程
mConnectThread.start();线程启动
setState(STATE_CONNECTING);//设置线程为正在连接
progressDialog.show();//弹出进度dialog
}
接下来就是看看connectTread里面写了什么
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;//连接必须要有device这个对象
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// 拿到socket,
//这里uuid有分安全模式和非安全模式之分,这里目前把他设置成一样
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
}
}
这里首先构造器拿到Device和secure这两个参数,一个参数是用来获得socket,这样只会才能启动连接,secure来判断连接模式是安全非安全模式。这里如果mmSocket.connect();这个方法没有抛出任何异常的话就可以说明连接成功,接下来调用connected这个方法。这个方法属于BluetoothChatService这个类。
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "connected, Socket Type:" + socketType);
progressDialog.dismiss();//连接成功取消进度框
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_CONNECTED);
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// 发送已经连接成功的设备名字返回ui线程,即通过ui一开始构造器传进来的mHandler来处理
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
}
那么连接成功后我们应该做什么呢,当然是监听数据啦,这又需要另外开启另一个线程ConnectedThread这个线程
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.d(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
StringBuilder sb=new StringBuilder();
int bytes;
Log.d(TAG,""+mState);
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
Log.d("message",new String(buffer,0,bytes));
sb.append(new String(buffer,0,bytes));
if(sb.charAt(sb.length()-1)=='$'){
sb.deleteCharAt(sb.length()-1);
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes-1, -1,sb.toString())
.sendToTarget();
sb.delete(0,sb.length());
}
// Send the obtained bytes to the UI Activity
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
}
}
}
/**
* Write to the connected OutStream.
*
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
哈哈, 大功告成,此时你发现蓝牙模块指示灯从快闪到慢闪,那么现在还缺少什么呢,当然除了接受数据还需要向蓝牙模块发送数据,因为发送数据并不会出现线程阻塞的情况,
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
这个方法属于BluetoothChatService这个类,只需要这个类实例调用下write就可以了。
这里主要是蓝牙作为客户端的代码,可以看到Google代码对各种异常捕捉考虑情况是很全的。
问题答案:1.当然不会冲突,因为接受数据是在另一个线程。不会产生任何干扰。
2.它是一个唯一的表示符号,打个比喻就像是时间和地点一样总是不能重复的,时间过了就再也没有那个时间了,我估计uuid匹配是部分匹配就能用。蓝牙模块的话我百度到的是
3.我们直接用客户端的程序,蓝牙模块那边可能本身就有主从模式可以设置,与之对应,估摸着蓝牙模块那边已经集成好了
private static final UUID MY_UUID_SECURE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
4.可以约定个协议,比如你的数据以什么结尾,读到那个结尾就拼接之前所有数据,并发送。可以看博文中ConnectedTread那里
源码地址
http://download.csdn.net/detail/sinat_28676875/9624338