實戰(zhàn)鴻蒙,實現(xiàn)一款權(quán)限請求框架

一茧彤、申請權(quán)限的一般步驟

  • 判斷是否有權(quán)限骡显,如果有權(quán)限,直接進(jìn)行下一步。
  • 如果沒有權(quán)限惫谤,則開始申請權(quán)限壁顶。
  • 如果用戶授權(quán),進(jìn)行下一步溜歪。
  • 如果用戶拒絕授權(quán)博助,后面再次申請權(quán)限,系統(tǒng)為了不打擾用戶痹愚,將不會出現(xiàn)系統(tǒng)的權(quán)限彈窗富岳。在用戶拒絕授權(quán)后,需要彈窗提示用戶必須授權(quán)才能訪問當(dāng)前功能拯腮,并引導(dǎo)用戶到系統(tǒng)設(shè)置中打開相應(yīng)的權(quán)限窖式。

每次申請權(quán)限的時候,都需要經(jīng)過以上幾個步驟动壤,當(dāng)申請的權(quán)限越來越多萝喘,大量的重復(fù)代碼就出現(xiàn)了。為了減少重復(fù)代碼琼懊,我封裝了一個權(quán)限請求框架阁簸。

二、權(quán)限請求框架

??桃夭是鴻蒙系統(tǒng)上的一款權(quán)限請求框架哼丈,封裝了權(quán)限請求邏輯启妹,采用鏈?zhǔn)秸{(diào)用的方式請求權(quán)限,極大的簡化了權(quán)限請求的代碼醉旦,同時支持在UI饶米、UIAbilityUIExtensionAbility里面申請權(quán)限车胡。需要注意的是檬输,應(yīng)用在UIExtensionAbility申請授權(quán)時,需要在onWindowStageCreate函數(shù)執(zhí)行結(jié)束后或在onWindowStageCreate函數(shù)回調(diào)中申請權(quán)限匈棘。
??本項目基于開源鴻蒙4.1開發(fā)丧慈,最低兼容到API 11,請將DevEco Studio升級到最新版主卫,DevEco Studio版本低于5.0.3.403可能無法編譯逃默。

三、桃夭名稱來源

??桃夭一詞出自古代第一部詩歌總集《詩經(jīng)》中《詩經(jīng)·桃夭》队秩,“桃之夭夭笑旺,灼灼其華♀勺剩”桃花怒放千萬朵筒主,色彩鮮艷紅似火关噪。

四、桃夭的使用方式

??下載

ohpm install @shijing/taoyao

??申請權(quán)限

TaoYao.with(this)
      .runtime()
      // 要申請的權(quán)限
      .permission(permissions)
      .onGranted(() => {
        // 權(quán)限申請成功
      })
      .onDenied(() => {
        // 權(quán)限申請失敗
      })
      .request()

??申請權(quán)限變得如此之簡單乌妙。

五使兔、實現(xiàn)原理

5、1 如何支持在UI藤韵、UIAbility虐沥、UIExtensionAbility里面申請權(quán)限。

??可以使用聯(lián)合類型泽艘,也可以使用重載欲险。這里通過重載的方式來實現(xiàn)在UIUIAbility匹涮、UIExtensionAbility里面申請權(quán)限天试。

 /**
   * 直接在UIExtensionAbility中申請權(quán)限
   *
   * @param uiAbility
   * @returns
   */
  static with(extensionAbility: UIExtensionAbility): IAccessControl;
  /**
   * 在UI中向用戶申請授權(quán)
   *
   * @param context
   * @returns
   */
  static with(context: common.UIAbilityContext): IAccessControl;

  /**
   * 直接在UIAbility中申請權(quán)限
   *
   * @param uiAbility
   * @returns
   */
  static with(uiAbility: UIAbility): IAccessControl;

  static with(context: common.UIAbilityContext | UIAbility | UIExtensionAbility): IAccessControl {
    if (context instanceof UIAbility) {
      return new AccessControl(new UIAbilityOrigin(context))
    } else if (context instanceof UIExtensionAbility) {
      return new AccessControl(new UIExtensionAbilityOrigin(context))
    } else {
      return new AccessControl(new ContextOrigin(context))
    }
  }

??UIUIAbility然低、UIExtensionAbility里面最重要就是Context對象喜每,申請權(quán)限的時候需要傳入Context對象,我們需要從UI雳攘、UIAbility带兜、UIExtensionAbility里面獲取Context對象。
??這里采用策略模式吨灭。創(chuàng)建接口Origin刚照,Origin代表從哪申請權(quán)限,定義getContext方法沃于,由子類實現(xiàn)該方法涩咖。

/**
 * 需要UI、UIAbility繁莹、UIExtensionAbility申請權(quán)限,同時獲取Context對象特幔。
 */
export interface Origin {

  /**
   * 獲取context對象
   *
   * @returns
   */
  getContext(): Context

}

??ContextOrigin代表在在UI中申請權(quán)限咨演,實現(xiàn)Origin接口,重寫getContext方法蚯斯。

/**
 * 在UI中申請權(quán)限
 */
export class ContextOrigin implements Origin {

  private context: common.UIAbilityContext

  constructor(context: common.UIAbilityContext) {
    this.context = context
  }

  getContext(): Context {
    return this.context
  }
}

??UIAbilityOrigin代表在在UIAbility中申請權(quán)限薄风,同樣實現(xiàn)Origin接口,重寫getContext方法拍嵌。

/**
 * 在UIAbility中申請權(quán)限
 */
export class UIAbilityOrigin implements Origin {

  private uiAbility: UIAbility

  constructor(uiAbility: UIAbility) {
    this.uiAbility = uiAbility
  }

  getContext(): Context {
    return this.uiAbility.context
  }
}

??UIExtensionAbilityOrigin代表在在UIExtensionAbility中申請權(quán)限遭赂,同樣實現(xiàn)Origin接口,重寫getContext方法横辆。

/**
 * 在UIExtensionAbility中申請權(quán)限
 */
export class UIExtensionAbilityOrigin implements Origin {

  private uiExtensionAbility: UIExtensionAbility

  constructor(uiExtensionAbility: UIExtensionAbility) {
    this.uiExtensionAbility = uiExtensionAbility
  }

  getContext(): Context {
    return this.uiExtensionAbility.context
  }

}

5撇他、2 檢測申請的權(quán)限是否在module.json5文件中聲明

??申請的權(quán)限必須在module.json5文件中聲明,否則桃夭會直接拋異常。如何檢測申請的權(quán)限是否在配置文件中聲明困肩?
??如下代碼划纽,通過bundleManager對象獲取應(yīng)用信息,之后就可以獲取應(yīng)用在配置文件中聲明的權(quán)限了锌畸。如果要申請的權(quán)限沒有module.json5文件中聲明勇劣,那就會拋異常。

/**
   * 檢查要申請的權(quán)限是否在module.json5文件中聲明
   *
   * @param permissions 要申請的權(quán)限
   */
  private checkCommonConfig(permissions: Array<Permissions>) {
    const bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
    // 同步獲取在module.json5文件中聲明的所有權(quán)限
    const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags)
    const reqPermissionDetails = bundleInfo.reqPermissionDetails
    if (ArrayUtils.isEmpty(reqPermissionDetails)) {
      throw new Error('請在module.json5文件中聲明權(quán)限')
    }
    const reqPermissions = new ArrayList<string>()
    reqPermissionDetails.forEach(reqPermissionDetail => {
      reqPermissions.add(reqPermissionDetail.name)
    })
    permissions.forEach((permission) => {
      if (!reqPermissions.has(permission)) {
        // 要申請的權(quán)限沒有module.json5文件中聲明
        throw new Error(`請在module.json5文件中聲明${permission}權(quán)限`)
      }
    })
  }

