Android 6.0 運(yùn)行時權(quán)限處理完全解析

Android 6.0 運(yùn)行時權(quán)限處理完全解析

運(yùn)行時權(quán)限的變化及特點(diǎn)

對于6.0以下的權(quán)限及在安裝的時候,根據(jù)權(quán)限聲明產(chǎn)生一個權(quán)限列表,用戶只有在同意之后才能完成app的安裝,造成了我們想要使用某個app,就要默默忍受其一些不必要的權(quán)限(比如是個app都要訪問通訊錄、短信等)。而在6.0以后,我們可以直接安裝,當(dāng)app需要我們授予不恰當(dāng)?shù)臋?quán)限的時候,我們可以予以拒絕(比如:單機(jī)的象棋對戰(zhàn),請求訪問任何權(quán)限,我都是不同意的)。當(dāng)然你也可以在設(shè)置界面對每個app的權(quán)限進(jìn)行查看,以及對單個權(quán)限進(jìn)行授權(quán)或者解除授權(quán)。

新的權(quán)限機(jī)制更好的保護(hù)了用戶的隱私,Google將權(quán)限分為兩類,一類是Normal Permissions,這類權(quán)限一般不涉及用戶隱私,是不需要用戶進(jìn)行授權(quán)的,比如手機(jī)震動、訪問網(wǎng)絡(luò)等;另一類是Dangerous Permission,一般是涉及到用戶隱私的,需要用戶進(jìn)行授權(quán),比如讀取sdcard、訪問通訊錄等。

  • Normal Permissions如下:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
  • Dangerous Permissions:
group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

通過下面命令可以查看:

adb shell pm list permissions -d -g

看到上面的dangerous permissions,會發(fā)現(xiàn)一個問題,好像危險(xiǎn)權(quán)限都是一組一組的,恩,沒錯,的確是這樣的。

那么有個問題:分組對我們的權(quán)限機(jī)制有什么影響嗎?

的確是有影響的,如果app運(yùn)行在Android 6.x的機(jī)器上,對于授權(quán)機(jī)制是這樣的。如果你申請某個危險(xiǎn)的權(quán)限,假設(shè)你的app早已被用戶授權(quán)了同一組的某個危險(xiǎn)權(quán)限,那么系統(tǒng)會立即授權(quán),而不需要用戶去點(diǎn)擊授權(quán)。比如你的app對READ_CONTACTS已經(jīng)授權(quán)了,當(dāng)你的app申請WRITE_CONTACTS時,系統(tǒng)會直接授權(quán)通過。此外,對于申請時彈出的dialog上面的文本說明也是對整個權(quán)限組的說明,而不是單個權(quán)限(ps:這個dialog是不能進(jìn)行定制的)。

不過需要注意的是,不要對權(quán)限組過多的依賴,盡可能對每個危險(xiǎn)權(quán)限都進(jìn)行正常流程的申請,因?yàn)樵诤笃诘陌姹局羞@個權(quán)限組可能會產(chǎn)生變化。

相關(guān)API

API的講解就跟著申請權(quán)限步驟一起了:

  1. 在AndroidManifest文件中添加需要的權(quán)限。

這個步驟和我們之前的開發(fā)并沒有什么變化,試圖去申請一個沒有聲明的權(quán)限可能會導(dǎo)致程序崩潰。

  1. 檢查權(quán)限
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
}else{
    //
}

這里涉及到一個API,ContextCompat.checkSelfPermission,主要用于檢測某個權(quán)限是否已經(jīng)被授予,方法返回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當(dāng)返回DENIED就需要進(jìn)行申請授權(quán)了。

  1. 申請授權(quán)
 ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

該方法是異步的,第一個參數(shù)是Context;第二個參數(shù)是需要申請的權(quán)限的字符串?dāng)?shù)組;第三個參數(shù)為requestCode,主要用于回調(diào)的時候檢測。可以從方法名requestPermissions以及第二個參數(shù)看出,是支持一次性申請多個權(quán)限的,系統(tǒng)會通過對話框逐一詢問用戶是否授權(quán)。

  1. 處理權(quán)限申請回調(diào)
@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
    }
}

OK,對于權(quán)限的申請結(jié)果,首先驗(yàn)證requestCode定位到你的申請,然后驗(yàn)證grantResults對應(yīng)于申請的結(jié)果,這里的數(shù)組對應(yīng)于申請時的第二個權(quán)限字符串?dāng)?shù)組。如果你同時申請兩個權(quán)限,那么grantResults的length就為2,分別記錄你兩個權(quán)限的申請結(jié)果。如果申請成功,就可以做你的事情了~

當(dāng)然,到此我們的權(quán)限申請的步驟,基本介紹就如上述。不過還有個API值得提一下:

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
        Manifest.permission.READ_CONTACTS)) 
    // Show an expanation to the user *asynchronously* -- don't block
    // this thread waiting for the user's response! After the user
    // sees the explanation, try again to request the permission.

}

