HarmonyOS 應用開發(fā)之分布式數(shù)據(jù)對象跨設備數(shù)據(jù)同步

場景介紹

傳統(tǒng)方式下乎婿,設備之間的數(shù)據(jù)同步测僵,需要開發(fā)者完成消息處理邏輯,包括:建立通信鏈接谢翎、消息收發(fā)處理捍靠、錯誤重試沐旨、數(shù)據(jù)沖突解決等操作,工作量非常大剂公。而且設備越多希俩,調(diào)試復雜度也將同步增加。

其實設備之間的狀態(tài)纲辽、消息發(fā)送進度、發(fā)送的數(shù)據(jù)等都是“變量”璃搜。如果這些變量支持“全局”訪問拖吼,那么開發(fā)者跨設備訪問這些變量就能像操作本地變量一樣,從而能夠自動高效这吻、便捷地實現(xiàn)數(shù)據(jù)多端同步吊档。

分布式數(shù)據(jù)對象即實現(xiàn)了對“變量”的“全局”訪問。向應用開發(fā)者提供內(nèi)存對象的創(chuàng)建唾糯、查詢怠硼、刪除、修改移怯、訂閱等基本數(shù)據(jù)對象的管理能力香璃,同時具備分布式能力。為開發(fā)者在分布式應用場景下提供簡單易用的JS接口舟误,輕松實現(xiàn)多設備間同應用的數(shù)據(jù)協(xié)同葡秒,同時設備間可以監(jiān)聽對象的狀態(tài)和數(shù)據(jù)變更。滿足超級終端場景下嵌溢,相同應用多設備間的數(shù)據(jù)對象協(xié)同需求眯牧。與傳統(tǒng)方式相比,分布式數(shù)據(jù)對象大大減少了開發(fā)者的工作量赖草。

基本概念

  • 分布式內(nèi)存數(shù)據(jù)庫
    分布式內(nèi)存數(shù)據(jù)庫將數(shù)據(jù)緩存在內(nèi)存中学少,以便應用獲得更快的數(shù)據(jù)存取速度,不會將數(shù)據(jù)進行持久化秧骑。若數(shù)據(jù)庫關(guān)閉版确,則數(shù)據(jù)不會保留。

  • 分布式數(shù)據(jù)對象
    分布式數(shù)據(jù)對象是一個JS對象型的封裝腿堤。每一個分布式數(shù)據(jù)對象實例會創(chuàng)建一個內(nèi)存數(shù)據(jù)庫中的數(shù)據(jù)表阀坏,每個應用程序創(chuàng)建的內(nèi)存數(shù)據(jù)庫相互隔離,對分布式數(shù)據(jù)對象的“讀取”或“賦值”會自動映射到對應數(shù)據(jù)庫的get/put操作笆檀。

    分布式數(shù)據(jù)對象的生命周期包括以下狀態(tài):

    • 未初始化:未實例化忌堂,或已被銷毀。
    • 本地數(shù)據(jù)對象:已創(chuàng)建對應的數(shù)據(jù)表酗洒,但是還無法進行數(shù)據(jù)同步士修。
    • 分布式數(shù)據(jù)對象:已創(chuàng)建對應的數(shù)據(jù)表枷遂,設備在線且組網(wǎng)內(nèi)設置同樣sessionId的對象數(shù)>=2甜刻,可以跨設備同步數(shù)據(jù)雹嗦。若設備掉線或?qū)essionId置為空芥喇,分布式數(shù)據(jù)對象退化為本地數(shù)據(jù)對象吨拍。

運作機制

圖1 分布式數(shù)據(jù)對象運作機制

分布式數(shù)據(jù)對象生長在分布式內(nèi)存數(shù)據(jù)庫之上程奠,在分布式內(nèi)存數(shù)據(jù)庫上進行了JS對象型的封裝证九,能像操作本地變量一樣操作分布式數(shù)據(jù)對象残制,數(shù)據(jù)的跨設備同步由系統(tǒng)自動完成遭贸。

