1 藍牙基本操作
隨着可穿戴設備的流行,研究藍牙是必不可少的一門技術了。
總結了下藍牙開發使用的一些東西分享一下。
藍牙權限
首先需要AndroidManifest.xml文件中添加操作藍牙的權限。<uses-permissionandroid:name="android.permission.BLUETOOTH" />
允許程序連接到已配對的藍牙設備。
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
允許程序發現和配對藍牙設備。
BluetoothAdapter
操作藍牙主要用到的類 BluetoothAdapter類,使用時導包
import android.bluetooth.BluetoothAdapter;
源碼具體位置frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
BluetoothAdapter 代表本地設備的藍牙適配器。該BluetoothAdapter可以執行基本的藍牙任務,例如啓
動設備發現,查詢配對的設備列表,使用已知的MAC地址實例化一個BluetoothDevice類,並創建一個
BluetoothServerSocket監聽來自其他設備的連接請求。
獲取藍牙適配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
開啓藍牙 if(!mBluetoothAdapter.isEnabled()){
//彈出對話框提示用戶是後打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
//不做提示,直接打開,不建議用下面的方法,有的手機會有問題。
// mBluetoothAdapter.enable();
}
獲取本地藍牙信息
//獲取本機藍牙名稱
String name = mBluetoothAdapter.getName();
//獲取本機藍牙地址
String address = mBluetoothAdapter.getAddress();
Log.d(TAG,"bluetooth name ="+name+" address ="+address);
//獲取已配對藍牙設備
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
Log.d(TAG, "bonded device size ="+devices.size());
for(BluetoothDevice bonddevice:devices){
Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress());
}
搜索設備
mBluetoothAdapter.startDiscovery();
停止搜索
mBluetoothAdapter.cancelDiscovery();
搜索藍牙設備,該過程是異步的,通過下面註冊廣播接受者,可以監聽是否搜到設備。
IntentFilter filter = new IntentFilter();
//發現設備
filter.addAction(BluetoothDevice.ACTION_FOUND);
//設備連接狀態改變
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//藍牙設備狀態改變
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothReceiver, filter);
監聽掃描結果
通過廣播接收者查看掃描到的藍牙設備,每掃描到一個設備,系統都會發送此廣播(BluetoothDevice.ACTION_FOUNDE)。其中參數intent可以獲取藍牙設備BluetoothDevice。
該demo中是連接指定名稱的藍牙設備,BLUETOOTH_NAME爲"Galaxy Nexus",如果掃描不到,記得改這個藍牙名稱。
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG,"mBluetoothReceiver action ="+action);
if(BluetoothDevice.ACTION_FOUND.equals(action)){//每掃描到一個設備,系統都會發送此廣播。
//獲取藍牙設備
BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(scanDevice == null || scanDevice.getName() == null) return;
Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());
//藍牙設備名稱
String name = scanDevice.getName();
if(name != null && name.equals(BLUETOOTH_NAME)){
mBluetoothAdapter.cancelDiscovery();
//取消掃描
mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting)); //連接到設備。
mBlthChatUtil.connect(scanDevice);
}
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}
}
};
設置藍牙可見性
有時候掃描不到某設備,這是因爲該設備對外不可見或者距離遠,需要設備該藍牙可見,這樣該才能被搜索到。
可見時間默認值爲120s,最多可設置300。
if (mBluetoothAdapter.isEnabled()) {
if (mBluetoothAdapter.getScanMode() !=
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
startActivity(discoverableIntent);
}
}
2 服務端
android 藍牙之間可以通過SDP協議建立連接進行通信,通信方式類似於平常使用socket。
首先創建BluetoothServerSocket ,BluetoothAdapter中提供了兩種創建BluetoothServerSocket 方式,如下圖所示爲創建安全的RFCOMM Bluetooth socket,該連接是安全的需要進行配對。而通過listenUsingInsecureRfcommWithServiceRecord創建的RFCOMM Bluetooth socket是不安全的,連接時不需要進行配對。
其中的uuid需要服務器端和客戶端進行統一。
private class AcceptThread extends Thread {
// 本地服務器套接字
private final BluetoothServerSocket mServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
// 創建一個新的偵聽服務器套接字
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(
SERVICE_NAME, SERVICE_UUID);
//tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);
} catch (IOException e) {
Log.e(TAG, "listen() failed", e);
}
mServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// 循環,直到連接成功
while (mState != STATE_CONNECTED) {
try {
// 這是一個阻塞調用 返回成功的連接
// mServerSocket.close()在另一個線程中調用,可以中止該阻塞
socket = mServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "accept() failed", e);
break;
}
// 如果連接被接受
if (socket != null) {
synchronized (BluetoothChatUtil.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// 正常情況。啓動ConnectedThread。
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// 沒有準備或已連接。新連接終止。
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
if (D) Log.i(TAG, "END mAcceptThread");
}
public void cancel() {
if (D) Log.d(TAG, "cancel " + this);
try {
mServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of server failed", e);
}
}
}
mServerSocket通過accept()等待客戶端的連接(阻塞),直到連接成功或失敗。3 客戶端
客戶端主要用來創建RFCOMM socket,並連接服務端。
先掃描周圍的藍牙設備,如果掃描到指定設備則進行連接。mBlthChatUtil.connect(scanDevice)連接到設備,
連接過程主要在ConnectThread線程中進行,先創建socket,方式有兩種,
如下代碼中是安全的(createRfcommSocketToServiceRecord)。另一種不安全連接對應的函數是createInsecureRfcommSocketToServiceRecord。
private class ConnectThread extends Thread {
private BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// 得到一個bluetoothsocket
try {
mmSocket = device.createRfcommSocketToServiceRecord
(SERVICE_UUID);
} catch (IOException e) {
Log.e(TAG, "create() failed", e);
mmSocket = null;
}
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread");
try {
// socket 連接,該調用會阻塞,直到連接成功或失敗
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
try {//關閉這個socket
mmSocket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
return;
}
// 啓動連接線程
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
接着客戶端socket主動連接服務端。連接過程中會自動進行配對,需要雙方同意纔可以連接成功。4 數據傳輸
客戶端與服務端連接成功後都會調用connected(mmSocket, mmDevice),創建一個ConnectedThread線程()。
該線程主要用來接收和發送數據。客戶端和服務端處理方式一樣。該線程通過socket獲得輸入輸出流。
private InputStream mmInStream = socket.getInputStream();
private OutputStream mmOutStream =socket.getOutputStream();
發送數據
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// 分享發送的信息到Activity
mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
接收數據線程循環進行接收數據。
public void run() {
// 監聽輸入流
while (true) {
try {
byte[] buffer = new byte[1024];
// 讀取輸入流
int bytes = mmInStream.read(buffer);
// 發送獲得的字節的ui activity
Message msg = mHandler.obtainMessage(MESSAGE_READ);
Bundle bundle = new Bundle();
bundle.putByteArray(READ_MSG, buffer);
msg.setData(bundle);
mHandler.sendMessage(msg);
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
demo下載:http://www.demodashi.com/demo/10676.html
歡迎掃一掃關注我的微信公衆號,定期推送優質技術文章: