CoreBluetooth 藍(lán)牙開發(fā)(后臺模式缚柏、狀態(tài)保存與恢復(fù))

最近新進(jìn)一家公司液兽,主要是做物聯(lián)網(wǎng)這一塊的的拂酣,項目需要用到藍(lán)牙開發(fā)奠涌,講真的娃殖,挑戰(zhàn)還是挺大的定庵,做了差不多四年的iOS開發(fā)衰粹,從沒有接觸過藍(lán)牙開發(fā)這一領(lǐng)域锣光,我是這樣學(xué)習(xí)的。

從網(wǎng)上找各種博客(國內(nèi)的铝耻,國外的)誊爹,借鑒別人寫過的Demo以及官方文檔蹬刷,花了整整的一周時間,對iOS的CoreBluetooth這個框架的使用稍微有一些的了解频丘,請聽我一一道來办成;

iOS 藍(lán)牙

簡稱:BLE(buletouch low energy),藍(lán)牙 4.0 設(shè)備因為低耗電搂漠,所以也叫做 BLE迂卢,CoreBluetooth框架就是蘋果公司為我們提供的一個庫,我們可以使用這個庫和其他支持藍(lán)牙4.0的設(shè)備進(jìn)行數(shù)據(jù)交互桐汤。值得注意的是在IOS10之后的APP中而克,我們需要在 info.plist文件中添加NSBluetoothPeripheralUsageDescription字段否則APP會崩潰

工作模式:藍(lán)牙通信中,首先需要提到的就是 central 和 peripheral 兩個概念怔毛。這是設(shè)備在通信過程中扮演的兩種角色员萍。直譯過來就是 [中心] 和 [周邊(可以理解為外設(shè))]。iOS 設(shè)備既可以作為 central拣度,也可以作為 peripheral碎绎,這主要取決于通信需求。

自己嘗試的寫了個Demo,實現(xiàn)的功能有:

1抗果、通過已知外圍設(shè)備的服務(wù)UUID搜索(這個UUID是指被廣播出來的服務(wù)UUID)混卵;
2、連接指定的外圍設(shè)備窖张;
3幕随、獲取指定的服務(wù),發(fā)現(xiàn)需要訂閱的特征宿接;
4赘淮、接收外圍設(shè)備發(fā)送的數(shù)據(jù);
5睦霎、向外圍設(shè)備寫數(shù)據(jù)梢卸;
6、實現(xiàn)藍(lán)牙服務(wù)的后臺模式副女;
7蛤高、實現(xiàn)藍(lán)牙服務(wù)的狀態(tài)保存與恢復(fù)(應(yīng)用被系統(tǒng)殺死的時候,系統(tǒng)會自動保存 central manager 的狀態(tài))碑幅;

中心角色的實現(xiàn):(central)

(1)戴陡、初始化中央管理器對象

/**
第一個參數(shù):代理
第二個參數(shù):隊列(nil為不指定隊列,默認(rèn)為主隊列)
第三個參數(shù):實現(xiàn)狀態(tài)保存的時候需要用到 eg:@{CBCentralManagerOptionRestoreIdentifierKey:@"centralManagerIdentifier"} 
*/  
centerManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:options];

中央管理器會調(diào)用 centralManagerDidUpdateState:通知藍(lán)牙的狀態(tài)

(2)沟涨、發(fā)現(xiàn)外圍設(shè)備

[centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]] options:nil];

每次中央管理器發(fā)現(xiàn)外圍設(shè)備時恤批,它都會調(diào)用centralManager:didDiscoverPeripheral:advertisementData:RSSI:其委托對象的方法。

(3)裹赴、發(fā)現(xiàn)想要的外圍設(shè)備進(jìn)行連接

#pragma mark -- 掃描發(fā)現(xiàn)到任何一臺設(shè)備都會通過這個代理方法回調(diào)
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    //過濾掉無效的結(jié)果
    if (peripheral == nil||peripheral.identifier == nil/*||peripheral.name == nil*/)
    {
        return;
    }
    
    NSString *pername =[NSString stringWithFormat:@"%@",peripheral.name];
    NSLog(@"所有服務(wù)****:%@",peripheral.services);

    NSLog(@"藍(lán)牙名字:%@  信號強(qiáng)弱:%@",pername,RSSI);
   //連接需要的外圍設(shè)備
    [self connectPeripheral:peripheral];
    //將搜索到的設(shè)備添加到列表中
    [self.peripherals addObject:peripheral];
    
    if (_didDiscoverPeripheralBlock) {
        _didDiscoverPeripheralBlock(central,peripheral,advertisementData,RSSI);
    }
}

