HarmonyOS APP性能優(yōu)化之提升應(yīng)用響應(yīng)速度

應(yīng)用對用戶的輸入需要快速反饋峦睡,以提升交互體驗(yàn),因此本文提供了以下方法來提升應(yīng)用響應(yīng)速度青灼。

  • 避免主線程被非UI任務(wù)阻塞
  • 減少組件刷新的數(shù)量

避免主線程被非UI任務(wù)阻塞

在應(yīng)用響應(yīng)用戶輸入期間卷仑,應(yīng)用主線程應(yīng)盡可能只執(zhí)行UI任務(wù)(待顯示數(shù)據(jù)的準(zhǔn)備孽尽、可見視圖組件的更新等)于游,非UI的耗時(shí)任務(wù)(長時(shí)間加載的內(nèi)容等)建議通過異步任務(wù)延遲處理或者分配到其他線程處理毁葱。

使用組件異步加載特性

當(dāng)前系統(tǒng)提供的Image組件默認(rèn)生效異步加載特性,當(dāng)應(yīng)用在頁面上展示一批本地圖片的時(shí)候贰剥,會先顯示空白占位塊倾剿,當(dāng)圖片在其他線程加載完畢后,再替換占位塊蚌成。這樣圖片加載就可以不阻塞頁面的顯示前痘,給用戶帶來良好的交互體驗(yàn)。因此担忧,只在加載圖片耗時(shí)比較短的情況下建議下述代碼芹缔。

@Entry
@Component
struct ImageExample1 {
  build() {
    Column() {
      Row() {
        Image('resources/base/media/sss001.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
        Image('resources/base/media/sss002.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
        Image('resources/base/media/sss003.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
        Image('resources/base/media/sss004.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
      }
    // 此處省略若干個(gè)Row容器,每個(gè)容器內(nèi)都包含如上的若干Image組件
    }
  }
}

建議:在加載圖片的耗時(shí)比較短的時(shí)候涵妥,通過異步加載的效果會大打折扣乖菱,建議配置Image的syncLoad屬性坡锡。

@Entry
@Component
struct ImageExample2 {
  build() {
    Column() {
      Row() {
        Image('resources/base/media/sss001.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
        Image('resources/base/media/sss002.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
        Image('resources/base/media/sss003.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
        Image('resources/base/media/sss004.jpg')
          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
      }
    // 此處省略若干個(gè)Row容器蓬网,每個(gè)容器內(nèi)都包含如上的若干Image組件
    }
  }
}

使用TaskPool線程池異步處理

當(dāng)前系統(tǒng)提供了TaskPool線程池,相比worker線程鹉勒,TaskPool提供了任務(wù)優(yōu)先級設(shè)置帆锋、線程池自動(dòng)管理機(jī)制,示例如下:

import taskpool from '@ohos.taskpool';

@Concurrent
function computeTask(arr: string[]): string[] {
  // 模擬一個(gè)計(jì)算密集型任務(wù)
  let count = 0;
  while (count < 100000000) {
    count++;
  }
  return arr.reverse();
}

@Entry
@Component
struct AspectRatioExample3 {
  @State children: string[] = ['1', '2', '3', '4', '5', '6'];

  aboutToAppear() {
    this.computeTaskInTaskPool();
  }

  async computeTaskInTaskPool() {
    const param = this.children.slice();
    let task = new taskpool.Task(computeTask, param);
    await taskpool.execute(task);
  }

  build() {
    // 組件布局
  }
}

創(chuàng)建異步任務(wù)

以下代碼展示了將一個(gè)長時(shí)間執(zhí)行的非UI任務(wù)通過Promise聲明成異步任務(wù)禽额,主線程可以先進(jìn)行用戶反饋-繪制初始頁面锯厢。等主線程空閑時(shí)皮官,再執(zhí)行異步任務(wù)。等到異步任務(wù)運(yùn)行完畢后实辑,重繪相關(guān)組件刷新頁面捺氢。

@Entry
@Component
struct AspectRatioExample4 {
  @State private children: string[] = ['1', '2', '3', '4', '5', '6'];
  private count: number = 0;

  aboutToAppear() {
    this.computeTaskAsync();  // 調(diào)用異步運(yùn)算函數(shù)
  }

  // 模擬一個(gè)計(jì)算密集型任務(wù)
  computeTask() {
    this.count = 0;
    while (this.count < 100000000) {
      this.count++;
    }
    this.children = this.children.reverse();
  }

  computeTaskAsync() {
    setTimeout(() => { // 這里使用setTimeout來實(shí)現(xiàn)異步延遲運(yùn)行
      this.computeTask();
    }, 1000)
  }

  build() {
    // 組件布局
  }
}

減少刷新的組件數(shù)量

應(yīng)用刷新頁面時(shí)需要盡可能減少刷新的組件數(shù)量,如果數(shù)量過多會導(dǎo)致主線程執(zhí)行測量剪撬、布局的耗時(shí)過長摄乒,還會在自定義組件新建和銷毀過程中,多次調(diào)用aboutToAppear()残黑、aboutToDisappear()方法馍佑,增加主線程負(fù)載。

使用容器限制刷新范圍

反例:如果容器內(nèi)有組件被if條件包含梨水,if條件結(jié)果變更會觸發(fā)創(chuàng)建和銷毀該組件拭荤,如果此時(shí)影響到容器的布局,該容器內(nèi)所有組件都會刷新疫诽,導(dǎo)致主線程UI刷新耗時(shí)過長舅世。

以下代碼的Text('New Page')組件被狀態(tài)變量isVisible控制,isVisible為true時(shí)創(chuàng)建奇徒,false時(shí)銷毀歇终。當(dāng)isVisible發(fā)生變化時(shí),Stack容器內(nèi)的所有組件都會刷新:

@Entry
@Component
struct StackExample5 {
  @State isVisible : boolean = false;

  build() {
    Column() {
      Stack({alignContent: Alignment.Top}) {
        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
          .align(Alignment.Center).textAlign(TextAlign.Center);

        // 此處省略100個(gè)相同的背景Text組件

        if (this.isVisible) {
          Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
            .align(Alignment.Center).textAlign(TextAlign.Center);
        }
      }
      Button("press").onClick(() => {
        this.isVisible = !(this.isVisible);
      })
    }
  }
}

建議:對于這種受狀態(tài)變量控制的組件逼龟,在if外套一層容器评凝,減少刷新范圍。

@Entry
@Component
struct StackExample6 {
  @State isVisible : boolean = false;

  build() {
    Column() {
      Stack({alignContent: Alignment.Top}) {
        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
          .align(Alignment.Center).textAlign(TextAlign.Center);

        // 此處省略100個(gè)相同的背景Text組件

        Stack() {
          if (this.isVisible) {
            Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
              .align(Alignment.Center).textAlign(TextAlign.Center);
          }
        }.width('100%').height('70%')
      }
      Button("press").onClick(() => {
        this.isVisible = !(this.isVisible);
      })
    }
  }
}

按需加載列表組件的元素

反例:this.arr中的每一項(xiàng)元素都被初始化和加載腺律,數(shù)組中的元素有10000個(gè)奕短,主線程執(zhí)行耗時(shí)長。

@Entry
@Component
struct MyComponent7 {
  @State arr: number[] = Array.from(Array<number>(10000), (v,k) =>k); 
  build() {
    List() {
      ForEach(this.arr, (item: number) => {
        ListItem() {
          Text(`item value: ${item}`)
        }
      }, (item: number) => item.toString())
    }
  }
}

建議:這種情況下用LazyForEach替換ForEach匀钧,LazyForEach一般只加載可見的元素翎碑,避免一次性初始化和加載所有元素。

class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = []

  public totalCount(): number {
    return 0
  }

  public getData(index: number): string {
    return ''
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener')
      this.listeners.push(listener)
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener')
      this.listeners.splice(pos, 1)
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded()
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index)
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index)
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index)
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to)
    })
  }
}

class MyDataSource extends BasicDataSource {
  private dataArray: string[] = Array.from(Array<number>(10000), (v, k) => k.toString());

  public totalCount(): number {
    return this.dataArray.length
  }

  public getData(index: number): string  {
    return this.dataArray[index]
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data)
    this.notifyDataAdd(index)
  }

  public pushData(data: string): void {
    this.dataArray.push(data)
    this.notifyDataAdd(this.dataArray.length - 1)
  }
}

@Entry
@Component
struct MyComponent {
  private data: MyDataSource = new MyDataSource()

  build() {
    List() {
      LazyForEach(this.data, (item: string) => {
        ListItem() {
            Text(item).fontSize(20).margin({ left: 10 })
        }
      }, (item:string) => item)
    }
  }
}