JS對象型存儲與封裝機制

  • 為每個分布式數(shù)據(jù)對象實例創(chuàng)建一個內(nèi)存數(shù)據(jù)庫雹锣,通過SessionId標識网沾,每個應用程序創(chuàng)建的內(nèi)存數(shù)據(jù)庫相互隔離。

  • 在分布式數(shù)據(jù)對象實例化的時候蕊爵,(遞歸)遍歷對象所有屬性辉哥,使用“Object.defineProperty”定義所有屬性的set和get方法,set和get中分別對應數(shù)據(jù)庫一條記錄的put和get操作攒射,Key對應屬性名醋旦,Value對應屬性值。

  • 在開發(fā)者對分布式數(shù)據(jù)對象進行“讀取”或者“賦值”的時候会放,都會自動調(diào)用到set和get方法饲齐,映射到對應數(shù)據(jù)庫的操作。

表1 分布式數(shù)據(jù)對象和分布式數(shù)據(jù)庫的對應關(guān)系

分布式對象實例 對象實例 屬性名稱 屬性值
分布式內(nèi)存數(shù)據(jù)庫 一個數(shù)據(jù)庫(sessionID標識) 一條數(shù)據(jù)庫記錄的key 一條數(shù)據(jù)庫記錄的value

跨設備同步和數(shù)據(jù)變更通知機制

分布式數(shù)據(jù)對象鸦概,最重要的功能就是對象之間的數(shù)據(jù)同步箩张。可信組網(wǎng)內(nèi)的設備可以在本地創(chuàng)建分布式數(shù)據(jù)對象窗市,并設置sessionID先慷。不同設備上的分布式數(shù)據(jù)對象,通過設置相同的sessionID咨察,建立對象之間的同步關(guān)系论熙。

如下圖所示,設備A和設備B上的“分布式數(shù)據(jù)對象1”摄狱,其sessionID均為session1脓诡,這兩個對象建立了session1的同步關(guān)系。

圖2 對象的同步關(guān)系

一個同步關(guān)系中媒役,一個設備只能有一個對象加入祝谚。比如上圖中,設備A的“分布式數(shù)據(jù)對象1”已經(jīng)加入了session1的同步關(guān)系酣衷,所以設備A的“分布式數(shù)據(jù)對象2”就加入失敗了交惯。

建立同步關(guān)系后,每個Session有一份共享對象數(shù)據(jù)。加入了同一個Session的對象席爽,支持以下操作:

(1)讀取/修改Session中的數(shù)據(jù)意荤。

(2)監(jiān)聽數(shù)據(jù)變更,感知其他設備對共享對象數(shù)據(jù)的修改只锻。

(3)監(jiān)聽狀態(tài)變更玖像,感知其他設備的加入和退出。

同步的最小單位

關(guān)于分布式數(shù)據(jù)對象的數(shù)據(jù)同步齐饮,值得注意的是捐寥,同步的最小單位是“屬性”。比如祖驱,下圖中對象1包含三個屬性:name上真、age和parents。當其中一個屬性變更時羹膳,則數(shù)據(jù)同步時只需同步此變更的屬性。

圖3 數(shù)據(jù)同步視圖

對象持久化緩存機制

分布式對象主要運行在應用程序的進程空間根竿。當調(diào)用分布式對象持久化接口時陵像,通過分布式數(shù)據(jù)庫對對象進行持久化和同步,進程退出后數(shù)據(jù)也不會丟失寇壳。

該場景是分布式對象的擴展場景醒颖,主要用于以下情況:

  • 在設備上創(chuàng)建持久化對象后APP退出,重新打開APP壳炎,創(chuàng)建持久化對象泞歉,加入同一個Session,數(shù)據(jù)可以恢復到APP退出前的數(shù)據(jù)匿辩。

  • 在設備A上創(chuàng)建持久化對象并同步后持久化到設備B后腰耙,A設備的APP退出,設備B打開APP铲球,創(chuàng)建持久化對象挺庞,加入同一個Session,數(shù)據(jù)可以恢復到A設備退出前的數(shù)據(jù)稼病。

資產(chǎn)同步機制

