前言
最近在做Android藍(lán)牙這部分內(nèi)容茉帅,所以查閱了很多相關(guān)資料粹懒,在此總結(jié)一下扮念。
基本概念
Bluetooth是一種短距離(10米)的無線通信技術(shù)標(biāo)準(zhǔn)榜跌,藍(lán)牙協(xié)議分為4層闪唆,即核心協(xié)議層、電纜替代協(xié)議層钓葫、電話控制協(xié)議層和采納的其它協(xié)議層悄蕾。這4種協(xié)議中最重要的是核心協(xié)議。藍(lán)牙的核心協(xié)議包括基帶础浮、鏈路管理帆调、邏輯鏈路控制和適應(yīng)協(xié)議四部分。其中鏈路管理(LMP)負(fù)責(zé)藍(lán)牙組件間連接的建立豆同。邏輯鏈路控制與適應(yīng)協(xié)議(L2CAP)位于基帶協(xié)議層上番刊,屬于數(shù)據(jù)鏈路層,是一個(gè)為高層傳輸和應(yīng)用層協(xié)議屏蔽基帶協(xié)議的適配協(xié)議影锈。
安卓平臺(tái)提供對(duì)藍(lán)牙的通訊棧的支持芹务,允許設(shè)別和其他的設(shè)備進(jìn)行無線傳輸數(shù)據(jù)。應(yīng)用程序?qū)油ㄟ^安卓API來調(diào)用藍(lán)牙的相關(guān)功能鸭廷,這些API使程序無線連接到藍(lán)牙設(shè)備枣抱,并擁有P2P或者多端無線連接的特性。
- 藍(lán)牙的功能:
- 掃描其他藍(lán)牙設(shè)備
- 為可配對(duì)的藍(lán)牙設(shè)備查詢藍(lán)牙適配器
- 建立RFCOMM通道(其實(shí)就是尼瑪?shù)恼J(rèn)證)
- 通過服務(wù)搜索來鏈接其他的設(shè)備
- 與其他的設(shè)備進(jìn)行數(shù)據(jù)傳輸
- 管理多個(gè)連接
- 藍(lán)牙建立連接必須要求:
- 打開藍(lán)牙
- 查找附近已配對(duì)或可用設(shè)備
- 連接設(shè)備
- 設(shè)備間數(shù)據(jù)交互
藍(lán)牙API
代碼分布
packages/apps/Bluetooth/
- 藍(lán)牙應(yīng)用,主要是關(guān)于藍(lán)牙應(yīng)用協(xié)議的表現(xiàn)代碼辆床,包括opp佳晶、hfp、hdp讼载、a2dp轿秧、pan等等
frameworks/base/core/Java/android/server/
- 4.2以后這個(gè)目錄雖然還有,但里面代碼已經(jīng)轉(zhuǎn)移到應(yīng)用層了咨堤,就是前面那個(gè)目錄菇篡,所以4.2.2上的藍(lán)牙這里可以忽略。
framework/base/core/java/android/bluetooth
- 這個(gè)目錄里的代碼更像一個(gè)橋梁一喘,里面有供java層使用一些類逸贾,也有對(duì)應(yīng)的aidl文件聯(lián)系C、C++部分的代碼,還是挺重要的铝侵。
kernel\drivers\bluetoothBluetooth
- 具體協(xié)議實(shí)現(xiàn)灼伤。包括hci,hid,rfcomm,sco,SDP等協(xié)議
kernel\net\bluetooth Linux kernel
- 對(duì)各種接口的Bluetoothdevice的驅(qū)動(dòng)咪鲜。例如:USB接口狐赡,串口等,上面kernel這兩個(gè)目錄有可能看不到的疟丙,但一定會(huì)有的颖侄。
external\bluetooth\bluedroid
- 官方藍(lán)牙協(xié)議棧
system\bluetoothBluetooth
- 適配層代碼,和framework那個(gè)作用類似享郊,是串聯(lián)framework與協(xié)議棧的工具览祖。
關(guān)鍵類
/frameworks/base/core/java/android/bluetooth/
-
BluetoothAdapter
代表本地藍(lán)牙適配器(藍(lán)牙發(fā)射器),是所有藍(lán)牙交互的入口。通過它可以搜索其它藍(lán)牙設(shè)備,查詢已經(jīng)配對(duì)的設(shè)備列表,通過已知的MAC地址創(chuàng)建BluetoothDevice,創(chuàng)建BluetoothServerSocket監(jiān)聽來自其它設(shè)備的通信炊琉。 -
BluetoothDevice
代表了一個(gè)遠(yuǎn)端的藍(lán)牙設(shè)備, 使用它請(qǐng)求遠(yuǎn)端藍(lán)牙設(shè)備連接或者獲取 遠(yuǎn)端藍(lán)牙設(shè)備的名稱展蒂、地址、種類和綁定狀態(tài)苔咪。 (其信息是封裝在 bluetoothsocket 中) 锰悼。 -
BluetoothSocket
代表了一個(gè)藍(lán)牙套接字的接口(類似于 tcp 中的套接字) ,他是應(yīng)用程 序通過輸入、輸出流與其他藍(lán)牙設(shè)備通信的連接點(diǎn)团赏。 -
BluetoothServerSocket
代表打開服務(wù)連接來監(jiān)聽可能到來的連接請(qǐng)求 (屬于 server 端) , 為了連接兩個(gè)藍(lán)牙設(shè)備必須有一個(gè)設(shè)備作為服務(wù)器打開一個(gè)服務(wù)套接字箕般。 當(dāng)遠(yuǎn)端設(shè)備發(fā)起連 接連接請(qǐng)求的時(shí)候,并且已經(jīng)連接到了的時(shí)候,Blueboothserversocket 類將會(huì)返回一個(gè) bluetoothsocket。 -
BluetoothClass
描述了一個(gè)設(shè)備的特性(profile)或該設(shè)備上的藍(lán)牙大致可以提供哪些服務(wù)(service),但不可信舔清。比如,設(shè)備是一個(gè)電話丝里、計(jì)算機(jī)或手持設(shè)備;Blueboothserversocket 設(shè)備可以提供audio/telephony服務(wù)等√遐耍可以用它來進(jìn)行一些UI上的提示丙者。 -
BluetoothProfile
藍(lán)牙協(xié)議 -
BluetoothHeadset
提供手機(jī)使用藍(lán)牙耳機(jī)的支持。這既包括藍(lán)牙耳機(jī)和免提(V1.5)模式营密。 -
BluetoothA2dp
定義高品質(zhì)的音頻,可以從一個(gè)設(shè)備傳輸?shù)搅硪粋€(gè)藍(lán)牙連接。 “A2DP的”代表高級(jí)音頻分配模式目锭。 -
BluetoothHealth
代表了醫(yī)療設(shè)備配置代理控制的藍(lán)牙服務(wù) -
BluetoothHealthCallback
一個(gè)抽象類,使用實(shí)現(xiàn)BluetoothHealth回調(diào)评汰。你必須擴(kuò)展這個(gè)類并實(shí)現(xiàn)回調(diào)方法接收更新應(yīng)用程序的注冊狀態(tài)和藍(lán)牙通道狀態(tài)的變化。 -
BluetoothHealthAppConfiguration
代表一個(gè)應(yīng)用程序的配置,藍(lán)牙醫(yī)療第三方應(yīng)用注冊與遠(yuǎn)程藍(lán)牙醫(yī)療設(shè)備交流痢虹。 -
BluetoothProfile.ServiceListener
當(dāng)他們已經(jīng)連接到或從服務(wù)斷開時(shí)通知BluetoothProfile IPX的客戶時(shí)一個(gè)接口(即運(yùn)行一個(gè)特定的配置文件,內(nèi)部服務(wù))被去。
\packages\apps\Settings\src\com\android\settings\bluetooth
-
BluetoothEnabler
界面上藍(lán)牙開啟、關(guān)閉的開關(guān)就是它了奖唯, -
BluetoothSettings
主界面惨缆,用于管理配對(duì)和連接設(shè)備 -
LocalBluetoothManager
提供了藍(lán)牙API上的簡單調(diào)用接口,這里只是開始。 -
CachedBluetoothDevice
描述藍(lán)牙設(shè)備的類坯墨,對(duì)BluetoothDevice的再封裝 -
BluetoothPairingDialog
那個(gè)配對(duì)提示的對(duì)話框
/packages/apps/Phone/src/com/android/phone/
-
BluetoothPhoneService
在phone的目錄肯定和電話相關(guān)了寂汇,藍(lán)牙接聽掛斷電話會(huì)用到這個(gè)
/packages/apps/Bluetooth/src/com/android/bluetooth/
說到這里不能不說4.2藍(lán)牙的目錄變了,在4.1及以前的代碼中packages層的代碼只有opp協(xié)議相關(guān)應(yīng)用的代碼捣染,也就是文件傳輸那部分骄瓣,而4.2的代碼應(yīng)用層的代碼則豐富了許多,按具體的藍(lán)牙應(yīng)用協(xié)議來區(qū)別耍攘,分為以下文件夾(這里一并對(duì)藍(lán)牙一些名詞作個(gè)簡單解釋)
-
btservice
這個(gè)前面AdapterService.java的描述大家應(yīng)該能猜到一些榕栏,關(guān)于藍(lán)牙基本操作的目錄,一切由此開始蕾各。- AdapterService (4.2后才有的代碼)藍(lán)牙打開扒磁、關(guān)閉、掃描式曲、配對(duì)都會(huì)走到這里妨托,其實(shí)更準(zhǔn)確的說它替代了4.1之前的BluetoothService.java,原來的工作就由這個(gè)類來完成了检访。
a2dp
(Advanced Audio Distribution Profile)高級(jí)音頻傳輸模式始鱼,藍(lán)牙立體聲,和藍(lán)牙耳機(jī)聽歌有關(guān)那些脆贵。avrcp
音頻/視頻遠(yuǎn)程控制配置文件医清,是用來聽歌時(shí)暫停,上下歌曲選擇的卖氨。hdp
(Health Device Profile)藍(lán)牙醫(yī)療設(shè)備模式会烙,可以創(chuàng)建支持藍(lán)牙的醫(yī)療設(shè)備,使用藍(lán)牙通信的應(yīng)用筒捺,例如心率監(jiān)視器柏腻,血液,溫度計(jì)和秤系吭。hfp
(Hands-free Profile)讓藍(lán)牙設(shè)備可以控制電話五嫂,如接聽、掛斷肯尺、拒接沃缘、語音撥號(hào)等,拒接则吟、語音撥號(hào)要視藍(lán)牙耳機(jī)及電話是否支持槐臀。pbap
(Phonebook Access Profile)電話號(hào)碼簿訪問協(xié)議hid
(The Human Interface Device)人機(jī)交互接口,藍(lán)牙鼠標(biāo)鍵盤什么的就是這個(gè)了氓仲。該協(xié)議改編自USB HID Protocol水慨。opp
(Object Push Profile)對(duì)象存儲(chǔ)規(guī)范得糜,最為常見的,文件的傳輸都是使用此協(xié)議晰洒。pan
(Personal Area Network)描述了兩個(gè)或更多個(gè)藍(lán)牙設(shè)備如何構(gòu)成一個(gè)即時(shí)網(wǎng)絡(luò)朝抖,和網(wǎng)絡(luò)有關(guān)還有串行端口功能(SPP),撥號(hào)網(wǎng)絡(luò)功能(DUN)
android 4.2的藍(lán)牙應(yīng)用層部分代碼更豐富了欢顷,雖然有些目錄還沒具體代碼槽棍,不過說不準(zhǔn)哪個(gè)版本更新就有了,就像4.0添加了hdp醫(yī)療那部分一樣抬驴。另外原本在framework的JNI代碼也被移到packages/apps/bluetooth當(dāng)中炼七。
主要方法
-
BluetoothAdapter
(藍(lán)牙本地適配器)-
getDefaultAdapter()
得到本地藍(lán)牙適配器 -
setName(String name)
設(shè)置藍(lán)牙名稱 -
disable()
關(guān)閉藍(lán)牙 -
enable()
打開藍(lán)牙 -
isEnabled()
判斷藍(lán)牙是否打開 -
getName()
得到本地藍(lán)牙的名稱 -
getAddress()
得到本地藍(lán)牙適配器的地址 -
getBondedDevices()
得到已經(jīng)綁定的藍(lán)牙的設(shè)備 -
getRemoteDevice(byte[] address)
得到遠(yuǎn)程藍(lán)牙設(shè)備 -
getRemoteDevice(String address)
得到遠(yuǎn)程藍(lán)牙設(shè)備 -
startDiscovery()
開始搜多附近藍(lán)牙 -
cancelDiscovery()
停止當(dāng)前搜索藍(lán)牙的 Task -
listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
創(chuàng)建 BluetoothServerSocket
-
-
BluetoothDevice
(藍(lán)牙設(shè)備)-
createBond()
藍(lán)牙配對(duì) (低版本不支持,>=api19) -
createRfcommSocketToServiceRecord(UUID uuid)
創(chuàng)建 BluetoothSocket -
getBondState()
得到配對(duì)的狀態(tài) -
getAddress()
得到遠(yuǎn)程藍(lán)牙適配器的地址 -
getName()
得到遠(yuǎn)程藍(lán)牙的名稱
-
-
BluetoothServerSocket
(數(shù)據(jù)傳輸服務(wù)端)
這個(gè)類一共只有三個(gè)方法兩個(gè)重載的布持。兩個(gè)重載的區(qū)別在于后面的方法指定了過時(shí)時(shí)間豌拙,需要注意的是,執(zhí)行這兩個(gè)方法的時(shí)候题暖,直到接收到了客戶端的請(qǐng)求(或是過期之后)按傅,都會(huì)阻塞線程,應(yīng)該放在新線程里運(yùn)行胧卤!-
close()
關(guān)閉 -
connect()
連接 -
isConnected()
判斷當(dāng)前的連接狀態(tài) -
accept()
接收請(qǐng)求 -
accept(int timeout)
接收請(qǐng)求
-
-
BluetoothSocket
(數(shù)據(jù)傳輸客戶端)-
close()
關(guān)閉 -
connect()
連接 -
getInptuStream()
獲取輸入流 -
getOutputStream()
獲取輸出流 -
getRemoteDevice()
獲取遠(yuǎn)程設(shè)備唯绍,這里指的是獲取bluetoothSocket指定連接的那個(gè)遠(yuǎn)程藍(lán)牙設(shè)備
-
藍(lán)牙操作
打開和關(guān)閉藍(lán)牙
開啟藍(lán)牙有兩種方法:
一、直接調(diào)用系統(tǒng)對(duì)話框啟動(dòng)藍(lán)牙:
在AndroidManifest.xml
文件中添加需要的權(quán)限枝誊,高版本也不需要?jiǎng)討B(tài)授權(quán):
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
然后况芒,在代碼中執(zhí)行:
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
如果不想讓用戶看到對(duì)話框,那么我們還可以選擇第二種方法叶撒,進(jìn)行靜默開啟藍(lán)牙绝骚。
二、靜默開啟祠够,不會(huì)有方法一的對(duì)話框:
照樣在AndroidManifest.xml
文件中添加需要的權(quán)限:
<!-- 已適配Android6.0 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
由于藍(lán)牙所需要的權(quán)限包含Dangerous Permissions压汪,所以我們需要在Java代碼中進(jìn)行動(dòng)態(tài)授權(quán)處理:
private static final int REQUEST_BLUETOOTH_PERMISSION=10;
private void requestBluetoothPermission(){
//判斷系統(tǒng)版本
if (Build.VERSION.SDK_INT >= 23) {
//檢測當(dāng)前app是否擁有某個(gè)權(quán)限
int checkCallPhonePermission = ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION);
//判斷這個(gè)權(quán)限是否已經(jīng)授權(quán)過
if(checkCallPhonePermission != PackageManager.PERMISSION_GRANTED){
//判斷是否需要 向用戶解釋,為什么要申請(qǐng)?jiān)摍?quán)限
if(ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION))
Toast.makeText(this,"Need bluetooth permission.",
Toast.LENGTH_SHORT).show();
ActivityCompat.requestPermissions(this ,new String[]
{Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_BLUETOOTH_PERMISSION);
return;
}else{
}
} else {
}
}
接下來我們就可以靜默開啟藍(lán)牙了:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.enable(); //開啟
關(guān)閉藍(lán)牙
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
搜索藍(lán)牙設(shè)備
搜索分為主動(dòng)搜索和被動(dòng)搜索:
一古瓤、被動(dòng)搜索
if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
{
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
// 設(shè)置被發(fā)現(xiàn)時(shí)間止剖,最大值是3600秒,0表示設(shè)備總是可以被發(fā)現(xiàn)的(小于0或者大于3600則會(huì)被自動(dòng)設(shè)置為120秒)
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
activity.startActivity(discoverableIntent);
}
二、主動(dòng)搜索
創(chuàng)建BluetoothAdapter
對(duì)象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
我們先獲取并顯示一下已經(jīng)配對(duì)的藍(lán)牙設(shè)備列表
/*
* 已配對(duì)設(shè)備列表
*/
private ListView mBoundDevicesLv;
/**
* 顯示已配對(duì)的設(shè)備列表
*/
private void showBoundDevices() {
List<Map<String, String>> mBoundDevicesList = new ArrayList<>();
Set<BluetoothDevice> boundDeviceSet = mBluetoothAdapter.getBondedDevices();
for (BluetoothDevice boundDevices : boundDeviceSet) {
Map<String, String> mBoundDevicesMap = new HashMap<>();
mBoundDevicesMap.put("name", boundDevices.getName());
mBoundDevicesMap.put("address", boundDevices.getAddress());
mBoundDevicesList.add(mBoundDevicesMap);
}
SimpleAdapter mSimpleAdapter = new SimpleAdapter(MainActivity.this, mBoundDevicesList,
android.R.layout.simple_list_item_2,
new String[]{"name", "address"},
new int[]{android.R.id.text1, android.R.id.text2});
mBoundDevicesLv.setAdapter(mSimpleAdapter);
}
開始搜索
if (mBluetoothAdapter == null) {
LogUtil.e(TAG, "設(shè)備不支持藍(lán)牙");
}
// 打開藍(lán)牙
if (!mBluetoothAdapter.isEnabled()) {
BluetoothAdapter.enable();
mBluetoothAdapter.cancelDiscovery();
}
// 尋找藍(lán)牙設(shè)備落君,android會(huì)將查找到的設(shè)備以廣播形式發(fā)出去
while (!mBluetoothAdapter.startDiscovery()) {
LogUtil.e(TAG, "嘗試失敗");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
定義搜索結(jié)果的廣播接收器
// 設(shè)置廣播信息過濾
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);//每搜索到一個(gè)設(shè)備就會(huì)發(fā)送一個(gè)該廣播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//當(dāng)全部搜索完后發(fā)送該廣播
filter.setPriority(Integer.MAX_VALUE);//設(shè)置優(yōu)先級(jí)
registerReceiver(receiver, filter);// 注冊藍(lán)牙搜索廣播接收者穿香,接收并處理搜索結(jié)果
搜索藍(lán)牙設(shè)備的廣播接收器如下:
/**
* 搜索出的設(shè)備集合
*/
private List<Map<String, String>> devices = new ArrayList<>();
/**
* 發(fā)現(xiàn)的設(shè)備列表
*/
private ListView mDevicesLv;
/**
* 定義廣播接收器
*/
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
ToastUtil.showToast(MainActivity.this, "Showing Devices");
// 從Intent中獲取設(shè)備對(duì)象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 定義一個(gè)裝載藍(lán)牙設(shè)備名字和地址的Map
Map<String, String> deviceMap = new HashMap<>();
// 過濾已配對(duì)的和重復(fù)的藍(lán)牙設(shè)備
if ((device.getBondState() != BluetoothDevice.BOND_BONDED) && isSingleDevice(device)) {
deviceMap.put("name", device.getName() == null ? "null" : device.getName());
deviceMap.put("address", device.getAddress());
devices.add(deviceMap);
}
// 顯示發(fā)現(xiàn)的藍(lán)牙設(shè)備列表
mDevicesLv.setVisibility(View.VISIBLE);
// 加載設(shè)備
showDevices();
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//已搜素完成
}
}
};定義服務(wù)端線程類:
/**
* 判斷此設(shè)備是否存在
*/
private boolean isSingleDevice(BluetoothDevice device) {
if (devices == null) {
return true;
}
for (Map<String, String> mDeviceMap : devices) {
if ((device.getAddress()).equals(mDeviceMap.get("address"))) {
return false;
}
}
return true;
}
/**
* 顯示搜索到的設(shè)備列表
*/
private void showDevices() {
SimpleAdapter mSimpleAdapter = new SimpleAdapter(MainActivity.this, devices,
android.R.layout.simple_list_item_2,
new String[]{"name", "address"},
new int[]{android.R.id.text1, android.R.id.text2});
mDevicesLv.setAdapter(mSimpleAdapter);
}
藍(lán)牙配對(duì)
當(dāng)我們搜索到了藍(lán)牙的之后,就需要配對(duì)叽奥,因?yàn)橹挥性谂鋵?duì)之后才能連接。
在上面的搜索到的設(shè)備列表的點(diǎn)擊事件中痛侍,進(jìn)行配對(duì)朝氓。
BluetoothDevice device = (BluetoothDevice) adapter.getItem(i);
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//是否已配對(duì)
connect(device);
} else {
try {
Method boned=device.getClass().getMethod("createBond");
boolean isok= (boolean) boned.invoke(device);
if(isOk) {
connect(device);
}
} catch (Exception e) {
e.printStackTrace();
}
}
這里需要說明的是魔市,這個(gè)配對(duì)Android在API19
之后對(duì)外提供了createBond()
這個(gè)方法。但是在API19
以前并沒有這個(gè)方法赵哲,所以用反射兼容性比較好待德。
藍(lán)牙的UUID
在進(jìn)行藍(lán)牙連接之前,先介紹一下一個(gè)關(guān)鍵的東西:兩個(gè)藍(lán)牙設(shè)備進(jìn)行連接時(shí)需要使用同一個(gè)UUID枫夺。但很多讀者可能發(fā)現(xiàn)将宪,有很多型號(hào)的手機(jī)(可能是非Android系統(tǒng)的手機(jī))之間使用了不同的程序也可以使用藍(lán)牙進(jìn)行通訊。從表面上看橡庞,它們之間幾乎不可能使用同一個(gè)UUID较坛。
UUID的格式如下:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UUID的格式被分成5段,其中中間3段的字符數(shù)相同扒最,都是4丑勤,第1段是8個(gè)字符,最后一段是12個(gè)字符吧趣。所以UUID實(shí)際上是一個(gè)8-4-4-4-12的字符串法竞。
實(shí)際上,UUID和TCP的端口一樣强挫,也有一些默認(rèn)的值岔霸。例如,將藍(lán)牙模擬成串口的服務(wù)就使用了一個(gè)標(biāo)準(zhǔn)的UUID:
00001101-0000-1000-8000-00805F9B34FB
除此之外俯渤,還有很多標(biāo)準(zhǔn)的UUID呆细,如下面就是兩個(gè)標(biāo)準(zhǔn)的UUID:
信息同步服務(wù):00001104-0000-1000-8000-00805F9B34FB
文件傳輸服務(wù):00001106-0000-1000-8000-00805F9B34FB
藍(lán)牙設(shè)備間的數(shù)據(jù)傳輸
藍(lán)牙傳輸數(shù)據(jù)與Socket類似。在網(wǎng)絡(luò)中使用Socket和ServerSocket控制客戶端和服務(wù)端的數(shù)據(jù)讀寫稠诲。而藍(lán)牙通訊也由客戶端和服務(wù)端Socket來完成侦鹏。藍(lán)牙客戶端Socket是BluetoothSocket
,藍(lán)牙服務(wù)端Socket是BluetoothServerSocket
臀叙。這兩個(gè)類都在android.bluetooth包中略水。
無論是BluetoothSocket
,還是BluetoothServerSocket
劝萤,都需要一個(gè)UUID(全局唯一標(biāo)識(shí)符,Universally Unique Identifier)渊涝,UUID相當(dāng)于Socket的端口,而藍(lán)牙地址相當(dāng)于Socket的IP床嫌。
下面跨释,我們開始進(jìn)行模擬一個(gè)藍(lán)牙數(shù)據(jù)的傳輸:
一、首先來看客戶端:
定義全局常量變量:
private ListView mDevicesLv;
private BluetoothAdapter mBluetoothAdapter;
private List<Map<String, String>> devices = new ArrayList<>();
//隨便定義一個(gè)UUID
private final UUID MY_UUID = UUID.fromString("abcd1234-ab12-ab12-ab12-abcdef123456");
private BluetoothSocket clientSocket;
private BluetoothDevice device;
private OutputStream os;//輸出流
接下來我們設(shè)置設(shè)備列表的點(diǎn)擊事件
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String, String> s = devices.get(i);
String address = s.get("address");//把地址解析出來
//主動(dòng)連接藍(lán)牙服務(wù)端
try {
// 如果當(dāng)前正在搜索厌处,則取消搜索鳖谈。
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
try {
if (device == null) {
//獲得遠(yuǎn)程設(shè)備
device = mBluetoothAdapter.getRemoteDevice(address);
}
if (clientSocket == null) {
//創(chuàng)建客戶端藍(lán)牙Socket
clientSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
//開始連接藍(lán)牙,如果沒有配對(duì)則彈出對(duì)話框提示我們進(jìn)行配對(duì)
clientSocket.connect();
//獲得輸出流(客戶端指向服務(wù)端輸出文本)
os = clientSocket.getOutputStream();
}
} catch (Exception e) {
}
if (os != null) {
//往服務(wù)端寫信息
os.write("藍(lán)牙信息來了".getBytes("utf-8"));
}
} catch (Exception e) {
}
}
二阔涉、接下來看服務(wù)端:
服務(wù)端使用的是另一部手機(jī)缆娃,接受上面手機(jī)通過藍(lán)牙發(fā)送過來的信息并顯示捷绒。
定義全局常量變量:
private BluetoothAdapter mBluetoothAdapter;
private AcceptThread acceptThread;
// 和客戶端相同的UUID
private final UUID MY_UUID = UUID.fromString("abcd1234-ab12-ab12-ab12-abcdef123456");
private final String NAME = "Bluetooth_Socket";
private BluetoothServerSocket serverSocket;
private BluetoothSocket socket;
private InputStream is;//輸入流
定義服務(wù)端線程類:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), String.valueOf(msg.obj),
Toast.LENGTH_LONG).show();
super.handleMessage(msg);
}
};
// 服務(wù)端監(jiān)聽客戶端的線程類
private class AcceptThread extends Thread {
public AcceptThread() {
try {
serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (Exception e) {
}
}
public void run() {
try {
socket = serverSocket.accept();
is = socket.getInputStream();
while(true) {
byte[] buffer =new byte[1024];
int count = is.read(buffer);
Message msg = new Message();
msg.obj = new String(buffer, 0, count, "utf-8");
handler.sendMessage(msg);
}
}
catch (Exception e) {
}
}
}
在onCreate方法中初始化線程類并開啟:
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
acceptThread = new AcceptThread();
acceptThread.start();
注意,使用socket.getInputStream接收到的數(shù)據(jù)是字節(jié)流贯要,這樣的數(shù)據(jù)是沒法分析的暖侨,所以很多情況需要一個(gè)byte轉(zhuǎn)十六進(jìn)制String的函數(shù):
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
藍(lán)牙協(xié)議
藍(lán)牙協(xié)議簡介
從Android 3.0開始,Bluetooth API就包含了對(duì)Bluetooth profiles的支持。
Bluetooth profile是基于藍(lán)牙的設(shè)備之間通信的無線接口規(guī)范崇渗。
你在你的類里可以完成BluetoothProfile接口來支持某一Bluetooth profile字逗。
Android Bluetooth API完成了下面的Bluetooth profile:
-
Headset profile
提供了移動(dòng)電話上的Bluetooth耳機(jī)支持。Android提供了BluetoothHeadset類,它是一個(gè)協(xié)議,用來通過IPC(interprocess communication)控制Bluetooth Headset Service宅广。BluetoothHeadset既包含Bluetooth Headset profile
也包含Hands-Free profile
,還包括對(duì)AT命令
的支持葫掉。 -
HFP (Hands-free Profile)
,免提模式乘碑,讓藍(lán)牙設(shè)備可以控制電話挖息,如接聽、掛斷兽肤、拒接套腹、語音撥號(hào)等,拒接资铡、語音撥號(hào)要視藍(lán)牙耳機(jī)及電話是否支持电禀。 -
HDP(Health Device Profile.)
,藍(lán)牙醫(yī)療設(shè)備模式笤休,可以創(chuàng)建支持藍(lán)牙的醫(yī)療設(shè)備尖飞,使用藍(lán)牙通信的應(yīng)用,例如心率監(jiān)視器店雅,血液政基,溫度計(jì)和秤。 -
AVRCP
闹啦,音頻/視頻遠(yuǎn)程控制配置文件沮明,是用來聽歌時(shí)暫停,上下歌曲選擇的窍奋。 -
A2DP(Advanced Audio Distribution Profile)
荐健,高級(jí)音頻傳輸模式。Android提供了BluetoothA2dp類,這是一個(gè)通過IPC來控制Bluetooth A2DP的協(xié)議琳袄。 -
HID (The Human Interface Device)
江场,人機(jī)交互接口,藍(lán)牙鼠標(biāo)鍵盤什么的就是這個(gè)了窖逗。該協(xié)議改編自USB HID Protocol址否。 -
OPP (Object Push Profile)
,對(duì)象存儲(chǔ)規(guī)范碎紊,最為常見的佑附,文件的傳輸都是使用此協(xié)議用含。 -
PAN (Personal Area Network)
,描述了兩個(gè)或更多個(gè)藍(lán)牙設(shè)備如何構(gòu)成一個(gè)即時(shí)網(wǎng)絡(luò)帮匾,和網(wǎng)絡(luò)有關(guān)還有串行端口功能(SPP),撥號(hào)網(wǎng)絡(luò)功能(DUN)痴鳄。 -
PBAP (Phonebook Access Profile)
瘟斜,電話號(hào)碼簿訪問協(xié)議。
藍(lán)牙協(xié)議的使用
下面是使用profile的基本步驟:
- 獲取默認(rèn)的Bluetooth適配器痪寻。
- 使用getProfileProxy()來建立一個(gè)與profile相關(guān)的profile協(xié)議對(duì)象的連接螺句。在下面的例子中,profile協(xié)議對(duì)象是BluetoothHeadset的一個(gè)實(shí)例。
- 設(shè)置BluetoothProfile.ServiceListener橡类。該listener通知BluetoothProfile IPC客戶端,當(dāng)客戶端連接或斷連服務(wù)器的時(shí)候
- 在BluetoothProfile.ServiceListener的onServiceConnected()內(nèi),得到一個(gè)profile協(xié)議對(duì)象的句柄蛇尚。
- 一旦擁有了profile協(xié)議對(duì)象,就可以用它來監(jiān)控連接的狀態(tài),完成于該profile相關(guān)的其他操作。
例如,下面的代碼片段顯示如何連接到一個(gè)BluetoothHeadset協(xié)議對(duì)象,用來控制Headset profile:
BluetoothHeadset mBluetoothHeadset;
// 獲取默認(rèn)的Bluetooth適配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 連接Headset profile
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener(){
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
// ... 使用 mBluetoothHeadset
// 使用之后顾画,關(guān)閉Proxy
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset)
以上取劫,就先分析到這兒吧。