跟L大帝一起做~iOS藍(lán)牙開(kāi)發(fā)

首先進(jìn)一則廣告:

藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG)2010年7月7日宣布,正式采納藍(lán)牙4.0核心規(guī)范(Bluetooth Core Specification Version 4.0 ),并啟動(dòng)對(duì)應(yīng)的認(rèn)證計(jì)劃。會(huì)員廠商可以提交其產(chǎn)品進(jìn)行測(cè)試,通過(guò)后將獲得藍(lán)牙4.0標(biāo)準(zhǔn)認(rèn)證。 該技術(shù)擁有極低的運(yùn)行和待機(jī)功耗,使用一粒紐扣電池甚至可連續(xù)工作數(shù)年之久。所以藍(lán)牙技術(shù)還是可以被長(zhǎng)久利用的一種鏈接技術(shù)。

然后直接進(jìn)入正題。

大家也許會(huì)注意到一個(gè)問(wèn)題。安卓和安卓手機(jī)可以用藍(lán)牙相互鏈接,但是蘋(píng)果和蘋(píng)果手機(jī)就不行。因?yàn)樘O(píng)果鏈接用的是airdrop,所以也就不需要藍(lán)牙傳輸大文件。大家也就忽略了這個(gè)現(xiàn)象。其實(shí)airdrop本質(zhì)也是藍(lán)牙建立鏈接的。。。。。。。

今天主要就是用一款app去讀寫(xiě)藍(lán)牙設(shè)備。應(yīng)用場(chǎng)景就是手機(jī)掃描外設(shè),鏈接外設(shè),找到外設(shè)的服務(wù)和屬性,對(duì)服務(wù)和屬性進(jìn)行讀寫(xiě)操作;

代碼實(shí)現(xiàn)流程。

1.建立中心管理控制對(duì)象


```objc

#import <CoreBluetooth/CoreBluetooth.h>(導(dǎo)入頭文件)

<CBCentralManagerDelegate,CBPeripheralDelegate>繼承代理協(xié)議

//系統(tǒng)藍(lán)牙設(shè)備中心管理對(duì)象可以去掃描和鏈接外設(shè)

CBCentralManager *_manager;

//用于保存被發(fā)現(xiàn)設(shè)備

NSMutableArray *_discoverPeripherals;

//初始化并設(shè)置委托和線(xiàn)程隊(duì)列,最好一個(gè)線(xiàn)程的參數(shù)可以為nil,默認(rèn)會(huì)就main線(xiàn)程

manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

//創(chuàng)建外設(shè)數(shù)組,保存發(fā)現(xiàn)的設(shè)備。不保存設(shè)備會(huì)導(dǎo)致代理方法不可用

discoverPeripherals = [[NSMutableArray array];

```


2.掃描外設(shè),只有設(shè)備打開(kāi)狀態(tài),才可以?huà)呙琛#ㄥe(cuò)誤原因之一)


介紹一下

CBCentralManagerDelegate的主要代理方法。

必須實(shí)現(xiàn)的:

- (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主設(shè)備狀態(tài)改變的委托,在初始化CBCentralManager的時(shí)候會(huì)打開(kāi)設(shè)備,只有當(dāng)設(shè)備正確打開(kāi)后才能使用

其他選擇實(shí)現(xiàn)的代理方法。

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外設(shè)的委托

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開(kāi)外設(shè)的委托



//工程中的代碼不適合直接放出來(lái),就用示例demo中的了。(感謝這些偉大的開(kāi)發(fā)工作者)

這些狀態(tài)不需要我一一翻譯了吧,如果不懂可以私信我。我一定讓你自己查字典去。。。

-(void)centralManagerDidUpdateState:(CBCentralManager *)central{

switch (central.state) {

case CBCentralManagerStateUnknown:

NSLog(@">>>CBCentralManagerStateUnknown");

break;

case CBCentralManagerStateResetting:

NSLog(@">>>CBCentralManagerStateResetting");

break;

case CBCentralManagerStateUnsupported:

NSLog(@">>>CBCentralManagerStateUnsupported");

break;

case CBCentralManagerStateUnauthorized:

NSLog(@">>>CBCentralManagerStateUnauthorized");

break;

case CBCentralManagerStatePoweredOff:

NSLog(@">>>CBCentralManagerStatePoweredOff");

break;

case CBCentralManagerStatePoweredOn:

NSLog(@">>>CBCentralManagerStatePoweredOn");

//開(kāi)始掃描周?chē)耐庠O(shè)********** 一定要在設(shè)備開(kāi)啟狀態(tài)再掃描外設(shè)*********。

/*

第一個(gè)參數(shù)nil就是掃描周?chē)械耐庠O(shè),可以特定你想掃描到的符合要求的設(shè)備。掃描到外設(shè)后會(huì)進(jìn)入

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;

*/

[central scanForPeripheralsWithServices:nil options:nil];

break;

default:

break;

}

}


3.掃描完成,進(jìn)入連接方法進(jìn)行設(shè)備連接。


-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{

NSLog(@"當(dāng)掃描到設(shè)備:%@",peripheral.name);

//這里自己去設(shè)置下連接規(guī)則,這里就是不同廠商特定設(shè)備鏈接的判斷規(guī)則。L是我喜歡的字母。

//? ? if ([peripheral.name hasPrefix:@"L"]){

/*

一個(gè)主設(shè)備最多能連7個(gè)外設(shè),每個(gè)外設(shè)最多只能給一個(gè)主設(shè)備連接,連接成功,失敗,斷開(kāi)會(huì)進(jìn)入各自的委托

//這個(gè)沒(méi)有驗(yàn)證過(guò)。沒(méi)有那么多測(cè)試設(shè)備。。。。

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托

- (void)centra`lManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開(kāi)外設(shè)的委托

*/

//找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!!(錯(cuò)誤之一)

[discoverPeripherals addObject:peripheral];

[central connectPeripheral:peripheral options:nil];

//? ? }

}

//連接到Peripherals-成功(其他操作不是我們想要的,自己做相關(guān)處理)

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{? ? NSLog(@">>>連接到名稱(chēng)為(%@)的設(shè)備-成功",peripheral.name);? ? //設(shè)置的peripheral委托CBPeripheralDelegate? ? //@interface ViewController : UIViewController[peripheral setDelegate:self];

//掃描外設(shè)Services,成功后會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{

[peripheral discoverServices:nil];

}


4.接下來(lái)是發(fā)現(xiàn)設(shè)備的服務(wù),對(duì)相關(guān)服務(wù)中的特性進(jìn)行操作。


//掃描到Services

-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{

//? NSLog(@">>>掃描到服務(wù):%@",peripheral.services);

if (error)

{

NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);

return;

}

for (CBService *service in peripheral.services) {

NSLog(@"%@",service.UUID);

//掃描每個(gè)service的Characteristics,掃描到后會(huì)進(jìn)入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error

[peripheral discoverCharacteristics:nil forService:service];

}

}

//掃描到Characteristics

-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{

if (error)

{

NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);

return;

}

for (CBCharacteristic *characteristic in service.characteristics)

{

NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);

}

//獲取Characteristic的值,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

for (CBCharacteristic *characteristic in service.characteristics){

{

[peripheral readValueForCharacteristic:characteristic];

}

}

//搜索Characteristic的Descriptors,讀到數(shù)據(jù)會(huì)進(jìn)入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

for (CBCharacteristic *characteristic in service.characteristics){

[peripheral discoverDescriptorsForCharacteristic:characteristic];

}

}

//獲取的charateristic的值

-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

//打印出characteristic的UUID和值

//!注意,value的類(lèi)型是NSData,具體開(kāi)發(fā)時(shí),會(huì)根據(jù)外設(shè)協(xié)議制定的方式去解析數(shù)據(jù)

NSLog(@"characteristic uuid:%@? value:%@",characteristic.UUID,characteristic.value);

}

//搜索到Characteristic的Descriptors

-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

//打印出Characteristic和他的Descriptors

NSLog(@"characteristic uuid:%@",characteristic.UUID);

for (CBDescriptor *d in characteristic.descriptors) {

NSLog(@"Descriptor uuid:%@",d.UUID);

}

}

//獲取到Descriptors的值