在分布式對象中选侨,可以使用資產(chǎn)類型來描述本地實體資產(chǎn)文件,分布式對象跨設備同步時然走,該文件會和數(shù)據(jù)一起同步到其他設備上援制。當前只支持資產(chǎn)類型,不支持資產(chǎn)類型數(shù)組芍瑞。如需同步多個資產(chǎn)晨仑,可將每個資產(chǎn)作為分布式對象的一個根屬性實現(xiàn)。

融合資產(chǎn)沖突解決機制

當分布式對象中包含的資產(chǎn)和關(guān)系型數(shù)據(jù)庫中包含的資產(chǎn)指向同一個實體資產(chǎn)文件,即兩個資產(chǎn)的Uri相同時寻歧,就會存在沖突掌栅,我們把這種資產(chǎn)稱為融合資產(chǎn)。若想解決融合資產(chǎn)的沖突码泛,需要先進行資產(chǎn)的綁定猾封。當應用退出session后,綁定關(guān)系隨之消失噪珊。

約束限制

  • 不同設備間只有相同bundleName的應用才能直接同步晌缘。

  • 分布式數(shù)據(jù)對象的數(shù)據(jù)同步發(fā)生在同一個應用程序下,且同sessionID之間痢站。

  • 不建議創(chuàng)建過多的分布式數(shù)據(jù)對象磷箕,每個分布式數(shù)據(jù)對象將占用100-150KB內(nèi)存。

  • 每個分布式數(shù)據(jù)對象大小不超過500KB阵难。

  • 設備A修改1KB數(shù)據(jù)岳枷,設備B收到變更通知,50ms內(nèi)完成呜叫。

  • 單個應用程序最多只能創(chuàng)建16個分布式數(shù)據(jù)對象實例空繁。

  • 考慮到性能和用戶體驗,最多不超過3個設備進行數(shù)據(jù)協(xié)同朱庆。

  • 如對復雜類型的數(shù)據(jù)進行修改盛泡,僅支持修改根屬性,暫不支持下級屬性修改娱颊。資產(chǎn)同步機制中傲诵,資產(chǎn)類型的數(shù)據(jù)支持下一級屬性修改。

  • 支持JS接口間的互通箱硕,與其他語言不互通拴竹。

接口說明

以下是分布式對象跨設備數(shù)據(jù)同步功能的相關(guān)接口,大部分為異步接口颅痊。異步接口均有callback和Promise兩種返回形式殖熟,下表均以callback形式為例,更多接口及使用方式請見分布式數(shù)據(jù)對象斑响。

接口名稱 描述
create(context: Context, source: object): DataObject 創(chuàng)建并得到一個分布式數(shù)據(jù)對象實例菱属。
genSessionId(): string 創(chuàng)建一個sessionId,可作為分布式數(shù)據(jù)對象的sessionId舰罚。
setSessionId(sessionId: string, callback: AsyncCallback<void>): void 設置同步的sessionId纽门,當可信組網(wǎng)中有多個設備時,多個設備間的對象如果設置為同一個sessionId营罢,就能自動同步赏陵。
setSessionId(callback: AsyncCallback<void>): void 退出所有已加入的session饼齿。
on(type: 'change', callback: (sessionId: string, fields: Array<string>) => void): void 監(jiān)聽分布式數(shù)據(jù)對象的數(shù)據(jù)變更。
off(type: 'change', callback?: (sessionId: string, fields: Array<string>) => void): void 取消監(jiān)聽分布式數(shù)據(jù)對象的數(shù)據(jù)變更蝙搔。
on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' | 'offline' ) => void): void 監(jiān)聽分布式數(shù)據(jù)對象的上下線缕溉。
off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' |'offline' ) => void): void 取消監(jiān)聽分布式數(shù)據(jù)對象的上下線。
save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void 保存分布式數(shù)據(jù)對象吃型。
revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void 撤回保存的分布式數(shù)據(jù)對象证鸥。
bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback<void>): void 綁定融合資產(chǎn)。

開發(fā)步驟

跨設備數(shù)據(jù)同步