如果連接請求成功喜庞,則中央管理器調(diào)用centralManager:didConnectPeripheral:其委托對象的方法诀浪。

(4)、發(fā)現(xiàn)所連接的外圍設(shè)備的服務(wù)

#pragma mark -- 連接成功延都、獲取當(dāng)前設(shè)備的服務(wù)和特征 并停止掃描
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"%@",peripheral);
    
    // 設(shè)置設(shè)備代理
    [peripheral setDelegate:self];
    // 大概獲取服務(wù)和特征
    [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
    
    NSLog(@"Peripheral Connected");
    
    if (_centerManager.isScanning) {
        [_centerManager stopScan];
    }
    NSLog(@"Scanning stopped");
    
}

發(fā)現(xiàn)指定的服務(wù)時雷猪,外圍設(shè)備(CBPeripheral你連接的對象)會調(diào)用peripheral:didDiscoverServices:其委托對象的方法。

(5)晰房、發(fā)現(xiàn)服務(wù)的特征

#pragma mark -- 獲取當(dāng)前設(shè)備服務(wù)services
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering services: %@", [error localizedDescription]);
        return;
    }
    NSLog(@"所有的servicesUUID%@",peripheral.services);   
    //遍歷所有service
    for (CBService *service in peripheral.services)
    {
        NSLog(@"服務(wù)%@",service.UUID);
        //找到你需要的servicesuuid
        if ([[NSString stringWithFormat:@"%@",service.UUID] isEqualToString:SERVICE_UUID])
        {
            // 根據(jù)UUID尋找服務(wù)中的特征
            [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service];
        }
    }
}

peripheral:didDiscoverCharacteristicsForService:error:當(dāng)發(fā)現(xiàn)指定服務(wù)的特征時春宣,外圍設(shè)備調(diào)用其委托對象的方法。

(6)嫉你、檢索特征價值

閱讀特征的值 ()

 [peripheral readValueForCharacteristic:interestingCharacteristic];

注意: 并非所有特征都是可讀的月帝。你可以通過檢查其properties屬性是否包含CBCharacteristicPropertyRead常量來確定特征是否可讀。如果嘗試讀取不可讀的特征值幽污,則peripheral:didUpdateValueForCharacteristic:error:委托方法將返回合適的錯誤嚷辅。

訂閱特征的值()

雖然使用該readValueForCharacteristic:方法讀取特征值對靜態(tài)值有效,但它不是檢索動態(tài)值的最有效方法距误。檢索隨時間變化的特征值 - 例如簸搞,你的心率 - 通過訂閱它們。訂閱特征值時准潭,您會在值更改時收到外圍設(shè)備的通知趁俊。

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

注意: 并非所有特征都提供訂閱。你可以通過檢查特性是否properties包含其中一個CBCharacteristicPropertyNotify或多個CBCharacteristicPropertyIndicate常量來確定特征是否提供訂閱刑然。
當(dāng)你訂閱(或取消訂閱)特征的值時寺擂,外圍設(shè)備會調(diào)用peripheral:didUpdateNotificationStateForCharacteristic:error:其委托對象的方法。

寫一個特征的值 ()

有時寫一個特征的值是有意義的泼掠。例如怔软,如果你的應(yīng)用程序與藍(lán)牙低功耗數(shù)字恒溫器交互,你可能需要為恒溫器提供設(shè)置房間溫度的值择镇。如果特征值是可寫的挡逼,則可以NSData通過調(diào)用外設(shè)writeValue:forCharacteristic:type:方法將數(shù)據(jù)值;

[self.discoveredPeripheral writeValue:data forCharacteristic:self.characteristic1 type:CBCharacteristicWriteWithResponse];

寫入特征的值時,指定要執(zhí)行的寫入類型腻豌。在上面的示例中家坎,寫入類型CBCharacteristicWriteWithResponse指示外圍設(shè)備通過調(diào)用peripheral:didWriteValueForCharacteristic:error:其委托對象的方法讓您的應(yīng)用程序知道寫入是否成功。

外圍角色的實現(xiàn)

(1)吝梅、初始化外圍設(shè)備管理器

peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];

創(chuàng)建外圍設(shè)備管理器時虱疏,外圍設(shè)備管理器會調(diào)用peripheralManagerDidUpdateState:其委托對象的方法。您必須實現(xiàn)此委托方法憔涉,以確保支持藍(lán)牙低功耗并可在本地外圍設(shè)備上使用订框。

(2)、設(shè)置服務(wù)和特征