這個API主要用于給用戶一個申請權(quán)限的解釋,該方法只有在用戶在上一次已經(jīng)拒絕過你的這個權(quán)限申請。也就是說,用戶已經(jīng)拒絕一次了,你又彈個授權(quán)框,你需要給用戶一個解釋,為什么要授權(quán),則使用該方法。

那么將上述幾個步驟結(jié)合到一起就是:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

簡單的例子

這里寫一個簡單的例子,針對于運(yùn)行時權(quán)限。相信大家在最開始接觸Android的時候,都利用Intent實(shí)驗(yàn)了打電話、發(fā)短信等功能。

我們看看直接撥打電話在Android 6.x的設(shè)備上權(quán)限需要如何處理。

當(dāng)然代碼非常簡單:

package com.zhy.android160217;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity
{

    private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testCall(View view)
    {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED)
        {

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    MY_PERMISSIONS_REQUEST_CALL_PHONE);
        } else
        {
            callPhone();
        }
    }

    public void callPhone()
    {
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "10086");
        intent.setData(data);
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {

        if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE)
        {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                callPhone();
            } else
            {
                // Permission Denied
                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

在Android 6.x上運(yùn)行是,點(diǎn)擊testCall,即會彈出授權(quán)窗口,如何你Allow則直接撥打電話,如果Denied則Toast彈出”Permission Denied”。

例子很簡單,不過需要注意的是,對于Intent這種方式,很多情況下是不需要授權(quán)的甚至權(quán)限都不需要的,比如:你是到撥號界面而不是直接撥打電話,就不需要去申請權(quán)限;打開系統(tǒng)圖庫去選擇照片;調(diào)用系統(tǒng)相機(jī)app去牌照等。更多請參考Consider Using an Intent

當(dāng)然,上例也說明了并非所有的通過Intent的方式都不需要申請權(quán)限。一般情況下,你是通過Intent打開另一個app,讓用戶通過該app去做一些事情,你只關(guān)注結(jié)果(onActivityResult),那么權(quán)限是不需要你處理的,而是由打開的app去處理。

封裝

  1. 申請權(quán)限

雖然權(quán)限處理并不復(fù)雜,但是需要編寫很多重復(fù)的代碼,所以目前也有很多庫對用法進(jìn)行了封裝,大家可以在github首頁搜索:android permission,對比了幾個庫的使用方式,發(fā)現(xiàn)https://github.com/lovedise/PermissionGen這個庫據(jù)我所見相比較而言使用算是比較簡單的,那么就以這個庫的源碼為基礎(chǔ)來講解,大家有興趣可以多看幾個庫的源碼。

封裝的代碼很簡單,正如大家的對上面的權(quán)限代碼所見,沒有特別復(fù)雜的地方。

對于申請權(quán)限的代碼,原本的編寫為:

if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED)
{

    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.CALL_PHONE},
            MY_PERMISSIONS_REQUEST_CALL_PHONE);
} else
{
    callPhone();
}

大家可以看到,對于其他的權(quán)限,其實(shí)申請的邏輯是類似的;唯一不同的肯定就是參數(shù),那么看上面的代碼,我們需要3個參數(shù):Activity|Fragment、權(quán)限字符串?dāng)?shù)組、int型申請碼。

也就是說,我們只需要寫個方法,接受這幾個參數(shù)即可,然后邏輯是統(tǒng)一的。

public static void needPermission(Fragment fragment, int requestCode, String[] permissions)
{
    requestPermissions(fragment, requestCode, permissions);
}