合理使用緩存提升響應(yīng)速度

緩存可以存儲經(jīng)常訪問的數(shù)據(jù)或資源之斯,當(dāng)下次需要訪問相同數(shù)據(jù)時(shí)日杈,可以直接從緩存中獲取,避免了重復(fù)的計(jì)算或請求佑刷,從而加快了響應(yīng)速度莉擒。

使用AVPlayer實(shí)例緩存提升視頻加載速度

AVPlayer實(shí)例的創(chuàng)建與銷毀都很消耗性能,針對這個(gè)問題可以使用實(shí)例緩存進(jìn)行優(yōu)化瘫絮,首次加載頁面時(shí)創(chuàng)建兩個(gè)實(shí)例涨冀,在打開新頁面時(shí)切換空閑實(shí)例,通過reset方法重置實(shí)例到初始化狀態(tài)麦萤。優(yōu)化點(diǎn)在于不需要頻繁創(chuàng)建銷毀實(shí)例鹿鳖,且reset方法性能優(yōu)于release方法扁眯。下面以AVPlayer為例列出正反例對比供參考。

反例:打開新頁面時(shí)創(chuàng)建實(shí)例翅帜,離開頁面時(shí)使用release方法銷毀實(shí)例姻檀。

import media from '@ohos.multimedia.media';

@Entry
@Component
struct Index {
  private avPlayer: media.AVPlayer | undefined = undefined;
  
  aboutToAppear(): void {
    // 頁面創(chuàng)建時(shí)初始化AVPlayer實(shí)例
    media.createAVPlayer().then((ret) => {
      this.avPlayer = ret;
    });
  }
  
  aboutToDisappear(): void {
    // 離開頁面時(shí)銷毀AVPlayer實(shí)例
    if (this.avPlayer) {
      this.avPlayer.release();
    }
    this.avPlayer = undefined;
  }
  
  build() {
    // 組件布局
  }
}

正例:首次加載頁面時(shí)維護(hù)兩個(gè)實(shí)例,在切換頁面時(shí)切換實(shí)例涝滴,并將之前的實(shí)例通過reset方法重置施敢。

import media from '@ohos.multimedia.media';

@Entry
@Component
struct Index {
  private avPlayer: media.AVPlayer | undefined = undefined;
  private avPlayerManager: AVPlayerManager = AVPlayerManager.getInstance();

  aboutToAppear(): void {
    this.avPlayerManager.switchPlayer();
    this.avPlayer = this.avPlayerManager.getCurrentPlayer();
  }

  aboutToDisappear(): void {
    this.avPlayerManager.resetCurrentPlayer();
    this.avPlayer = undefined;
  }

  build() {
    // 組件布局
  }
}

class AVPlayerManager {
  private static instance?: AVPlayerManager;

  private player1?: media.AVPlayer;
  private player2?: media.AVPlayer;
  private currentPlayer?: media.AVPlayer;

  public static getInstance(): AVPlayerManager {
    if (!AVPlayerManager.instance) {
      AVPlayerManager.instance = new AVPlayerManager();
    }
    return AVPlayerManager.instance;
  }

  async AVPlayerManager() {
    this.player1 = await media.createAVPlayer();
    this.player2 = await media.createAVPlayer();
  }

  /**
   * 切換頁面時(shí)切換AVPlayer實(shí)例
   */
  switchPlayer(): void {
    if (this.currentPlayer === this.player1) {
      this.currentPlayer = this.player2;
    } else {
      this.currentPlayer = this.player1;
    }
  }

  getCurrentPlayer(): media.AVPlayer | undefined {
    return this.currentPlayer;
  }

  /**
   * 使用reset方法重置AVPlayer實(shí)例
   */
  resetCurrentPlayer(): void {
    this.currentPlayer?.pause(() => {
      this.currentPlayer?.reset();
    });
  }
}

合理使用預(yù)加載提升響應(yīng)速度

使用NodeContainer提前渲染降低響應(yīng)時(shí)延

應(yīng)用啟動(dòng)時(shí)有廣告頁的場景下。如果先渲染廣告頁而后再渲染首頁狭莱,很可能造成首頁響應(yīng)時(shí)延較長僵娃,影響用戶體驗(yàn)。針對此類問題可以使用NodeContainer在廣告頁渲染時(shí)同步渲染首頁腋妙,等到跳轉(zhuǎn)到首頁時(shí)直接送顯默怨,提高響應(yīng)速度。

