android 連線藍芽模組教程附原始碼
由於最近專案需要連線藍芽,類似於智慧手環連線藍芽模組,網上找了些教程,有一些零零散散的程式,自己於是也寫了一個能用,然後我發現谷歌有一個藍芽的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那裡