首先需要在清單配置里面添加兩個權(quán)限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
android里面藍(lán)牙是通過BluetoothAdapter來進(jìn)行操作的,所以首先我們需要獲取到BluetoothAdapter的實(shí)例
//先獲取BlueToothAdapter的實(shí)例
BluetoothAdapter blueToothAdapter = BluetoothAdapter.getDefaultAdapter();
在搜索之前娃善,我們可以先獲取與我們配對過的設(shè)備
//獲取已經(jīng)配對過的設(shè)備的集合
Set<BluetoothDevice> bondedDevices = blueToothAdapter.getBondedDevices()
但是想要獲取到配對過的設(shè)備论衍,我們必須是在
//手機(jī)有藍(lán)牙設(shè)備并且藍(lán)牙是打開的
blueToothAdapter != null && blueToothAdapter.isEnabled()
下面我們談一下藍(lán)牙的<b>打開方式</b>,目前作者知道的方式有3種
第一種:
//強(qiáng)制打開藍(lán)牙
blueToothAdapter.enable();
第二種:
//會以dialog的形式打開一個activity聚磺,并且如果我們通過startActivityForResult的形式的話
//還能查看藍(lán)牙是否被打開坯台,或者處理藍(lán)牙被打開之后的操作
//如果是result_ok的話那么是打開,反之打開失敗
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
第三種:
//設(shè)置本地設(shè)備可以被其它設(shè)備搜索瘫寝,可被搜索的時間是有限的捂人,最多為300s
//效果和第二種類似
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
<b>關(guān)閉藍(lán)牙</b>我們可以調(diào)用
blueToothAdapter.disable();
接下來是<b>藍(lán)牙搜索</b>
首先如果想要開啟藍(lán)牙搜索,那么只需要調(diào)用
blueToothAdapter.startDiscovery();
但是我們該如何接收我們搜索到的設(shè)備呢矢沿,很明顯當(dāng)然是通過接收廣播的形式來接收滥搭。所以我們應(yīng)該自定義一個廣播接收器,
class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
Log.e(getPackageName(), "找到新設(shè)備了");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
}
}
}
如上代碼捣鲸,我們可以獲取到搜索到的藍(lán)牙設(shè)備瑟匆。我們只需在代碼里面注冊這個廣播接收器,在調(diào)用藍(lán)牙的搜索方法栽惶,就能夠進(jìn)行藍(lán)牙的搜索了愁溜。
//注冊廣播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(new BluetoothReceiver(), intentFilter);
這里需要注意的是,如果你的代碼將運(yùn)行在(Build.VERSION.SDK_INT >= 23)的設(shè)備上外厂,那么務(wù)必加上以下權(quán)限冕象,并在代碼中動態(tài)的申請權(quán)限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
private void requestPermission() {
if (Build.VERSION.SDK_INT >= 23) {
int checkAccessFinePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if (checkAccessFinePermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSION_ACCESS_LOCATION);
Log.e(getPackageName(), "沒有權(quán)限,請求權(quán)限");
return;
}
Log.e(getPackageName(), "已有定位權(quán)限");
//這里可以開始搜索操作
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_ACCESS_LOCATION: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e(getPackageName(), "開啟權(quán)限permission granted!");
//這里可以開始搜索操作
} else {
Log.e(getPackageName(), "沒有定位權(quán)限汁蝶,請先開啟!");
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
再來就是<b>藍(lán)牙的配對</b>渐扮,目前樓主采用的是通過反射的形式來調(diào)用BluetoothDevice的createBondde()方法论悴,如果我們想監(jiān)聽配對的這個過程,那么我們可以為廣播接收器再注冊一個action墓律。
try {
//如果想要取消已經(jīng)配對的設(shè)備膀估,只需要將creatBond改為removeBond
Method method = BluetoothDevice.class.getMethod("createBond");
Log.e(getPackageName(), "開始配對");
method.invoke(device);
} catch (Exception e) {
e.printStackTrace();
}
//綁定狀態(tài)發(fā)生變化
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
廣播接收器里面我們可以這樣寫
if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_NONE:
Log.e(getPackageName(), "取消配對");
break;
case BluetoothDevice.BOND_BONDING:
Log.e(getPackageName(), "配對中");
break;
case BluetoothDevice.BOND_BONDED:
Log.e(getPackageName(), "配對成功");
break;
}
}
再來就是<b>向已經(jīng)配對的設(shè)備發(fā)送數(shù)據(jù)</b>
發(fā)送數(shù)據(jù)分為服務(wù)端和客戶端,通過socket來進(jìn)行消息的交互耻讽。
服務(wù)端
new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
try {
BluetoothServerSocket serverSocket = blueToothAdapter.listenUsingRfcommWithServiceRecord("serverSocket", uuid);
mHandler.sendEmptyMessage(startService);
BluetoothSocket accept = serverSocket.accept();
is = accept.getInputStream();
byte[] bytes = new byte[1024];
int length = is.read(bytes);
Message msg = new Message();
msg.what = getMessageOk;
msg.obj = new String(bytes, 0, length);
mHandler.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
客戶端
new Thread(new Runnable() {
@Override
public void run() {
OutputStream os = null;
try {
BluetoothSocket socket = strArr.get(i).createRfcommSocketToServiceRecord(uuid);
socket.connect();
os = socket.getOutputStream();
os.write("testMessage".getBytes());
os.flush();
mHandler.sendEmptyMessage(sendOver);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
可以看到無論是服務(wù)端還是客戶端察纯,都需要新起一個子線程來操作。那么服務(wù)端和客戶端是怎么識別對方的呢针肥,那么就需要用到<b>UUID</b>了饼记,只有當(dāng)服務(wù)端和客戶端的<b>UUID</b>相同的時候才能夠建立連接。
<b>藍(lán)牙連接發(fā)送數(shù)據(jù)的時候的坑</b>
當(dāng)博主第一次寫好客戶端服務(wù)端測試的時候慰枕,服務(wù)端一直報(bào)錯
java.io.IOException: bt socket closed, read return: -1
也就是這句話報(bào)錯
is.read(bytes)
當(dāng)時一直以為是讀取返回值是-1就會報(bào)錯具则,但是不對啊,以前這么寫也沒錯過捺僻,在網(wǎng)上百度了半天,看了別人的博客論壇也沒有解決辦法崇裁,最后才注意到報(bào)錯的前一句匕坯,
bt socket closed
之前一直認(rèn)為是什么服務(wù)端這邊,報(bào)錯連接才會斷開拔稳,后來一想客戶端也會斷開連接啊葛峻,這才找到問題所在。原來是我當(dāng)時write完數(shù)據(jù)之后將連接關(guān)閉了巴比,這才導(dǎo)致服務(wù)端這邊連接斷開的术奖。所以重要的事情要說3遍,千萬別急著斷開連接轻绞,千萬別急著斷開連接采记,千萬別急著斷開連接。
<a >github源代碼鏈接</a>