@TargetApi(value = Build.VERSION_CODES.M)
private static void requestPermissions(Object object, int requestCode, String[] permissions)
{
    if (!Utils.isOverMarshmallow())
    {
        doExecuteSuccess(object, requestCode);
        return;
    }
    List<String> deniedPermissions = Utils.findDeniedPermissions(getActivity(object), permissions);

    if (deniedPermissions.size() > 0)
    {
        if (object instanceof Activity)
        {
            ((Activity) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else if (object instanceof Fragment)
        {
            ((Fragment) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else
        {
            throw new IllegalArgumentException(object.getClass().getName() + " is not supported");
        }

    } else
    {
        doExecuteSuccess(object, requestCode);
    }
}

Utils.findDeniedPermissions其實(shí)就是check沒有授權(quán)的權(quán)限。

#Utils
@TargetApi(value = Build.VERSION_CODES.M)
public static List<String> findDeniedPermissions(Activity activity, String... permission)
{
    List<String> denyPermissions = new ArrayList<>();
    for (String value : permission)
    {
        if (activity.checkSelfPermission(value) != PackageManager.PERMISSION_GRANTED)
        {
            denyPermissions.add(value);
        }
    }
    return denyPermissions;
}

那么上述的邏輯就很清晰了,需要的3種參數(shù)傳入,先去除已經(jīng)申請的權(quán)限,然后開始申請權(quán)限。

ok,我相信上面代碼,大家掃一眼就可以了解了。

  1. 處理權(quán)限回調(diào)

對于回調(diào),因?yàn)橐鶕?jù)是否授權(quán)去執(zhí)行不同的事情,所以很多框架也需要些一連串的代碼,或者和前面的申請代碼耦合。不過這個框架還是比較方便的,也是我選擇它來講解的原因。

回調(diào)主要做的事情:

了解是否授權(quán)成功。
根據(jù)授權(quán)情況進(jìn)行回調(diào)。
對于第一條邏輯都一樣;對于第二條,因?yàn)樯婕暗絻蓚€分支,每個分支執(zhí)行不同的方法。

對于第二條,很多框架選擇將兩個分支的方法在申請權(quán)限的時候進(jìn)行注冊,然后在回調(diào)中根據(jù)requestCode進(jìn)行匹配執(zhí)行,不過這樣需要在針對每次申請進(jìn)行對象管理。

不過這個框架采取了一種很有意思的做法,它利用注解去確定需要執(zhí)行的方法,存在兩個注解:

@PermissionSuccess(requestCode = 100)
@PermissionFail(requestCode = 100)

利用反射根據(jù)授權(quán)情況+requestCode即可找到注解標(biāo)注的方法,然后直接執(zhí)行即可。

大致的代碼為:

@Override public void onRequestPermissionsResult(int requestCode, String[] permissions,
      int[] grantResults) {
    PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}

private static void requestResult(Object obj, int requestCode, String[] permissions,
                                  int[] grantResults)
{
    List<String> deniedPermissions = new ArrayList<>();
    for (int i = 0; i < grantResults.length; i++)
    {
        if (grantResults[i] != PackageManager.PERMISSION_GRANTED)
        {
            deniedPermissions.add(permissions[i]);
        }
    }

    if (deniedPermissions.size() > 0)
    {
        doExecuteFail(obj, requestCode);
    } else
    {
        doExecuteSuccess(obj, requestCode);
    }
}

首先根據(jù)grantResults進(jìn)行判斷成功還是失敗,對于成功則:

private static void doExecuteSuccess(Object activity, int requestCode)
{
    Method executeMethod = Utils.findMethodWithRequestCode(activity.getClass(),
            PermissionSuccess.class, requestCode);

    executeMethod(activity, executeMethod);
}

#Utils
public static <A extends Annotation> Method findMethodWithRequestCode(Class clazz,  Class<A> annotation, int requestCode)
{
    for (Method method : clazz.getDeclaredMethods())
    {
        if (method.isAnnotationPresent(annotation))
        {
            if (isEqualRequestCodeFromAnntation(method, annotation, requestCode))
            {
                return method;
            }
        }
    }
    return null;
}

根據(jù)注解和requestCode找到方法,然后反射執(zhí)行即可。失敗的邏輯類似,不貼代碼了。

ok,到此我們的運(yùn)行時權(quán)限相對于早起版本的變化、特點(diǎn)、以及如何處理和封裝都介紹完了。

不過對于上面講解的庫,肯定有人會說:運(yùn)行時反射會影響效率,沒錯,不過我已經(jīng)在上述代碼的基礎(chǔ)上將運(yùn)行時注解改成Annotation Processor的方式了,即編譯時注解,這樣的話,就不存在反射損失效率的問題了。本來準(zhǔn)備fork修改,然后PR,結(jié)果寫完,改動太大,估計(jì)PR是不可能通過了,所以另起項(xiàng)目了,也方便后面的做一些擴(kuò)展和維護(hù)。

MPermissions庫用法

對外的接口和PermissionGen基本一致,因?yàn)樯暾堉恍枰齻€參數(shù),拋棄了使用原本類庫的單例的方式,直接一個幾個靜態(tài)方法,簡單整潔暴力。

貼一個用法:

public class MainActivity extends AppCompatActivity
{

    private Button mBtnSdcard;
    private static final int REQUECT_CODE_SDCARD = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);
        mBtnSdcard.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {
        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @PermissionGrant(REQUECT_CODE_SDCARD)
    public void requestSdcardSuccess()
    {
        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }

    @PermissionDenied(REQUECT_CODE_SDCARD)
    public void requestSdcardFailed()
    {
        Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }
}

是不是簡單明了~~對于onRequestPermissionsResult所有的Activity都是一致的,所以可以放到BaseActivity中去。此外,在Fragment中使用的方式一致,詳見demo。

詳見庫:https://github.com/hongyangAndroid/MPermissions

至于為什么不直接介紹MPermission的源碼,因?yàn)橹饕婕暗紸nnotation Processor,所以以這種方式引出,后面考慮單篇博文介紹下我目前所會的編譯時注解的相關(guān)做法以及API的使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評論 2 374

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