5潭枣、3 檢測其它配置

對于位置權(quán)限比默,有三種情況:
??第一:申請模糊位置權(quán)限,大部分情況下盆犁,不會申請模糊位置權(quán)限命咐,更多的是第二種情況。
??第二:申請精確位置權(quán)限蚣抗。
??第三:申請后臺位置權(quán)限侈百。
針對位置權(quán)限,我們需要額外的配置下翰铡。
??如果用戶申請精確位置權(quán)限钝域,那就要先申請粗略位置權(quán)限。
??如果用戶申請后臺位置權(quán)限锭魔,那就先申請模糊位置權(quán)限和精確位置權(quán)限例证。當(dāng)同意這兩個權(quán)限后,彈窗提示用戶到系統(tǒng)設(shè)置中打開相應(yīng)的權(quán)限迷捧,用戶在設(shè)置界面中的選擇“始終允許”應(yīng)用訪問位置信息權(quán)限织咧,應(yīng)用就獲取了后臺位置權(quán)限。

 /**
   * 檢查權(quán)限的其它配置
   *
   * @param permissions
   */
  checkOtherConfig(permissions: Array<Permissions>) {
    const locationPermissionIndex = permissions.indexOf(this.LOCATION_PERMISSION)
    const locationBackgroundIndex = permissions.indexOf(this.LOCATION_IN_BACKGROUND)
    if (locationPermissionIndex >= 0 && locationBackgroundIndex < 0) {
      /*
       * 對于位置權(quán)限漠秋,有兩種情況
       * 第一:申請模糊位置權(quán)限笙蒙,大部分情況下,不會申請模糊位置權(quán)限庆锦,更多的是第二種情況捅位。
       * 第二:申請精確位置權(quán)限,需要先申請模糊位置權(quán)限搂抒。
       */
      permissions = []
      permissions.push(this.APPROXIMATELY_LOCATION)
      permissions.push(this.LOCATION_PERMISSION)
    }
    if (locationBackgroundIndex >= 0) {
      // 申請后臺位置權(quán)限艇搀,需要先申請模糊位置權(quán)限和精確位置權(quán)限。當(dāng)用戶點擊彈窗授予前臺位置權(quán)限后求晶,應(yīng)用通過彈窗焰雕、提示窗等形式告知用戶前往設(shè)置界面授予后臺位置權(quán)限。
      permissions = []
      permissions.push(this.APPROXIMATELY_LOCATION)
      permissions.push(this.LOCATION_PERMISSION)
      permissions.push(this.LOCATION_IN_BACKGROUND)
    }
    this.setNewPermission(permissions)
  }