為自定義服務(wù)和特征創(chuàng)建自己的UUID
在終端使用 uuidgen 命令獲取以ASCII字符串形式的128位值的UUID:71DA3FD1-7E10-41C1-B16F-4430B506CDE7

構(gòu)建服務(wù)樹和特征

myCharacteristic =[[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID properties:CBCharacteristicPropertyRead value:myValue permissions:CBAttributePermissionsReadable];   //特征
 myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];    //與特征所關(guān)聯(lián)的服務(wù)

myService.characteristics = @ [myCharacteristic];        //設(shè)置服務(wù)的特征數(shù)組,將特征與其關(guān)聯(lián)

(3)兜叨、發(fā)布服務(wù)和特征

  [peripheralManager addService:myService];

當(dāng)調(diào)用此方法發(fā)布服務(wù)時穿扳,外圍管理器將調(diào)用peripheralManager:didAddService:error:其委托對象的方法。通過error可以知道是否發(fā)布成功;
將服務(wù)及其任何關(guān)聯(lián)特性發(fā)布到外圍設(shè)備的數(shù)據(jù)庫后国旷,該服務(wù)將被緩存矛物,將無法再對其進(jìn)行更改。

(4)跪但、廣播服務(wù)

  [peripheralManager startAdvertising:@ {CBAdvertisementDataServiceUUIDsKey:@[myFirstService.UUID履羞,mySecondService.UUID]}];

當(dāng)開始在本地外圍設(shè)備上公布某些數(shù)據(jù)時,外圍設(shè)備管理器會調(diào)用peripheralManagerDidStartAdvertising:error:其委托對象的方法屡久。

(5)忆首、響應(yīng)來自中央的讀取和寫入請求

當(dāng)連接的中央請求讀取某個特征的值時,外圍管理器會調(diào)用peripheralManager:didReceiveReadRequest:其委托對象的方法被环。

 [peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset]; 

設(shè)置讀取請求不要求從超出特征值的邊界的索引位置讀取

  request.value = [myCharacteristic.value subdataWithRange:NSMakeRange(request.offset糙及,myCharacteristic.value.length  -  request.offset)];  

將請求的特性屬性(默認(rèn)值為nil)的值設(shè)置為您在本地外圍設(shè)備上創(chuàng)建的特征值,同時考慮讀取請求的偏移量

設(shè)置值后筛欢,響應(yīng)遠(yuǎn)程中央以指示請求已成功完成浸锨。通過調(diào)用類的respondToRequest:withResult:方法CBPeripheralManager,傳回請求(其更新的值)和請求的結(jié)果

當(dāng)連接的中心發(fā)送寫入一個或多個特征值的請求時版姑,外圍管理器會調(diào)用peripheralManager:didReceiveWriteRequests:其委托對象的方法

(6)柱搜、將更新的特征值發(fā)送到訂閱的中心

當(dāng)連接的中心訂閱某個特征的值時,外圍管理器會調(diào)用peripheralManager:central:didSubscribeToCharacteristic:其委托對象的方法
獲取特征的更新值剥险,并通過調(diào)用類的updateValue:forCharacteristic:onSubscribedCentrals:方法將其發(fā)送到中心CBPeripheralManager聪蘸。

處理常駐后臺任務(wù)

首先需要在Capabilities-->Background Modes申請中心角色的后臺模式說明

如圖:


中心角色后臺模式.jpg

(1)、狀態(tài)保存與恢復(fù)

因為狀態(tài)的保存和恢復(fù) Core Bluetooth 都為我們封裝好了表制,所以我們只需要選擇是否需要這個特性即可宇姚。系統(tǒng)會保存當(dāng)前 central manager 或 peripheral manager,并且繼續(xù)執(zhí)行藍(lán)牙相關(guān)事件(即使程序已經(jīng)不再運(yùn)行)夫凸。一旦事件執(zhí)行完畢浑劳,系統(tǒng)會在后臺重啟 app,這時你有機(jī)會去存儲當(dāng)前狀態(tài)夭拌,并且處理一些事物魔熏。在之前提到的 “門鎖” 的例子中,系統(tǒng)會監(jiān)視連接請求鸽扁,并在 centralManager:didConnectPeripheral: 回調(diào)時蒜绽,重啟 app,在用戶回家后桶现,連接操作結(jié)束躲雅。