以一次分布式數(shù)據(jù)對象同步為例勤晚,說明開發(fā)步驟枉层。

  1. 導入@ohos.data.distributedDataObject模塊。

    import distributedDataObject from '@ohos.data.distributedDataObject';
    
  2. 請求權(quán)限赐写。

    1. 需要申請ohos.permission.DISTRIBUTED_DATASYNC權(quán)限鸟蜡,配置方式請參見聲明權(quán)限。
    2. 同時需要在應用首次啟動時彈窗向用戶申請授權(quán)挺邀,使用方式請參見向用戶申請授權(quán)揉忘。
  3. 創(chuàng)建并得到一個分布式數(shù)據(jù)對象實例。

    Stage模型示例:

    // 導入模塊
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import UIAbility from '@ohos.app.ability.UIAbility';
    import { BusinessError } from '@ohos.base';
    import window from '@ohos.window';
    
    class ParentObject {
      mother: string
      father: string
    
      constructor(mother: string, father: string) {
        this.mother = mother
        this.father = father
      }
    }
    class SourceObject {
      name: string | undefined
      age: number | undefined
      isVis: boolean | undefined
      parent: Object | undefined
    
      constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
        this.name = name
        this.age = age
        this.isVis = isVis
        this.parent = parent
      }
    }
    
    class EntryAbility extends UIAbility {
      onWindowStageCreate(windowStage: window.WindowStage) {
        let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
        let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
        let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, source);
      }
    }
    

    FA模型示例:

    // 導入模塊
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import featureAbility from '@ohos.ability.featureAbility';
    // 獲取context
    let context = featureAbility.getContext();
    class ParentObject {
      mother: string
      father: string
      constructor(mother: string, father: string) {
        this.mother = mother
        this.father = father
      }
    }
    class SourceObject {
      name: string | undefined
      age: number | undefined
      isVis: boolean | undefined
      parent: ParentObject | undefined
      constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
        this.name = name
        this.age = age
        this.isVis = isVis
        this.parent = parent
      }
    }
    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
    let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
    // 創(chuàng)建對象端铛,該對象包含4個屬性類型:string癌淮、number、boolean和Object
    let localObject: distributedDataObject.DataObject = distributedDataObject.create(context, source);
    
  4. 加入同步組網(wǎng)沦补。同步組網(wǎng)中的數(shù)據(jù)對象分為發(fā)起方和被拉起方。

    // 設備1加入sessionId
    let sessionId: string = '123456';
    
    localObject.setSessionId(sessionId);
    
    // 和設備1協(xié)同的設備2加入同一個session
    
    // 創(chuàng)建對象咪橙,該對象包含4個屬性類型:string夕膀、number、boolean和Object
    let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
    let remoteObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, remoteSource);
    // 收到status上線后remoteObject同步數(shù)據(jù)美侦,即name變成jack产舞,age變成18
    remoteObject.setSessionId(sessionId);
    
  5. 監(jiān)聽對象數(shù)據(jù)變更〔な#可監(jiān)聽對端數(shù)據(jù)的變更易猫,以callback作為變更回調(diào)實例。

    localObject.on("change", (sessionId: string, fields: Array<string>) => {
      console.info("change" + sessionId);
      if (fields != null && fields != undefined) {
        for (let index: number = 0; index < fields.length; index++) {
          console.info(`The element ${localObject[fields[index]]} changed.`);
        }
      }
    });
    
  6. 修改對象屬性具壮,對象屬性支持基本類型(數(shù)字類型准颓、布爾類型、字符串類型)以及復雜類型(數(shù)組棺妓、基本類型嵌套等)攘已。

    localObject["name"] = 'jack1';
    localObject["age"] = 19;
    localObject["isVis"] = false;
    let parentSource1: ParentObject = new ParentObject('jack1 mom', 'jack1 Dad');
    localObject["parent"] = parentSource1;
    

    說明:

    針對復雜類型的數(shù)據(jù)修改,目前僅支持對根屬性的修改怜跑,暫不支持對下級屬性的修改样勃。