5芳杏、4 判斷是否有權(quán)限

??當(dāng)所有的檢測都通過后矩屁,就可以判斷是否有權(quán)限了辟宗。調(diào)用checkAccessToken()方法來校驗當(dāng)前是否已經(jīng)授權(quán)。如果已經(jīng)授權(quán)档插,則回調(diào)告知調(diào)用者已經(jīng)有權(quán)限慢蜓,否則需要進(jìn)行下一步操作,即向用戶申請授權(quán)郭膛。

  hasPermission(permissions: Array<Permissions>): boolean {
    for (let i = 0; i < permissions.length; i++) {
      const permission = permissions[i]
      let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
      let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
      // 獲取應(yīng)用程序的accessTokenID
      let tokenId: number = 0;
      try {
        let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
        let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
        tokenId = appInfo.accessTokenId;
      } catch (error) {
        const err: BusinessError = error as BusinessError;
        console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
      }
      // 校驗應(yīng)用是否被授予權(quán)限
      grantStatus = atManager.checkAccessTokenSync(tokenId, permission);
      if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
        return false
      }
    }
    return true
  }

5晨抡、5 申請權(quán)限

??調(diào)用requestPermissionsFromUser(),如果用戶授權(quán)则剃,則調(diào)用mOnGranted耘柱。如果用戶拒絕授權(quán),提示用戶必須授權(quán)才能訪問當(dāng)前頁面的功能棍现,并引導(dǎo)用戶到系統(tǒng)設(shè)置中打開相應(yīng)的權(quán)限调煎。

 /**
   * 申請權(quán)限
   *
   * @param permissions
   */
  requestPermission(permissions: Permissions[]) {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    // requestPermissionsFromUser會判斷權(quán)限的授權(quán)狀態(tài)來決定是否喚起彈窗
    atManager.requestPermissionsFromUser(this.origin.getContext(), permissions).then((data) => {
      let grantStatus: Array<number> = data.authResults;
      let length: number = grantStatus.length;
      for (let i = 0; i < length; i++) {
        if (grantStatus[i] === 0) {
          // 用戶授權(quán),可以繼續(xù)訪問目標(biāo)操作
          this.mOnGranted?.(this.originPermissions)
        } else {
          // 用戶拒絕授權(quán)己肮,提示用戶必須授權(quán)才能訪問當(dāng)前頁面的功能士袄,并引導(dǎo)用戶到系統(tǒng)設(shè)置中打開相應(yīng)的權(quán)限
          this.mOnDenied?.(this.originPermissions)
          return;
        }
      }
      // 授權(quán)成功
    }).catch((err: BusinessError) => {
      console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
    })
  }