-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{

//打印出DescriptorsUUID 和value

//這個(gè)descriptor都是對(duì)于characteristic的描述,一般都是字符串,所以這里我們轉(zhuǎn)換成字符串去解析

NSLog(@"characteristic uuid:%@? value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);

}


5.解析到這些數(shù)據(jù),就可以滿(mǎn)足日常計(jì)步,心率等一系列只讀數(shù)據(jù)的操作。接下來(lái)是向外設(shè)寫(xiě)數(shù)據(jù)。


//寫(xiě)數(shù)據(jù)

-(void)writeCharacteristic:(CBPeripheral *)peripheral

characteristic:(CBCharacteristic *)characteristic

value:(NSData *)value{

//打印出 characteristic 的權(quán)限,可以看到有很多種,這是一個(gè)NS_OPTIONS,就是可以同時(shí)用于好幾個(gè)值,常見(jiàn)的有read,write,notify,indicate,知道這幾個(gè)基本就夠用了,前幾個(gè)是讀寫(xiě)權(quán)限,后兩個(gè)都是通知,兩種不同的通知方式。

/*

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {

CBCharacteristicPropertyBroadcast = 0x01,

CBCharacteristicPropertyRead = 0x02,

CBCharacteristicPropertyWriteWithoutResponse = 0x04,

CBCharacteristicPropertyWrite = 0x08,

CBCharacteristicPropertyNotify = 0x10,

CBCharacteristicPropertyIndicate = 0x20,

CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,

CBCharacteristicPropertyExtendedProperties = 0x80,

CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,

CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200

};

*/

NSLog(@"%lu", (unsigned long)characteristic.properties);

//只有 characteristic.properties 有write的權(quán)限才可以寫(xiě)

if(characteristic.properties & CBCharacteristicPropertyWrite){

/*

最好一個(gè)type參數(shù)可以為CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,區(qū)別是是否會(huì)有反饋

*/

[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];

}else{

NSLog(@"該字段不可寫(xiě)!");

}

}

//訂閱外設(shè)特性的通知。

//設(shè)置通知,數(shù)據(jù)通知會(huì)進(jìn)入:didUpdateValueForCharacteristic方法

[peripheral setNotifyValue:YES forCharacteristic:characteristic];

//取消通知

[peripheral setNotifyValue:NO forCharacteristic:characteristic];

//設(shè)置通知和取消通知均可以觸發(fā)此方法。

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;


6.操作完成 或者電量不足要進(jìn)行停止掃描斷開(kāi)設(shè)備操作。


//停止掃描并斷開(kāi)連接

-(void)disconnectPeripheral:(CBCentralManager *)centralManager

peripheral:(CBPeripheral *)peripheral{

//停止掃描

[centralManager stopScan];

//斷開(kāi)連接

[centralManager cancelPeripheralConnection:peripheral];

}


本來(lái)藍(lán)牙開(kāi)發(fā)就需要硬件工程師定義好每個(gè)硬件對(duì)應(yīng)的服務(wù),每個(gè)服務(wù)的描述以及服務(wù)特性的屬性。所以開(kāi)發(fā)中的代碼一般都已經(jīng)定義好了。最重要的就是了解各種方法是什么作用。在各自開(kāi)發(fā)的過(guò)程中是否必須用到。為了讓大家有個(gè)跟更加進(jìn)一步的了解。接下來(lái)我再說(shuō)一下服務(wù)特性的具體讀寫(xiě)與訂閱。當(dāng)然我也是搜索了很多資料。借鑒了很多大神的資源。當(dāng)時(shí)在github上下載了好多東西。具體是哪位大神寫(xiě)的也不曉得。反正很崇拜這些人。。。O(∩_∩)O哈哈哈~

我們看一下外設(shè)管理類(lèi)

以上寫(xiě)的是centralManager的內(nèi)容接下來(lái)是peripheralManager。

1.初始化外設(shè)管理者


//外設(shè)管理者

CBPeripheralManager *_peripheralManager;

//首先初始化外設(shè)管理者

peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];(默認(rèn)為主隊(duì)列)

//在你用過(guò)之后記得停止廣播

//[peripheralManager stopAdvertising];


2.配置藍(lán)牙讀寫(xiě)的屬性以及描述


//注意此方法是在保證外設(shè)在CBPeripheralManagerStatePoweredOn狀態(tài)下才可調(diào)用***********

-(void)initService{

//characteristics字段描述

CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];

/*

可以給主設(shè)備發(fā)送通知的特性?

properties:CBCharacteristicPropertyNotify

許可特性可讀

permissions CBAttributePermissionsReadable

*/

CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];

/*

可讀寫(xiě)的特性

properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead

permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable

*/

CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];

//設(shè)置description

CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];

[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];