反例:按次序依次渲染送顯

主要代碼邏輯如下:

1骤素、模擬廣告頁匙睹,通過點(diǎn)擊不同按鈕分別進(jìn)入普通頁面和預(yù)加載頁面

// Index.ets

import router from '@ohos.router';

@Entry
@Component
struct Index {
  build() {
    Column({ space: 5 }) {
      // 進(jìn)入普通頁面
      Button("普通頁面")
        .type(ButtonType.Capsule)
        .onClick(() => {
          router.pushUrl({ url: 'pages/CommonPage' })
        })
      // 進(jìn)入預(yù)加載頁面
      Button("預(yù)加載頁面")
        .type(ButtonType.Capsule)
        .onClick(() => {
          router.pushUrl({ url: 'pages/PreloadedPage' })
        })
    }.height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

2、普通首頁济竹,也即按順序普通渲染的頁面

// CommonPage.ets

import { MyBuilder, getNumbers } from '../builder/CustomerBuilder';

@Entry
@Component
struct CommonPage {
  build() {
    Row() {
      MyBuilder(getNumbers())
    }
  }
}

3痕檬、自定義builder,用來定制頁面結(jié)構(gòu)

// CustomerBuilder.ets

@Builder
export function MyBuilder(numbers: string[]) {
  Column() {
    List({ space: 20, initialIndex: 0 }) {
      ForEach(numbers, (item: string) => {
        ListItem() {
          Text('' + item)
            .width('100%')
            .height(50)
            .fontSize(16)
            .textAlign(TextAlign.Center)
            .borderRadius(10)
            .backgroundColor(0xFFFFFF)
        }
      }, (day: string) => day)
    }
    .listDirection(Axis.Vertical) // 排列方向
    .scrollBar(BarState.Off)
    .friction(0.6)
    .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之間的分界線
    .edgeEffect(EdgeEffect.Spring) // 邊緣效果設(shè)置為Spring
    .width('90%')
    .height('100%')
  }
  .width('100%')
  .height('100%')
  .backgroundColor(0xDCDCDC)
  .padding({ top: 5 })
}

export const getNumbers = (): string[] => {
  const numbers: string[] = [];
  for (let i = 0; i < 100; i++) {
    numbers.push('' + i)
  }
  return numbers;
}

正例:在啟動(dòng)時(shí)預(yù)加載首頁

主要代碼邏輯如下:

1送浊、應(yīng)用啟動(dòng)時(shí)提前創(chuàng)建首頁

// EntryAbility.ets  

import { ControllerManager } from '../builder/CustomerController';
import { getNumbers } from '../builder/CustomerBuilder';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
    window.getLastWindow(this.context, (err: BusinessError, data) => {
      if (err.code) {
        console.error('Failed to obtain top window. Cause:' + JSON.stringify(err));
        return;
      }
      // 提前創(chuàng)建
      ControllerManager.getInstance().createNode(data.getUIContext(), getNumbers());
    })
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
    // 清空組件梦谜,防止內(nèi)存泄漏
    ControllerManager.getInstance().clearNode();
  }
}

2、預(yù)加載的首頁袭景,使用NodeContainer進(jìn)行占位唁桩,當(dāng)跳轉(zhuǎn)到本頁時(shí)直接將提前創(chuàng)建完成的首頁填充

// PreloadedPage.ets

import { ControllerManager } from '../builder/CustomerController';

@Entry
@Component
struct PreloadedPage {
  build() {
    Row() {
      NodeContainer(ControllerManager.getInstance().getNode())
    }
  }
}

3、自定義NodeController耸棒,并提供提前創(chuàng)建首頁的能力

// CustomerController.ets

import { UIContext } from '@ohos.arkui.UIContext';
import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node";
import { MyBuilder } from './CustomerBuilder';

export class MyNodeController extends NodeController {
  private rootNode: BuilderNode<[string[]]> | null = null;
  private wrapBuilder: WrappedBuilder<[string[]]> = wrapBuilder(MyBuilder);
  private numbers: string[] | null = null;

  constructor(numbers: string[]) {
    super();
    this.numbers = numbers;
  }