5、6 系統(tǒng)設(shè)置彈窗

??用戶拒絕授權(quán)谎僻,提示用戶必須授權(quán)才能訪問當(dāng)前頁面的功能娄柳,并引導(dǎo)用戶到系統(tǒng)設(shè)置中打開相應(yīng)的權(quán)限。但在跳轉(zhuǎn)系統(tǒng)設(shè)置之前艘绍,需要彈窗提示用戶赤拒,這里提供一個默認(rèn)的彈窗。如果這個彈窗不滿足你的要求诱鞠,你可以改掉挎挖。當(dāng)用戶在彈窗里面點擊取消,則隱藏彈窗航夺。當(dāng)用戶在彈窗里面點擊去設(shè)置蕉朵,則跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面。

import { TaoYao } from '@shijing/taoyao/Index'
import { common, Permissions } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'

/**
 * 跳轉(zhuǎn)系統(tǒng)設(shè)置之前阳掐,需要先彈窗
 */
@CustomDialog
export struct PermissionDialog {

  private title: string = '權(quán)限設(shè)置'
  private subtitle?: Resource
  private left: string = '取消'
  private right: string = '去設(shè)置'
  private permissions = new Array<Permissions>()
  private context = getContext(this) as common.UIAbilityContext
  controller: CustomDialogController