// 支持的修改方式
let parentSource1: ParentObject = new ParentObject('mom', 'Dad');
localObject["parent"] = parentSource1;
// 不支持的修改方式
localObject["parent"]["mother"] = 'mom';
  1. 訪問對象。可以通過直接獲取的方式訪問到分布式數(shù)據(jù)對象的屬性峡眶,且該數(shù)據(jù)為組網(wǎng)內(nèi)的最新數(shù)據(jù)剧防。

    console.info(`name:${localObject['name']}`); 
    
  2. 刪除監(jiān)聽數(shù)據(jù)變更”栌#可以指定刪除監(jiān)聽的數(shù)據(jù)變更回調(diào)峭拘;也可以不指定,這將會刪除該分布式數(shù)據(jù)對象的所有數(shù)據(jù)變更回調(diào)搏熄。

    // 刪除變更回調(diào)
    localObject.off('change', (sessionId: string, fields: Array<string>) => {
      console.info("change" + sessionId);
      if (fields != null && fields != undefined) {
        for (let index: number = 0; index < fields.length; index++) {
          console.info("changed !" + fields[index] + " " + localObject[fields[index]]);
        }
      }
    });
    // 刪除所有的變更回調(diào)
    localObject.off('change'); 
    
  3. 監(jiān)聽分布式數(shù)據(jù)對象的上下線棚唆。可以監(jiān)聽對端分布式數(shù)據(jù)對象的上下線心例。

    localObject.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info("status changed " + sessionId + " " + status + " " +  networkId);
      // 業(yè)務處理
    });
    
  4. 保存和撤回已保存的數(shù)據(jù)對象宵凌。

    // 保存數(shù)據(jù)對象,如果應用退出后組網(wǎng)內(nèi)設備需要恢復對象數(shù)據(jù)時調(diào)用
    localObject.save("local").then((result: distributedDataObject.SaveSuccessResponse) => {
      console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to save. Code:${err.code},message:${err.message}`);
    });
    
    // 撤回保存的數(shù)據(jù)對象
    localObject.revokeSave().then((result: distributedDataObject.RevokeSaveSuccessResponse) => {
      console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`);
    });
    
  5. 刪除監(jiān)聽分布式數(shù)據(jù)對象的上下線止后∠贡梗可以指定刪除監(jiān)聽的上下線回調(diào);也可以不指定译株,這將會刪除該分布式數(shù)據(jù)對象的所有上下線回調(diào)瓜喇。

    // 刪除上下線回調(diào)
    localObject.off('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info("status changed " + sessionId + " " + status + " " + networkId);
      // 業(yè)務處理
    });
    // 刪除所有的上下線回調(diào)
    localObject.off('status');
    
  6. 退出同步組網(wǎng)。分布式數(shù)據(jù)對象退出組網(wǎng)后歉糜,本地的數(shù)據(jù)變更對端不會同步乘寒。

    localObject.setSessionId(() => {
      console.info('leave all session.');
    });
    

跨設備資產(chǎn)同步