Core Bluetooth 的狀態(tài)保存與恢復(fù)在設(shè)備作為 central、peripheral 或者這兩種角色時骡和,都可用相赁。在設(shè)備作為 central 并添加了狀態(tài)保存與恢復(fù)支持后相寇,如果 app 被強(qiáng)行關(guān)閉進(jìn)程,系統(tǒng)會自動保存 central manager 的狀態(tài)(如果 app 有多個 central manager钮科,你可以選擇哪一個需要系統(tǒng)保存)唤衫。

對于 CBCentralManager,系統(tǒng)會保存以下信息:

central 準(zhǔn)備連接或已經(jīng)連接的 peripheral
central 需要掃描的 service(包括掃描時绵脯,配置的 options)
central 訂閱的 characteristic
對于 peripheral 來說佳励,情況也差不多。系統(tǒng)對 CBPeripheralManager 的處理方式如下:
peripheral 在廣播的數(shù)據(jù)
peripheral 存入的 service 和 characteristic 的樹形結(jié)構(gòu)
已經(jīng)被 central 訂閱了的 characteristic 的值
當(dāng)系統(tǒng)在后臺重新加載程序后(可能是因為找到了要找的 peripheral)蛆挫,你可以重新實例化 central manager 或 peripheral 并恢復(fù)他們的狀態(tài)赃承。

(2)、選擇支持存儲和恢復(fù)

如果要支持存儲和恢復(fù)悴侵,則需要在初始化 manager 的時候給一個 restoration identifier瞧剖。restoration identifier 是 string 類型,并標(biāo)識了 app 中的 central manager 或 peripheral manager畜挨。這個 string 很重要筒繁,它將會告訴 Core Bluetooth 需要存儲狀態(tài),畢竟 Core Bluetooth 恢復(fù)有 identifier 的對象巴元。

例如毡咏,在 central 端,要想支持該特性逮刨,可以在調(diào)用 CBCentralManager 的初始化方法時呕缭,配置 CBCentralManagerOptionRestoreIdentifierKey:

centralManager = [[CBCentralManager alloc] initWithDelegate:self 
queue:nil
options:@{CBCentralManagerOptionRestoreIdentifierKey:@"centralManagerIdentifier"}];

雖然以上代碼沒有展示出來,其實在 peripheral manager 中要設(shè)置 identifier 也是這樣的修己。只是在初始化時恢总,將 key 改成了 CBPeripheralManagerOptionRestoreIdentifierKey。
因為程序可以有多個 CBCentralManager 和 CBPeripheralManager睬愤,所以要確保每個 identifier 都是唯一的片仿。

(3)、重新初始化 central manager 和 peripheral manager

當(dāng)系統(tǒng)重新在后臺加載程序時尤辱,首先需要做的即根據(jù)存儲的 identifier砂豌,重新初始化 central manager 或 peripheral manager。如果你只有一個 manager光督,并且 manager 存在于 app 生命周期中阳距,那這個步驟就不需要做什么了。
.
如果 app 中包含多個 manager结借,或者 manager 不是在整個 app 生命周期中都存在的筐摘,那 app 就必須要區(qū)分你要重新初始化哪個 manager 了。你可以通過從 app delegate 中的 application:didFinishLaunchingWithOptions: 中取出 key(UIApplicationLaunchOptionsBluetoothCentralsKey 或 UIApplicationLaunchOptionsBluetoothPeripheralsKey)中的 value(數(shù)組類型)來得到程序退出之前存儲的 manager identifier 列表:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

NSArray *centralManagerIdentifiers =
    launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
    if (centralManagerIdentifiers.count) {
        //重新初始化所有的 manager 
        for (NSString *identifier in centralManagerIdentifiers) {
            NSLog(@"系統(tǒng)啟動項目");
            //在這里創(chuàng)建的藍(lán)牙實例一定要被當(dāng)前類持有,不然出了這個函數(shù)就被銷毀了咖熟,藍(lán)牙檢測會出現(xiàn)“XPC connection invalid”
            self.bluetooth = [[MSBBlueTooth alloc]initWithQueue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey : identifier}];
            NSLog(@"");
        }
    }

return YES;
}

(4)圃酵、實現(xiàn)恢復(fù)狀態(tài)的代理方法