  aboutToAppear(): void {
    if (this.permissions.indexOf(('ohos.permission.ACCESS_BLUETOOTH' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.access_bluetooth')
    } else if (this.permissions.indexOf(('ohos.permission.MEDIA_LOCATION' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.media_location')
    } else if (this.permissions.indexOf(('ohos.permission.APP_TRACKING_CONSENT' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.app_tracking_consent')
    } else if (this.permissions.indexOf(('ohos.permission.ACTIVITY_MOTION' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.activity_motion')
    } else if (this.permissions.indexOf(('ohos.permission.CAMERA' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.camera')
    } else if (this.permissions.indexOf(('ohos.permission.DISTRIBUTED_DATASYNC' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.distributed_datasync')
    } else if (this.permissions.indexOf(('ohos.permission.LOCATION_IN_BACKGROUND' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.location_in_background')
    } else if (this.permissions.indexOf(('ohos.permission.LOCATION' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.location')
    } else if (this.permissions.indexOf(('ohos.permission.APPROXIMATELY_LOCATION' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.approximately_location')
    } else if (this.permissions.indexOf(('ohos.permission.MICROPHONE' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.microphone')
    } else if (this.permissions.indexOf(('ohos.permission.READ_CALENDAR' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.read_calendar')
    } else if (this.permissions.indexOf(('ohos.permission.WRITE_CALENDAR' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.write_calendar')
    } else if (this.permissions.indexOf(('ohos.permission.READ_HEALTH_DATA' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.read_health_data')
    } else if (this.permissions.indexOf(('ohos.permission.READ_MEDIA' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.read_media')
    } else if (this.permissions.indexOf(('ohos.permission.WRITE_MEDIA' as Permissions)) >= 0) {
      this.subtitle = $r('app.string.write_media')
    }
  }

  build() {
    Column() {
      Text(this.title)
        .fontSize(20)
        .fontColor('#151724')
      Text(this.subtitle)
        .fontColor('#151724')
        .fontSize(15)
        .margin({top: 30})
      Row() {
        Button(this.left)
          .fontColor('#585a5c')
          .borderRadius(24)
          .backgroundColor('#eeeeee')
          .width('40%')
          .height(48)
          .margin({right: 20})
          .onClick(() => {
            this.controller.close()
          })
        Button(this.right)
          .fontColor('#ffffff')
          .borderRadius(24)
          .backgroundColor('#4b54fa')
          .width('40%')
          .height(48)
          .onClick(() => {
            this.controller.close()
            TaoYao.goToSettingPage(this.context)
          })
      }
      .margin({top: 30})
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .width('100%')
    .borderRadius(20)
    .backgroundColor('#ffffff')
    .padding({left: 24, right: 24, top: 30, bottom: 28})
  }
}

5墓造、7 跳轉(zhuǎn)到設(shè)置頁面

??使用下面的代碼即可跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面。構(gòu)建一個want對象锚烦,指定bundleNameabilityName帝雇、uri涮俄、parameters等參數(shù),調(diào)用startAbility尸闸。

function openPermissionsInSystemSettings(context: common.UIAbilityContext): void {
  let wantInfo: Want = {
    bundleName: 'com.huawei.hmos.settings', // 系統(tǒng)設(shè)置的包名
    abilityName: 'com.huawei.hmos.settings.MainAbility', // 系統(tǒng)設(shè)置權(quán)限頁面的類名
    uri: 'application_info_entry',
    parameters: {
      pushParams: 'com.example.myapplication' // 應(yīng)用的包名彻亲,也就是打開指定應(yīng)用的詳情頁面
    }
  }
  context.startAbility(wantInfo).then(() => {
    // ...
  }).catch((err: BusinessError) => {
    // ...
  })

??目前只有華為手機(jī)使用了開源鴻蒙系統(tǒng)孕锄,不排除后續(xù)會有其它的廠商使用開源鴻蒙系統(tǒng),到時want對象的bundleName苞尝、abilityName畸肆、uri可能會不一樣。在這種情況下宙址,上面的代碼就會有兼容性問題轴脐。這就需要針對不同的品牌,創(chuàng)建不同的want對象抡砂。這里采用策略模式大咱。如下代碼,創(chuàng)建SettingWant接口注益,定義getWant方法碴巾,由子類實現(xiàn)該方法,也就是由子類來創(chuàng)建want對象丑搔。

export interface SettingWant {

  /**
   * 獲取want對象
   *
   * @param bundleName
   * @returns
   */
  getWant(bundleName: string): Want
}

??新建DefaultSettingWant類厦瓢,DefaultSettingWant是一個默認(rèn)創(chuàng)建Want對象的子類。

/**
 * 默認(rèn)獲取的want參數(shù)
 */
export class DefaultSettingWant implements SettingWant {

  getWant(bundleName: string): Want {
    let wantInfo: Want = {
      bundleName: 'com.huawei.hmos.settings',
      abilityName: 'com.huawei.hmos.settings.MainAbility',
      uri: 'application_info_entry',
      parameters: {
        pushParams: bundleName // 打開指定應(yīng)用的詳情頁面
      }
    }
    return wantInfo
  }

}

??對于華為手機(jī)啤月,我們就繼承DefaultSettingWant煮仇,直接使用默認(rèn)創(chuàng)建的Want對象。

/**
 * 獲取華為手機(jī)上的want參數(shù)
 */
export class HuaWeiSettingWant extends DefaultSettingWant {
}

??如下代碼顽冶,先創(chuàng)建SettingWant對象欺抗,通過deviceInfo.brand判斷品牌,如果是華為手機(jī)强重,則創(chuàng)建HuaWeiSettingWant绞呈。調(diào)用getWant獲取到Want對象,調(diào)用startAbility跳轉(zhuǎn)到系統(tǒng)設(shè)置间景。

gToSettingPage(): void {
    const bundleName = this.getContext().abilityInfo.bundleName
    let settingWant: SettingWant
    if (deviceInfo.brand === "HUAWEI") {
      settingWant = new HuaWeiSettingWant()
    } else {
      settingWant = new DefaultSettingWant()
    }
    const want = settingWant.getWant(bundleName)
    if (this.origin instanceof UIExtensionAbilityOrigin) {
      // 在UIExtensionAbility中跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面
      this.startAbilityFromUIExtensionAbility(want)
    } else {
      // 在UI或者UIAbility中跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面
      this.startAbilityFromUIAbility(want)
    }
  }

 /**
   * 在UIExtensionAbility中跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面
   *
   * @param want
   */
  private startAbilityFromUIExtensionAbility(want: Want) {
    (this.origin.getContext() as common.UIExtensionContext).startAbility(want).then(() => {
      // 跳轉(zhuǎn)成功
    }).catch((err: BusinessError) => {
      console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
    })
  }

  /**
   * 在UI或者UIAbility中跳轉(zhuǎn)到系統(tǒng)設(shè)置頁面
   *
   * @param want
   */
  private startAbilityFromUIAbility(want: Want) {
    this.getContext().startAbility(want).then(() => {
      // 跳轉(zhuǎn)成功
    }).catch((err: BusinessError) => {
      console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
    })
  }

  getContext(): common.UIAbilityContext {
    return (this.origin.getContext()) as common.UIAbilityContext
  }

六佃声、源碼

??更多具體的代碼,請下載源碼倘要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末圾亏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子封拧,更是在濱河造成了極大的恐慌志鹃,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泽西,死亡現(xiàn)場離奇詭異曹铃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捧杉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門陕见,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秘血,“玉大人展融,你說我怎么就攤上這事晴音。” “怎么了漏益?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵忍坷,是天一觀的道長粘舟。 經(jīng)常有香客問我,道長承匣,這世上最難降的妖魔是什么蓖乘? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮韧骗,結(jié)果婚禮上嘉抒,老公的妹妹穿的比我還像新娘。我一直安慰自己袍暴,他們只是感情好些侍,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著政模,像睡著了一般岗宣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淋样,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天耗式,我揣著相機(jī)與錄音,去河邊找鬼趁猴。 笑死刊咳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的儡司。 我是一名探鬼主播娱挨,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捕犬!你這毒婦竟也來了跷坝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碉碉,失蹤者是張志新(化名)和其女友劉穎柴钻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垢粮,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顿颅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粱腻。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斩跌,靈堂內(nèi)的尸體忽然破棺而出绍些,到底是詐尸還是另有隱情,我是刑警寧澤耀鸦,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布柬批,位于F島的核電站,受9級特大地震影響袖订,放射性物質(zhì)發(fā)生泄漏氮帐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一洛姑、第九天 我趴在偏房一處隱蔽的房頂上張望上沐。 院中可真熱鬧,春花似錦楞艾、人聲如沸参咙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕴侧。三九已至,卻和暖如春两入,著一層夾襖步出監(jiān)牢的瞬間净宵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工裹纳, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留择葡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓痊夭,卻偏偏與公主長得像刁岸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子她我,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

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