一:介紹
React Native (簡稱RN)是Facebook于2015年4月開源的跨平臺移動應用開發(fā)框架旧巾,是Facebook早先開源的JS框架 React 在原生移動應用平臺的衍生產(chǎn)物幻林,目前支持iOS和安卓兩大平臺。RN使用Javascript語言智亮,類似于HTML的JSX克懊,以及CSS來開發(fā)移動應用洋丐,因此熟悉Web前端開發(fā)的技術(shù)人員只需很少的學習就可以進入移動應用開發(fā)領(lǐng)域豪诲。
在React Native移動平臺項目開發(fā)中,除了React Native 提供的封裝好的部分插件和原聲組建外达椰,在實際的項目中還需要使用到很多其他的插件翰蠢,比如網(wǎng)絡(luò)請求、數(shù)據(jù)庫啰劲、相機梁沧、相冊、通訊錄蝇裤、視頻播放器廷支、瀏覽器、藍牙連接栓辜、圖片處理恋拍、消息推送、地圖藕甩、統(tǒng)計施敢、埋點等等APP開發(fā)中需要用到的功能,都為IDE開發(fā)平臺提供封裝好的插件辛萍,以便項目開發(fā)使用悯姊。
另外羡藐,這些博文都是來源于我日常開發(fā)中的技術(shù)總結(jié)贩毕,在時間允許的情況下,我會針對技術(shù)點分別分享iOS仆嗦、Android兩個版本辉阶,如果有其他技術(shù)點需要,可在文章后留言瘩扼,我會盡全力幫助大家谆甜。這篇文章重點介紹系統(tǒng)藍牙插件的開發(fā)與使用
二:實現(xiàn)思路分析
系統(tǒng)藍牙插件是通過調(diào)用系統(tǒng) CoreBluetooth 藍牙庫實現(xiàn)藍牙掃描、連接集绰、發(fā)送數(shù)據(jù)等功能规辱。并通過 BlueToothPlugin 類開放 BlueToothPlugin 接口提供給 H5頁面端調(diào)用。
具體的實現(xiàn)思路如下:
新建 BlueToothPlugin 類栽燕,實現(xiàn) RCTBridgeModule 協(xié)議
添加 RCT_EXPORT_MODULE() 宏
添加 React Native 跟控制器
聲明被 JavaScript 調(diào)用的藍牙初始化方法
聲明被 JavaScript 調(diào)用的發(fā)送藍牙數(shù)據(jù)方法
聲明被 JavaScript 調(diào)用的斷開藍牙連接方法
實現(xiàn)判斷藍牙狀態(tài)代理方法
實現(xiàn)藍牙外設(shè)發(fā)現(xiàn)代理方法
實現(xiàn)外設(shè)服務(wù)特征的代理方法
實現(xiàn)寫入藍牙數(shù)據(jù)后回調(diào)的代理方法
Javascript調(diào)用瀏覽器方法
三:實現(xiàn)源碼分析
實現(xiàn)源碼分析是根據(jù)上面列出的具體實現(xiàn)思路來為大家解刨內(nèi)部的實現(xiàn)流程及核心代碼分析罕袋。
1. 新建 BlueToothPlugin 類改淑,實現(xiàn) RCTBridgeModule 協(xié)議
新建繼承 NSObject 的 BlueToothPlugin 類,并實現(xiàn) RCTBridgeModule 協(xié)議
// BlueToothPlugin.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "RCTEventEmitter.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface BlueToothPlugin : RCTEventEmitter<RCTBridgeModule,CBCentralManagerDelegate, CBPeripheralDelegate>
@end
2. 添加 RCT_EXPORT_MODULE() 宏
為了實現(xiàn) RCTBridgeModule 協(xié)議浴讯,BlueToothPlugin 的類需要包含RCT_EXPORT_MODULE() 宏朵夏。
并在這個宏里面添加一個參數(shù)“ BlueToothPlugin”用來指定在 JavaScript 中訪問這個模塊的名字。
如果你不指定榆纽,默認就會使用這個 Objective-C 類的名字仰猖。
如果類名以 RCT 開頭,則 JavaScript 端引入的模塊名會自動移除這個前綴奈籽。
// BlueToothPlugin.m
#import "BlueToothPlugin.h"
@implementation BlueToothPlugin
RCT_EXPORT_MODULE(BlueToothPlugin);
@end
3. 添加React Native跟控制器
如果不添加React Native跟控制器饥侵,view將不能正常顯示出來,實現(xiàn)方法如下:
// BlueToothPlugin.m
#import <React/RCTUtils.h>
引入<React/RCTUtils.h>之后衣屏,在視圖初始化或者顯示的時候爆捞,按照如下方法調(diào)用即可
UIViewController *vc = RCTPresentedViewController();
4. 聲明被 JavaScript 調(diào)用的藍牙初始化方法
藍牙初始化方法是對 CBCentralManager 的初始化以及代理調(diào)用線程設(shè)置,React Native需要明確的聲明要給 JavaScript 導出的方法勾拉,否則 React Native 不會導出任何方法煮甥。下面通過舉例來展示聲明的方法,通過RCT_EXPORT_METHOD()宏來實現(xiàn):
// ScanPlugin.m
#import "ScanPlugin.h"
#import <React/RCTUtils.h>
@implementation ScanPlugin
RCT_EXPORT_MODULE(ScanPlugin);
RCT_EXPORT_METHOD(initPeripheral:(NSDictionary *)arguments
withCompletionHandler:(RCTResponseSenderBlock)completion
failureHandler:(RCTResponseSenderBlock)failure)
{
NSLog(@"調(diào)起藍牙初始化方法");
self.cMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
@end
5. 聲明被 JavaScript 調(diào)用的發(fā)送藍牙數(shù)據(jù)方法
發(fā)送藍牙數(shù)據(jù)方法是在獲取外設(shè)并連接成功之后調(diào)用藕赞,需要獲取寫入特征 characteristic成肘,需要發(fā)送的數(shù)據(jù),數(shù)據(jù)為 NSData 類型斧蜕。
核心源碼如下:
RCT_EXPORT_METHOD(sendData:(NSDictionary *)arguments
withCompletionHandler:(RCTResponseSenderBlock)completion
failureHandler:(RCTResponseSenderBlock)failure)
{
NSLog(@"調(diào)起藍牙發(fā)送數(shù)據(jù)方法");
NSData *data = [self p_dataWithString:arguments[@"data"]];
if (self.characteristic) {
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}
}
6. 聲明被 JavaScript 調(diào)用的斷開藍牙連接方法
當連接的設(shè)備藍牙連接成功后如果想主動斷開連接双霍,需要調(diào)用 cancelPeripheralConnection 方法來實現(xiàn),傳參需要斷開設(shè)備的 peripheral 批销。
React Native需要明確的聲明要給 JavaScript 導出的方法洒闸,否則 React Native 不會導出任何方法。代碼如下:
RCT_EXPORT_METHOD(disconnectBlueTooth:(RCTResponseSenderBlock)completion
failureHandler:(RCTResponseSenderBlock)failure)
{
NSLog(@"調(diào)起斷開藍牙連接方法");
[self.cMgr cancelPeripheralConnection:self.peripheral];
}
7. 實現(xiàn)判斷藍牙狀態(tài)代理方法
藍牙狀態(tài)獲取需要通過實現(xiàn)代理均芽,在 CBCentralManager 初始化時會觸發(fā)代理方法丘逸,并且只有獲取到藍牙的狀態(tài)才可以進行藍牙連接,否則提前連接無效掀宋。
源碼如下:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state) {
case CBManagerStateUnknown:{
NSLog(@"未知狀態(tài)");
}
break;
case CBManagerStateResetting:
{
NSLog(@"重置狀態(tài)");
}
break;
case CBManagerStateUnsupported:
{
NSLog(@"不支持的狀態(tài)");
}
break;
case CBManagerStateUnauthorized:
{
NSLog(@"未授權(quán)的狀態(tài)");
}
break;
case CBManagerStatePoweredOff:
{
NSLog(@"關(guān)閉狀態(tài)");
}
break;
case CBManagerStatePoweredOn:
{
NSLog(@"開啟狀態(tài)-可用狀態(tài)");
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
break;
default:
break;
}
}
8. 實現(xiàn)藍牙外設(shè)發(fā)現(xiàn)代理方法
在發(fā)起藍牙掃面之后深纲,如果掃面到設(shè)備會在 didDiscoverPeripheral 代理方法中回調(diào)相關(guān)設(shè)備數(shù)據(jù),其中包括劲妙,中心管理者 central湃鹊、外設(shè) peripheral、設(shè)備自定義數(shù)據(jù) advertisementData镣奋、RSSI 信號強度币呵。
核心源碼如下:
- (void)centralManager:(CBCentralManager *)central // 中心管理者
didDiscoverPeripheral:(CBPeripheral *)peripheral // 外設(shè)
advertisementData:(NSDictionary *)advertisementData // 外設(shè)攜帶的數(shù)據(jù)
RSSI:(NSNumber *)RSSI // 外設(shè)發(fā)出的藍牙信號強度
{
}
- 實現(xiàn)外設(shè)服務(wù)特征的代理方法
實現(xiàn)外設(shè)服務(wù)特征,這個是比較重要的方法侨颈,你在這里可以通過事先知道UUID找到你需要的特征余赢,訂閱特征掸驱,或者這里寫入數(shù)據(jù)給特征也可以。源碼如下:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
// 遍歷所有的特征
for (CBCharacteristic *characteristic in service.characteristics)
{
NSLog(@"特征值:%@",characteristic.UUID.UUIDString);
}
}
- 實現(xiàn)寫入藍牙數(shù)據(jù)后回調(diào)的代理方法
在寫入數(shù)據(jù)成功之后没佑,會通過 didWriteValueForCharacteristic 方法回調(diào)告訴我們數(shù)據(jù)發(fā)送成功毕贼,可以在該方法中進行下一步操作。源碼如下:
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
}
11. Javascript調(diào)用瀏覽器方法
現(xiàn)在從 Javascript 里可以這樣調(diào)用這個方法:
import { NativeModules } from "react-native";
const BlueToothPlugin = NativeModules.BlueToothPlugin;
BlueToothPlugin.initPeripheral({"peripheralName": "", "peripheralServiceID": "", "peripheralCharacteristicID": ""}, (msg) => {
Alert.alert(JSON.stringify(msg));
},(err) => {
Alert.alert(JSON.stringify(err));
});
BlueToothPlugin.sendData({"data": ""}, (msg) => {
Alert.alert(JSON.stringify(msg));
},(err) => {
Alert.alert(JSON.stringify(err));
});
BlueToothPlugin.disconnectBlueTooth((msg) => {
Alert.alert(JSON.stringify(msg));
},(err) => {
Alert.alert(JSON.stringify(err));
});