在重新初始化 manager 之后,接下來需要同步 Core Bluetooth 存儲的他們的狀態(tài)球恤。要想弄清楚在程序被退出時都在做些什么辜昵,就需要正確的實現(xiàn)代理方法荸镊。對于 central manager 來說咽斧,需要實現(xiàn) centralManager:willRestoreState:;對于 peripheral manager 來說躬存,需要實現(xiàn) peripheralManager:willRestoreState:张惹。
.
注意:如果選擇存儲和恢復(fù)狀態(tài),當(dāng)系統(tǒng)在后臺重新加載程序時岭洲,首先調(diào)用的方法是 centralManager:willRestoreState: 或 peripheralManager:willRestoreState:宛逗。如果沒有選擇存儲的恢復(fù)狀態(tài)(或者喚醒時沒有什么內(nèi)容需要恢復(fù)),那么首先調(diào)用的方法是 centralManagerDidUpdateState: 或 peripheralManagerDidUpdateState:盾剩。
.
無論是以上哪種代理方法雷激,最后一個參數(shù)都是一個包含程序退出前狀態(tài)的字典。字典中告私,可用的 key 屎暇,

central 端有:
NSString *const CBCentralManagerRestoredStatePeripheralsKey;
NSString *const CBCentralManagerRestoredStateScanServicesKey;
NSString *const CBCentralManagerRestoredStateScanOptionsKey;

peripheral 端有:
NSString *const CBPeripheralManagerRestoredStateServicesKey;
NSString *const CBPeripheralManagerRestoredStateAdvertisementDataKey;

要恢復(fù) central manager 的狀態(tài),可以用 centralManager:willRestoreState: 返回字典中的 key 來得到驻粟。假如說 central manager 有想要或者已經(jīng)連接的 peripheral根悼,那么可以通過 CBCentralManagerRestoredStatePeripheralsKey 對應(yīng)得到的 peripheral(CBPeripheral 對象)數(shù)組來得到。

- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary *)state {
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
    //講狀態(tài)保存的設(shè)備加入列表蜀撑,在藍(lán)牙檢測狀態(tài)的回調(diào)里實現(xiàn)重連
    self.peripherals = [NSMutableArray arrayWithArray:peripherals];

}

具體要對拿到的 peripheral 數(shù)組做什么就要根據(jù)需求來了挤巡。如果這是個 central manager 搜索到的 peripheral 數(shù)組,那就可以存儲這個數(shù)組的引用酷麦,并且開始建立連接了(注意給這些 peripheral 設(shè)置代理矿卑,否則連接后不會走 peripheral 的代理方法)。
.
恢復(fù) peripheral manager 的狀態(tài)和 central manager 的方式類似沃饶,就只是把代理方法換成了 peripheralManager:willRestoreState:母廷,并且使用對應(yīng)的 key 即可

寫的不是很好,也算是東拼西湊了绍坝,但也是花了時間去整理的徘意,如果看不懂,可以下載我的Demo自己跑一遍轩褐;

想要看實現(xiàn)效果椎咧,可以下載Demo,看的再多也不如項目跑一遍來的快,療效是不騙人的;

有需要的可以加我微信Jarvis-LLL勤讽,一起討論學(xué)習(xí)

喜歡就點(diǎn)個贊蟋座,也可以在下方評論一起討論討論

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市脚牍,隨后出現(xiàn)的幾起案子向臀,更是在濱河造成了極大的恐慌,老刑警劉巖诸狭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件券膀,死亡現(xiàn)場離奇詭異,居然都是意外死亡驯遇,警方通過查閱死者的電腦和手機(jī)芹彬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叉庐,“玉大人舒帮,你說我怎么就攤上這事《傅” “怎么了玩郊?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長枉阵。 經(jīng)常有香客問我译红,道長,這世上最難降的妖魔是什么岭妖? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任临庇,我火速辦了婚禮,結(jié)果婚禮上昵慌,老公的妹妹穿的比我還像新娘假夺。我一直安慰自己,他們只是感情好斋攀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布已卷。 她就那樣靜靜地躺著,像睡著了一般淳蔼。 火紅的嫁衣襯著肌膚如雪侧蘸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天鹉梨,我揣著相機(jī)與錄音讳癌,去河邊找鬼。 笑死存皂,一個胖子當(dāng)著我的面吹牛晌坤,可吹牛的內(nèi)容都是我干的逢艘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼骤菠,長吁一口氣:“原來是場噩夢啊……” “哼它改!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起商乎,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤央拖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鹉戚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲜戒,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年崩瓤,在試婚紗的時候發(fā)現(xiàn)自己被綠了袍啡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踩官。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡却桶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蔗牡,到底是詐尸還是另有隱情颖系,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布辩越,位于F島的核電站嘁扼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黔攒。R本人自食惡果不足惜趁啸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望督惰。 院中可真熱鬧不傅,春花似錦、人聲如沸赏胚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽觉阅。三九已至崖疤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間典勇,已是汗流浹背劫哼。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留割笙,地道東北人权烧。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親豪嚎。 傳聞我的和親對象是個殘疾皇子搔驼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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