原文地址:https://blog.csdn.net/VNanyesheshou/article/details/51554852
1 藍(lán)牙基本操作
隨著可穿戴設(shè)備的流行,研究藍(lán)牙是必不可少的一門技術(shù)了喘漏。
總結(jié)了下藍(lán)牙開發(fā)使用的一些東西分享一下。
藍(lán)牙權(quán)限
首先需要AndroidManifest.xml文件中添加操作藍(lán)牙的權(quán)限世分。
<uses-permissionandroid:name="android.permission.BLUETOOTH" />
允許程序連接到已配對(duì)的藍(lán)牙設(shè)備恋沃。
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
允許程序發(fā)現(xiàn)和配對(duì)藍(lán)牙設(shè)備。
BluetoothAdapter
操作藍(lán)牙主要用到的類 BluetoothAdapter類绩脆,使用時(shí)導(dǎo)包
import android.bluetooth.BluetoothAdapter;
源碼具體位置frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
BluetoothAdapter 代表本地設(shè)備的藍(lán)牙適配器边翁。該BluetoothAdapter可以執(zhí)行基本的藍(lán)牙任務(wù)翎承,例如啟
動(dòng)設(shè)備發(fā)現(xiàn),查詢配對(duì)的設(shè)備列表符匾,使用已知的MAC地址實(shí)例化一個(gè)BluetoothDevice類叨咖,并創(chuàng)建一個(gè)
BluetoothServerSocket監(jiān)聽來自其他設(shè)備的連接請(qǐng)求。
獲取藍(lán)牙適配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
開啟藍(lán)牙
if(!mBluetoothAdapter.isEnabled()){
//彈出對(duì)話框提示用戶是后打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
? ? ? //不做提示啊胶,直接打開甸各,不建議用下面的方法,有的手機(jī)會(huì)有問題焰坪。
? ? ? // mBluetoothAdapter.enable();
}
獲取本地藍(lán)牙信息
//獲取本機(jī)藍(lán)牙名稱
String name = mBluetoothAdapter.getName();
//獲取本機(jī)藍(lán)牙地址
String address = mBluetoothAdapter.getAddress();
Log.d(TAG,"bluetooth name ="+name+" address ="+address);
//獲取已配對(duì)藍(lán)牙設(shè)備
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());
}
搜索設(shè)備
mBluetoothAdapter.startDiscovery();
停止搜索
mBluetoothAdapter.cancelDiscovery();
搜索藍(lán)牙設(shè)備趣倾,該過程是異步的,通過下面注冊(cè)廣播接受者某饰,可以監(jiān)聽是否搜到設(shè)備儒恋。
IntentFilter filter = new IntentFilter();
//發(fā)現(xiàn)設(shè)備
filter.addAction(BluetoothDevice.ACTION_FOUND);
//設(shè)備連接狀態(tài)改變
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//藍(lán)牙設(shè)備狀態(tài)改變
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothReceiver, filter);
監(jiān)聽掃描結(jié)果
通過廣播接收者查看掃描到的藍(lán)牙設(shè)備,每掃描到一個(gè)設(shè)備黔漂,系統(tǒng)都會(huì)發(fā)送此廣播(BluetoothDevice.ACTION_FOUNDE)诫尽。其中參數(shù)intent可以獲取藍(lán)牙設(shè)備BluetoothDevice。
該demo中是連接指定名稱的藍(lán)牙設(shè)備炬守,BLUETOOTH_NAME為"Galaxy Nexus",如果掃描不到牧嫉,記得改這個(gè)藍(lán)牙名稱。
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)){//每掃描到一個(gè)設(shè)備减途,系統(tǒng)都會(huì)發(fā)送此廣播酣藻。
//獲取藍(lán)牙設(shè)備
BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(scanDevice == null || scanDevice.getName() == null) return;
Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());
//藍(lán)牙設(shè)備名稱
String name = scanDevice.getName();
if(name != null && name.equals(BLUETOOTH_NAME)){
mBluetoothAdapter.cancelDiscovery();
//取消掃描
mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting)); //連接到設(shè)備。
mBlthChatUtil.connect(scanDevice);
}
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}
}
};
設(shè)置藍(lán)牙可見性
有時(shí)候掃描不到某設(shè)備鳍置,這是因?yàn)樵撛O(shè)備對(duì)外不可見或者距離遠(yuǎn)辽剧,需要設(shè)備該藍(lán)牙可見,這樣該才能被搜索到税产。
可見時(shí)間默認(rèn)值為120s怕轿,最多可設(shè)置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 服務(wù)端
android 藍(lán)牙之間可以通過SDP協(xié)議建立連接進(jìn)行通信,通信方式類似于平常使用socket撤卢。
首先創(chuàng)建BluetoothServerSocket ,BluetoothAdapter中提供了兩種創(chuàng)建BluetoothServerSocket 方式梧兼,如下圖所示為創(chuàng)建安全的RFCOMM Bluetooth socket放吩,該連接是安全的需要進(jìn)行配對(duì)。而通過listenUsingInsecureRfcommWithServiceRecord創(chuàng)建的RFCOMM Bluetooth socket是不安全的羽杰,連接時(shí)不需要進(jìn)行配對(duì)渡紫。
其中的uuid需要服務(wù)器端和客戶端進(jìn)行統(tǒng)一。
private class AcceptThread extends Thread {
? ? ? ? // 本地服務(wù)器套接字
? ? ? ? private final BluetoothServerSocket mServerSocket;
? ? ? ? public AcceptThread() {? ? ?
? ? ? ? ? ? BluetoothServerSocket tmp = null;
? ? ? ? ? ? // 創(chuàng)建一個(gè)新的偵聽服務(wù)器套接字
? ? ? ? ? ? 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;
? ? ? ? ? ? // 循環(huán)考赛,直到連接成功
? ? ? ? ? ? while (mState != STATE_CONNECTED) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? // 這是一個(gè)阻塞調(diào)用 返回成功的連接
? ? ? ? ? ? ? ? ? ? // mServerSocket.close()在另一個(gè)線程中調(diào)用惕澎,可以中止該阻塞
? ? ? ? ? ? ? ? ? ? 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:
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 正常情況。啟動(dòng)ConnectedThread颜骤。
? ? ? ? ? ? ? ? ? ? ? ? ? ? connected(socket, socket.getRemoteDevice());
? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? case STATE_NONE:
? ? ? ? ? ? ? ? ? ? ? ? case STATE_CONNECTED:
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 沒有準(zhǔn)備或已連接唧喉。新連接終止。
? ? ? ? ? ? ? ? ? ? ? ? ? ? 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 客戶端
客戶端主要用來創(chuàng)建RFCOMM socket,并連接服務(wù)端鸠项。
先掃描周圍的藍(lán)牙設(shè)備干跛,如果掃描到指定設(shè)備則進(jìn)行連接。mBlthChatUtil.connect(scanDevice)連接到設(shè)備祟绊,
連接過程主要在ConnectThread線程中進(jìn)行楼入,先創(chuàng)建socket,方式有兩種牧抽,
如下代碼中是安全的(createRfcommSocketToServiceRecord)嘉熊。另一種不安全連接對(duì)應(yīng)的函數(shù)是createInsecureRfcommSocketToServiceRecord。
private class ConnectThread extends Thread {
? ? ? ? private BluetoothSocket mmSocket;
? ? ? ? private final BluetoothDevice mmDevice;
? ? ? ? public ConnectThread(BluetoothDevice device) {
? ? ? ? ? ? mmDevice = device;
? ? ? ? ? ? BluetoothSocket tmp = null;
? ? ? ? ? ? // 得到一個(gè)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 連接,該調(diào)用會(huì)阻塞阎姥,直到連接成功或失敗
? ? ? ? ? ? ? ? mmSocket.connect();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? connectionFailed();
? ? ? ? ? ? ? ? try {//關(guān)閉這個(gè)socket
? ? ? ? ? ? ? ? ? ? mmSocket.close();
? ? ? ? ? ? ? ? } catch (IOException e2) {
? ? ? ? ? ? ? ? ? ? e2.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 啟動(dòng)連接線程
? ? ? ? ? ? connected(mmSocket, mmDevice);
? ? ? ? }
? ? ? ? public void cancel() {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? mmSocket.close();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? Log.e(TAG, "close() of connect socket failed", e);
? ? ? ? ? ? }
? ? ? ? }
? ? }
接著客戶端socket主動(dòng)連接服務(wù)端记舆。連接過程中會(huì)自動(dòng)進(jìn)行配對(duì),需要雙方同意才可以連接成功呼巴。
4 數(shù)據(jù)傳輸
客戶端與服務(wù)端連接成功后都會(huì)調(diào)用connected(mmSocket, mmDevice)泽腮,創(chuàng)建一個(gè)ConnectedThread線程()。
該線程主要用來接收和發(fā)送數(shù)據(jù)衣赶≌锷蓿客戶端和服務(wù)端處理方式一樣。該線程通過socket獲得輸入輸出流府瞄。
private ?InputStream mmInStream = socket.getInputStream();
private ?OutputStream mmOutStream =socket.getOutputStream();
發(fā)送數(shù)據(jù)
public void write(byte[] buffer) {
? ? try {
? ? ? ? mmOutStream.write(buffer);
? ? ? ? // 分享發(fā)送的信息到Activity
? ? ? ? mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
? ? ? ? ? ? ? ? .sendToTarget();
? ? } catch (IOException e) {
? ? ? ? Log.e(TAG, "Exception during write", e);
? ? }
}
接收數(shù)據(jù)
線程循環(huán)進(jìn)行接收數(shù)據(jù)碧磅。
public void run() {
? ? // 監(jiān)聽輸入流
? ? while (true) {
? ? ? ? try {
? ? ? ? ? ? byte[] buffer = new byte[1024];
? ? ? ? ? ? // 讀取輸入流
? ? ? ? ? ? int bytes = mmInStream.read(buffer);
? ? ? ? ? ? // 發(fā)送獲得的字節(jié)的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;
? ? ? ? ? ? }
? ? ? ? }
? }
---------------------
作者:zpengyong
來源:CSDN
原文:https://blog.csdn.net/VNanyesheshou/article/details/51554852
版權(quán)聲明:本文為博主原創(chuàng)文章碘箍,轉(zhuǎn)載請(qǐng)附上博文鏈接!