  makeNode(uiContext: UIContext): FrameNode | null {
    if (this.rootNode != null) {
      // 返回FrameNode節(jié)點(diǎn)
      return this.rootNode.getFrameNode();
    }
    // 返回null控制動(dòng)態(tài)組件脫離綁定節(jié)點(diǎn)
    return null;
  }

  // 通過UIContext初始化BuilderNode荒澡,再通過BuilderNode中的build接口初始化@Builder中的內(nèi)容
  initNode(uiContext: UIContext) {
    if (this.rootNode != null) {
      return;
    }
    // 創(chuàng)建節(jié)點(diǎn),需要uiContext
    this.rootNode = new BuilderNode(uiContext)
    // 創(chuàng)建組件
    this.rootNode.build(this.wrapBuilder, this.numbers)
  }
}

export class ControllerManager {
  private static instance?: ControllerManager;
  private myNodeController?: MyNodeController;

  static getInstance(): ControllerManager {
    if (!ControllerManager.instance) {
      ControllerManager.instance = new ControllerManager();
    }
    return ControllerManager.instance;
  }

  /**
   * 初始化需要UIContext 需在Ability獲取
   * @param uiContext
   * @param numbers
   */
  createNode(uiContext: UIContext, numbers: string[]) {
    // 創(chuàng)建NodeController
    this.myNodeController = new MyNodeController(numbers);
    this.myNodeController.initNode(uiContext);
  }

  /**
   * 自定義獲取NodeController實(shí)例接口
   * @returns MyNodeController
   */
  getNode(): MyNodeController | undefined {
    return this.myNodeController;
  }

  /**
   * 解除占用与殃,防止內(nèi)存泄漏
   */
  clearNode(): void {
    this.myNodeController = undefined;
  }
}

通過SmartPerf-Host工具抓取相關(guān)trace進(jìn)行分析首頁響應(yīng)時(shí)延单山,其中主要關(guān)注兩個(gè)trace tag分別是DispatchTouchEvent代表點(diǎn)擊事件和MarshRSTransactionData代表響應(yīng),如下圖所示:

反例響應(yīng)時(shí)延:18.1ms

正例響應(yīng)時(shí)延:9.4ms

由上述對比數(shù)據(jù)即可得出結(jié)論,預(yù)加載首頁能優(yōu)化首頁響應(yīng)時(shí)延幅疼。

寫在最后

如果你覺得這篇內(nèi)容對你還蠻有幫助米奸,我想邀請你幫我三個(gè)小忙

  • 點(diǎn)贊,轉(zhuǎn)發(fā)衣屏,有你們的 『點(diǎn)贊和評論』躏升,才是我創(chuàng)造的動(dòng)力辩棒。
  • 關(guān)注小編狼忱,同時(shí)可以期待后續(xù)文章ing??膨疏,不定期分享原創(chuàng)知識。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點(diǎn)钻弄,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佃却,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子窘俺,更是在濱河造成了極大的恐慌饲帅,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘤泪,死亡現(xiàn)場離奇詭異灶泵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)对途,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門赦邻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人实檀,你說我怎么就攤上這事惶洲。” “怎么了膳犹?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵恬吕,是天一觀的道長。 經(jīng)常有香客問我须床,道長铐料,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任豺旬,我火速辦了婚禮余赢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哈垢。我一直安慰自己妻柒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布耘分。 她就那樣靜靜地躺著举塔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪求泰。 梳的紋絲不亂的頭發(fā)上央渣,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音渴频,去河邊找鬼芽丹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卜朗,可吹牛的內(nèi)容都是我干的拔第。 我是一名探鬼主播咕村,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚊俺!你這毒婦竟也來了懈涛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤泳猬,失蹤者是張志新(化名)和其女友劉穎批钠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體得封,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡埋心,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忙上。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踩窖。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晨横,靈堂內(nèi)的尸體忽然破棺而出洋腮,到底是詐尸還是另有隱情,我是刑警寧澤手形,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布啥供,位于F島的核電站,受9級特大地震影響库糠,放射性物質(zhì)發(fā)生泄漏伙狐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一瞬欧、第九天 我趴在偏房一處隱蔽的房頂上張望贷屎。 院中可真熱鬧,春花似錦艘虎、人聲如沸唉侄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽属划。三九已至,卻和暖如春候生,著一層夾襖步出監(jiān)牢的瞬間同眯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工唯鸭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留须蜗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像明肮,于是被迫代替她去往敵國和親菱农。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

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