/*

只讀的Characteristic

properties:CBCharacteristicPropertyRead

permissions CBAttributePermissionsReadable

*/

CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];

//serviceOne初始化并加入兩個(gè)characteristics

CBMutableService *serviceOne = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];

NSLog(@"%@",serviceOne.UUID);

[serviceOne setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];

//serviceTwo初始化并加入一個(gè)characteristics

CBMutableService *serviceTwo = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];

[serviceTwo setCharacteristics:@[readCharacteristic]];

//添加后就會(huì)調(diào)用代理的

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error

[peripheralManager addService:serviceOne];

[peripheralManager addService:serviceTwo];

}


3.外設(shè)管理者相關(guān)的代理方法介紹


//perihpheral添加了service就調(diào)用此方法

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{

if (!error) {

//兩次都添加完成后才去發(fā)送廣播

//添加服務(wù)后可以在此向外界發(fā)出通告 調(diào)用完這個(gè)方法后會(huì)調(diào)用代理的

//-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error

//開(kāi)始發(fā)送廣播

[peripheralManager startAdvertising:@{

CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],

CBAdvertisementDataLocalNameKey : LocalNameKey

}

];

}

}


//peripheral開(kāi)始發(fā)送advertising

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{

//在這里做一些發(fā)送廣播時(shí)需要的記錄或者動(dòng)作。

}

//This method is invoked when a central configurescharacteristicto notify or indicate. *? ? ? ? ? ? ? ? ? ? ? ? ? It should be used as a cue to start sending updates as the characteristic value changes.

//當(dāng)特性的值發(fā)生改變就會(huì)調(diào)用這個(gè)方法

//訂閱characteristics

-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{

NSLog(@"訂閱了 %@的數(shù)據(jù)",characteristic.UUID);

//每秒執(zhí)行一次給主設(shè)備發(fā)送一個(gè)當(dāng)前時(shí)間的秒數(shù)(這里就是最主要的發(fā)送數(shù)據(jù)的方法)

timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendDate:) userInfo:characteristic? repeats:YES];

}

//發(fā)送數(shù)據(jù),發(fā)送當(dāng)前時(shí)間的秒數(shù)(這里自己定制)

-(BOOL)sendData:(NSTimer *)t {

CBMutableCharacteristic *characteristic = t.userInfo;

NSDateFormatter *dft = [[NSDateFormatter alloc]init];

[dft setDateFormat:@"ss"];

NSLog(@"%@",[dft stringFromDate:[NSDate date]]);

//執(zhí)行回應(yīng)Central通知數(shù)據(jù)

return? [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];

}

//讀characteristics請(qǐng)求

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{

NSLog(@"didReceiveReadRequest");

//判斷是否有讀數(shù)據(jù)的權(quán)限

if (request.characteristic.properties & CBCharacteristicPropertyRead) {

NSData *data = request.characteristic.value;

[request setValue:data];

//對(duì)請(qǐng)求作出成功響應(yīng)

[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];

}else{

[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];

}

}

//寫(xiě)characteristics請(qǐng)求

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{

NSLog(@"didReceiveWriteRequests");

CBATTRequest *request = requests[0];

//判斷是否有寫(xiě)數(shù)據(jù)的權(quán)限

if (request.characteristic.properties & CBCharacteristicPropertyWrite) {

//需要轉(zhuǎn)換成CBMutableCharacteristic對(duì)象才能進(jìn)行寫(xiě)值

CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;

c.value = request.value;

[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];

}else{

[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];

}

}


至此藍(lán)牙的整個(gè)連接發(fā)送數(shù)據(jù)的過(guò)程也就完成了。蘋(píng)果的文檔中也介紹的很清楚。如果你覺(jué)得某種情況需要操作但是本文沒(méi)有寫(xiě)到的。可以看看蘋(píng)果的文檔。也許你會(huì)發(fā)現(xiàn)更多利用起來(lái)很方便的方法。

學(xué)習(xí)新知識(shí)最好站在巨人的肩膀上。這樣可以少走彎路。感謝那些大公無(wú)私的為大家提供學(xué)習(xí)知識(shí)的大牛。你覺(jué)得挺不錯(cuò)可以給個(gè)喜歡喲。動(dòng)動(dòng)手指關(guān)注我也是可以的,做個(gè)好基友也是可以的,玩?zhèn)€lol也是可以的,做人呢最重要是開(kāi)心。。。哈哈哈哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容