分布式對象中加入資產(chǎn)類型屬性,可以觸發(fā)資產(chǎn)同步機制匪补,將資產(chǎn)類型屬性所描述的文件同步到其他設備伞辛。持有資產(chǎn)文件的設備為發(fā)起端,得到資產(chǎn)文件的設備為接收端夯缺。

  1. 導入@ohos.data.distributedDataObject@ohos.data.commonType模塊蚤氏。

    import distributedDataObject from '@ohos.data.distributedDataObject';
    import commonType from '@ohos.data.commonType';
    
  2. 請求權(quán)限。

    1. 需要申請ohos.permission.DISTRIBUTED_DATASYNC權(quán)限踊兜。
    2. 同時需要在應用首次啟動時彈窗向用戶申請授權(quán)竿滨。
  3. 發(fā)起端創(chuàng)建包含資產(chǎn)的分布式對象并加入組網(wǎng)。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import type window from '@ohos.window';
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import commonType from '@ohos.data.commonType';
    import type { BusinessError } from '@ohos.base';
    
    class Note {
      title: string | undefined
      text: string | undefined
      attachment: commonType.Asset | undefined
    
      constructor(title: string | undefined, text: string | undefined, attachment: commonType.Asset | undefined) {
        this.title = title;
        this.text = text;
        this.attachment = attachment;
      }
    }
    
    class EntryAbility extends UIAbility {
      onWindowStageCreate(windowStage: window.WindowStage) {
        let attachment: commonType.Asset = {
          name: 'test_img.jpg',
          uri: 'file://com.example.myapplication/data/storage/el2/distributedfiles/dir/test_img.jpg',
          path: '/dir/test_img.jpg',
          createTime: '2024-01-02 10:00:00',
          modifyTime: '2024-01-02 10:00:00',
          size: '5',
          status: commonType.AssetStatus.ASSET_NORMAL
        }
        // 創(chuàng)建一個自定義筆記類型捏境,其中包含一張圖片資產(chǎn)
        let note: Note = new Note('test', "test", attachment);
        let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note);
        localObject.setSessionId('123456');
      }
    }
    
  4. 接收端創(chuàng)建分布式對象并加入組網(wǎng)

    let note: Note = new Note(undefined, undefined, undefined);
    let receiverObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note);
    receiverObject.on('change', (sessionId: string, fields: Array<string>) => {
      if (fields.includes('attachment')) {
        // 接收端監(jiān)聽到資產(chǎn)類型屬性的數(shù)據(jù)變更時于游,代表其所描述的資產(chǎn)文件同步完成
        console.info('attachment synchronization completed');
      }
    });
    receiverObject.setSessionId('123456');
    
  5. 若資產(chǎn)為融合資產(chǎn),可以創(chuàng)建綁定信息垫言,綁定融合資產(chǎn)曙砂,以解決融合資產(chǎn)的沖突。

    const bindInfo: distributedDataObject.BindInfo = {
      storeName: 'notepad',
      tableName: 'note_t',
      primaryKey: {
        'uuid': '00000000-0000-0000-0000-000000000000'
      },
      field: 'attachment',
      assetName: attachment.name
    }
    
    localObject.bindAssetStore('attachment', bindInfo, (err: BusinessError) => {
      if (err) {
        console.error('bindAssetStore failed.');
      }
      console.info('bindAssetStore success.');
    });
    

寫在最后

  • 如果你覺得這篇內(nèi)容對你還蠻有幫助骏掀,我想邀請你幫我三個小忙:
  • 點贊鸠澈,轉(zhuǎn)發(fā)柱告,有你們的 『點贊和評論』,才是我創(chuàng)造的動力笑陈。
  • 關(guān)注小編际度,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識涵妥。
  • 想要獲取更多完整鴻蒙最新學習知識點乖菱,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蓬网,隨后出現(xiàn)的幾起案子窒所,更是在濱河造成了極大的恐慌,老刑警劉巖帆锋,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吵取,死亡現(xiàn)場離奇詭異,居然都是意外死亡锯厢,警方通過查閱死者的電腦和手機皮官,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來实辑,“玉大人捺氢,你說我怎么就攤上這事〖羟耍” “怎么了摄乒?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長残黑。 經(jīng)常有香客問我缺狠,道長,這世上最難降的妖魔是什么萍摊? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮如叼,結(jié)果婚禮上冰木,老公的妹妹穿的比我還像新娘。我一直安慰自己笼恰,他們只是感情好踊沸,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著社证,像睡著了一般逼龟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上追葡,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天腺律,我揣著相機與錄音奕短,去河邊找鬼。 笑死匀钧,一個胖子當著我的面吹牛翎碑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播之斯,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼日杈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佑刷?” 一聲冷哼從身側(cè)響起莉擒,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瘫絮,沒想到半個月后涨冀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡檀何,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年蝇裤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片频鉴。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡栓辜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垛孔,到底是詐尸還是另有隱情藕甩,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布周荐,位于F島的核電站狭莱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏概作。R本人自食惡果不足惜腋妙,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讯榕。 院中可真熱鬧骤素,春花似錦、人聲如沸愚屁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霎槐。三九已至送浊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丘跌,已是汗流浹背袭景。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工唁桩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浴讯。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓朵夏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榆纽。 傳聞我的和親對象是個殘疾皇子仰猖,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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