【Android】當(dāng)關(guān)閉通知消息權(quán)限后無法顯示系統(tǒng)Toast的解決方案

前言

不知道大家是否遇到了當(dāng)你們的App在5.0以上系統(tǒng)中被用戶關(guān)閉消息通知后(其實(shí)用戶本身只是想關(guān)閉Notification的涝涤,猜測)贪嫂,系統(tǒng)的Toast也神奇的無法顯示痊班。當(dāng)然這個(gè)問題并不復(fù)雜,有很多種解決方案遵班,我們逐一探討一下霜幼,然后來看看到底哪種方式會好一點(diǎn)嫩码。

干貨

項(xiàng)目地址:Blincheng/Toast
1.在project的build文件中添加如下:

allprojects {
    repositories {
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

2.然后在app的build文件依賴即可

compile 'com.github.Blincheng:Toast:v1.0'

3.使用和系統(tǒng)的Toast完全一致,唯一要注意的就是該Toast的路徑是import com.yiguo.toast.Toast;

Toast.makeText(MainActivity.this,"我是一個(gè)屏蔽通知我也是可以顯示的Toast",Toast.LENGTH_SHORT).show();

問題分析

直接跟蹤Toast的源碼罪既,其實(shí)我們可以發(fā)現(xiàn)铸题,果真Toast其實(shí)是通過NotificationManagerService 維護(hù)一個(gè)toast隊(duì)列铡恕,然后通知給Toast中的客戶端 TN 調(diào)用 WindowManager 添加view。那么當(dāng)用戶關(guān)閉通知權(quán)限后自然也無法顯示Toast了回挽。

/**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }
    ....
    static private INotificationManager getService() {
        if (sService != null) {
            return sService;
        }
        sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
        return sService;
    }

解決思路

這邊就來說說我這邊的幾種解決方案没咙,就是大致我能想到的,哈哈千劈。

  • 自己仿照系統(tǒng)的Toast然后用自己的消息隊(duì)列來維護(hù)祭刚,讓其不受NotificationManagerService影響。
  • 通過WindowManager自己來寫一個(gè)通知墙牌。
  • 通過Dialog涡驮、PopupWindow來編寫一個(gè)自定義通知。
  • 通過直接去當(dāng)前頁面最外層content布局來添加View喜滨。

仿照系統(tǒng)Toast自己來維護(hù)Toast消息隊(duì)列

這部分我就不寫了捉捅,大家有興趣可以看下解決小米MIUI系統(tǒng)上后臺應(yīng)用沒法彈Toast的問題 這篇博文,東西寫的很詳細(xì)虽风,內(nèi)容也很細(xì)棒口,大家可以看看。

通過WindowManager自己來寫一個(gè)通知

說起WindowManager辜膝,其實(shí)我對這個(gè)東西的第一印象就是強(qiáng)大无牵,懸浮窗什么的其實(shí)都是通過WindowManager來實(shí)現(xiàn)的,那么我們來看看怎么實(shí)現(xiàn)厂抖,我就直接上代碼了

package com.yiguo.utils;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

/**
 * Created by bl on 2016/10/11.
 */

public class Toast {
    private Context mContext;
    private WindowManager wm;
    private int mDuration;
    private View mNextView;
    public static final int LENGTH_SHORT = 1500;
    public static final int LENGTH_LONG = 3000;

    public Toast(Context context) {
        mContext = context.getApplicationContext();
        wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    }

    public static Toast makeText(Context context, CharSequence text,
                                 int duration) {
        Toast result = new Toast(context);
        View view = android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).getView();
        if (view != null){
            TextView tv = (TextView) view.findViewById(android.R.id.message);
            tv.setText(text);
        }
        result.mNextView = view;
        result.mDuration = duration;
        return result;
    }

    public static Toast makeText(Context context, int resId, int duration)
            throws Resources.NotFoundException {
        return makeText(context, context.getResources().getText(resId),duration);
    }

    public void show() {
        if (mNextView != null) {
            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
            params.gravity = Gravity.CENTER | Gravity.CENTER_HORIZONTAL;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
            params.format = PixelFormat.TRANSLUCENT;
            params.windowAnimations = android.R.style.Animation_Toast;
            params.y = dip2px(mContext, 64);
            params.type = WindowManager.LayoutParams.TYPE_TOAST;
            wm.addView(mNextView, params);
            new Handler().postDelayed(new Runnable() {

                @Override
                public void run() {
                    if (mNextView != null) {
                        wm.removeView(mNextView);
                        mNextView = null;
                        wm = null;
                    }
                }
            }, mDuration);
        }
    }

    /**
     * dip與px的轉(zhuǎn)換
     *
     * @參數(shù)   @param context
     * @參數(shù)   @param dipValue
     * @返回值 int
     *
     */
    private int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
}

嗯茎毁,這樣寫應(yīng)該是沒問題的,然后為啥沒有效果呢忱辅?七蜘?好吧,其實(shí)寫了這么多墙懂,就是給自己挖坑橡卤,很明顯,這個(gè)東西在現(xiàn)在的5.0以上機(jī)器中有一個(gè)懸浮窗權(quán)限损搬,而且系統(tǒng)默認(rèn)是關(guān)閉該權(quán)限的蒜魄,只有用戶手動打開才能顯示,而且代碼中也要添加如下一條權(quán)限场躯。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

那么問題又回來了,用戶一般不會打開旅挤,這不是又白搞么踢关。

通過Dialog、PopupWindow來編寫一個(gè)自定義通知粘茄。

這個(gè)方案貌似也是可行的签舞,代碼就不寫了秕脓,提醒一點(diǎn)就是一般來說Dialog和PopupWindow顯示時(shí)有一個(gè)隔板,用戶是無法點(diǎn)擊其余部分控件的儒搭,所以記得加上以上屬性吠架。

 public static void setPopupWindowTouchModal(PopupWindow popupWindow,
                                                boolean touchModal) {
        if (null == popupWindow) {
            return;
        }
        Method method;
        try {
            method = PopupWindow.class.getDeclaredMethod("setTouchModal",
                    boolean.class);
            method.setAccessible(true);
            method.invoke(popupWindow, touchModal);

        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

通過直接去當(dāng)前頁面最外層content布局來添加View。

說說這種方式吧搂鲫,其實(shí)剛開始我也是沒有想到的傍药,因?yàn)橐话愫苌倩厝ブ苯幽肁ctivity最外層的content布局去創(chuàng)建一個(gè)View并且顯示在上面的。

(ViewGroup) ((Activity) context).findViewById(android.R.id.content);

其實(shí)我們是可以直接通過findViewById去直接拿到最外層布局的哦魂仍,當(dāng)然context記得一定是Activity拐辽。
然后通過以下代碼就可以直接把布局顯示在當(dāng)前content布局之上。

ViewGroup container = (ViewGroup) ((Activity) context).findViewById(android.R.id.content);
View v = ((Activity) context).getLayoutInflater().inflate(R.layout.etoast,container);

這種方式是不是有點(diǎn)奇怪擦酌,好吧俱诸,我也是這么想的,不過感覺還是非常的實(shí)在的赊舶,也不復(fù)雜睁搭,東西也不多,直接上代碼笼平。

public class EToast {
    public static final int LENGTH_SHORT = 0;
    public static final int LENGTH_LONG = 1;
    private static EToast result;
    private final int ANIMATION_DURATION = 600;
    private static TextView mTextView;
    private ViewGroup container;
    private View v;

    private int HIDE_DELAY = 2000;

    private LinearLayout mContainer;
    private AlphaAnimation mFadeOutAnimation;
    private AlphaAnimation mFadeInAnimation;

    private boolean isShow = false;
    private static Context mContext;
    private Handler mHandler = new Handler();
    private String TOAST_TAG = "EToast_Log";

    private EToast(Context context) {
        mContext = context;
        container = (ViewGroup) ((Activity) context)
                .findViewById(android.R.id.content);
        View viewWithTag = container.findViewWithTag(TOAST_TAG);
        if(viewWithTag == null){
            v = ((Activity) context).getLayoutInflater().inflate(
                    R.layout.etoast, container);
            v.setTag(TOAST_TAG);
        }else{
            v = viewWithTag;
        }
        mContainer = (LinearLayout) v.findViewById(R.id.mbContainer);
        mContainer.setVisibility(View.GONE);
        mTextView = (TextView) v.findViewById(R.id.mbMessage);
    }

    public static EToast makeText(Context context, String message, int HIDE_DELAY) {
        if(result == null){
            result = new EToast(context);
        }else{
            if(!mContext.getClass().getName().equals(context.getClass().getName())){
                result = new EToast(context);
            }
        }
        if(HIDE_DELAY == LENGTH_LONG){
            result.HIDE_DELAY = 2500;
        }else{
            result.HIDE_DELAY = 1500;
        }
        mTextView.setText(message);
        return result;
    };
    public static EToast makeText(Context context, int resId, int HIDE_DELAY) {
        String mes = "";
        try{
            mes = context.getResources().getString(resId);
        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
        return makeText(context,mes,HIDE_DELAY);
    }
    public void show() {
        if(isShow){
            return;
        }
        isShow = true;
        mFadeInAnimation = new AlphaAnimation(0.0f, 1.0f);
        mFadeOutAnimation = new AlphaAnimation(1.0f, 0.0f);
        mFadeOutAnimation.setDuration(ANIMATION_DURATION);
        mFadeOutAnimation
                .setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        isShow = false;
                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        mContainer.setVisibility(View.GONE);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }
                });
        mContainer.setVisibility(View.VISIBLE);

        mFadeInAnimation.setDuration(ANIMATION_DURATION);

        mContainer.startAnimation(mFadeInAnimation);
        mHandler.postDelayed(mHideRunnable, HIDE_DELAY);
    }

    private final Runnable mHideRunnable = new Runnable() {
        @Override
        public void run() {
            mContainer.startAnimation(mFadeOutAnimation);
        }
    };
    public void cancel(){
        if(isShow) {
            isShow = false;
            mContainer.setVisibility(View.GONE);
            mHandler.removeCallbacks(mHideRunnable);
        }
    }
    public static void reset(){
        result = null;
    }
    public void setText(CharSequence s){
        if(result == null) return;
        TextView mTextView = (TextView) v.findViewById(R.id.mbMessage);
        if(mTextView == null) throw new RuntimeException("This Toast was not created with Toast.makeText()");
        mTextView.setText(s);
    }
    public void setText(int resId) {
        setText(mContext.getText(resId));
    }
}

簡單說下吧园骆,代碼應(yīng)該是很簡單的,然后簡單封裝了和Toast相同的幾個(gè)方法出吹。嗯遇伞,其實(shí)大家也應(yīng)該能發(fā)現(xiàn)我這邊的布局其實(shí)是一直都在的,只是直接GONE掉了捶牢。所以呢鸠珠,還是有待優(yōu)化的地方,當(dāng)然可以去想想是不是可以直接remove()掉什么的秋麸。我這邊也沒有用隊(duì)列渐排,我覺得在一個(gè)Toast顯示的期間如果再需要顯示另一個(gè)Toast,直接把當(dāng)前的文本改過來就好了灸蟆,沒有必要搞個(gè)隊(duì)列的驯耻,而且系統(tǒng)Toast我最厭惡的就是這個(gè)了,用戶如果不停的點(diǎn)擊炒考,那Toast一個(gè)接一個(gè)的顯示可缚,這個(gè)我覺得是不合理的。上面的布局文件我也貼一下吧斋枢。有一點(diǎn)大家還是要注意下帘靡,因?yàn)槲以谕晟频倪^程中其實(shí)遇到了很多種情況的BUG,所以最終需要大家再BaseActivity中的onDestory()方法中去手動調(diào)用一下EToast.reset();具體可以看源碼中的解釋瓤帚。
etoast.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:id="@+id/mbContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="50dp"
    android:paddingRight="50dp"
    android:layout_marginBottom="50dp"
    android:gravity="bottom|center">
    <LinearLayout
        android:id="@+id/toast_linear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/shape_eroast_bg"
        android:gravity="bottom|center"
        android:padding="5dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/mbMessage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:layout_margin="5dp"
            android:layout_gravity="center"
            android:textColor="#ffffffff"
            android:shadowColor="#BB000000"
            android:shadowRadius="2.75"/>
    </LinearLayout>
</LinearLayout>

shape_eroast_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 實(shí)心 -->
    <solid
        android:color="@color/BlackTransparent" />
    <corners
        android:radius="45dp"
        />
</shape>

優(yōu)化

上面的幾種方式我大致也都走了一遍描姚,其實(shí)我覺得都沒啥區(qū)別涩赢,看你喜歡用哪種吧。我其實(shí)是采用了第四種轩勘,因?yàn)榈谝环N的話我是不喜歡隊(duì)列的筒扒,比如5個(gè)Toast排隊(duì)還要一個(gè)一個(gè)等待顯示,這樣的體驗(yàn)我是不喜歡的绊寻。第二種就不推薦了花墩,因?yàn)橛稚婕暗搅似渌臋?quán)限。第三種我沒試榛斯,實(shí)現(xiàn)應(yīng)該是不難的观游,效果的話也是隨你喜歡。最后我采用的是第四種驮俗,因?yàn)檫@種方式之前是沒有用到過的懂缕,也嘗試一下。

那么來說說優(yōu)化王凑,如果直接替換掉系統(tǒng)的Toast搪柑,那相當(dāng)?shù)谋┝Γ隙ㄍ淄椎乃髋搿D敲次覀兡懿荒苤悄艿娜ヅ袛嘁幌履毓つ耄绻脩魶]有關(guān)閉通知權(quán)限,那么久跟隨系統(tǒng)的Toast去吧百姓,這樣好讓App采用系統(tǒng)風(fēng)格渊额,對吧。
方法是有的垒拢,如下:

/**
 * 用來判斷是否開啟通知權(quán)限
 * */
    private static boolean isNotificationEnabled(Context context){

        AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        ApplicationInfo appInfo = context.getApplicationInfo();

        String pkg = context.getApplicationContext().getPackageName();

        int uid = appInfo.uid;

        Class appOpsClass = null; /* Context.APP_OPS_MANAGER */

        try {

            appOpsClass = Class.forName(AppOpsManager.class.getName());

            Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);

            Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
            int value = (int)opPostNotificationValue.get(Integer.class);
            return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

據(jù)說android24 可以使用NotificationManagerCompat.areNotificationsEnabled()來判斷旬迹,具體大家可以嘗試。那么如何來替換老項(xiàng)目中的Toast呢求类?
我這邊的話自定義Toast就是EToast了奔垦。為什么要E開頭呢,因?yàn)楣?.....你懂的尸疆。然后寫一個(gè)Toast的工具類椿猎,如下:

/**
 * Created by blin on 2016/10/11.
 */

public class Toast {
    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
    private static int checkNotification = 0;
    private Object mToast;
    public static final int LENGTH_SHORT = 0;
    public static final int LENGTH_LONG = 1;
    private Toast(Context context, String message, int duration) {
        try{
            checkNotification = isNotificationEnabled(context) ? 0 : 1;
            if (checkNotification == 1 && context instanceof Activity) {
                mToast = EToast.makeText(context, message, duration);
            } else {
                mToast = android.widget.Toast.makeText(context, message, duration);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private Toast(Context context, int resId, int duration) {
        if (checkNotification == -1){
            checkNotification = isNotificationEnabled(context) ? 0 : 1;
        }

        if (checkNotification == 1 && context instanceof Activity) {
            mToast = EToast.makeText(context, resId, duration);
        } else {
            mToast = android.widget.Toast.makeText(context, resId, duration);
        }
    }

    public static Toast makeText(Context context, String message, int duration) {
        return new Toast(context,message,duration);
    }
    public static Toast makeText(Context context, int resId, int duration) {
        return new Toast(context,resId,duration);
    }

    public void show() {
        if(mToast instanceof EToast){
            ((EToast) mToast).show();
        }else if(mToast instanceof android.widget.Toast){
            ((android.widget.Toast) mToast).show();
        }
    }
    public void cancel(){
        if(mToast instanceof EToast){
            ((EToast) mToast).cancel();
        }else if(mToast instanceof android.widget.Toast){
            ((android.widget.Toast) mToast).cancel();
        }
    }
    public void setText(int resId){
        if(mToast instanceof EToast){
            ((EToast) mToast).setText(resId);
        }else if(mToast instanceof android.widget.Toast){
            ((android.widget.Toast) mToast).setText(resId);
        }
    }
    public void setText(CharSequence s){
        if(mToast instanceof EToast){
            ((EToast) mToast).setText(s);
        }else if(mToast instanceof android.widget.Toast){
            ((android.widget.Toast) mToast).setText(s);
        }
    }
    /**
     * 用來判斷是否開啟通知權(quán)限
     * */
    private static boolean isNotificationEnabled(Context context){
        if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT){
            return true;
        }
        AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        ApplicationInfo appInfo = context.getApplicationInfo();

        String pkg = context.getApplicationContext().getPackageName();

        int uid = appInfo.uid;

        Class appOpsClass = null; /* Context.APP_OPS_MANAGER */

        try {

            appOpsClass = Class.forName(AppOpsManager.class.getName());

            Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);

            Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
            int value = (int)opPostNotificationValue.get(Integer.class);
            return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

然后直接把你項(xiàng)目的import android.widget.Toast 全局替換成import 你Toast的包名 即可。

總結(jié)

最后呢寿弱,提前祝大家周末愉快犯眠,這周瘋狂的七天工作日過了明天就要結(jié)束了,開黑開黑V⒏铩阔逼!哈哈哈~

ps:如果發(fā)現(xiàn)我上面有什么問題或者有好的解決方案的話歡迎和我留言討論,比比在此謝過啦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末地沮,一起剝皮案震驚了整個(gè)濱河市嗜浮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摩疑,老刑警劉巖危融,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雷袋,居然都是意外死亡吉殃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門楷怒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛋勺,“玉大人,你說我怎么就攤上這事鸠删”辏” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵刃泡,是天一觀的道長巧娱。 經(jīng)常有香客問我,道長烘贴,這世上最難降的妖魔是什么禁添? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮桨踪,結(jié)果婚禮上老翘,老公的妹妹穿的比我還像新娘。我一直安慰自己锻离,他們只是感情好铺峭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纳账,像睡著了一般逛薇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疏虫,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天永罚,我揣著相機(jī)與錄音,去河邊找鬼卧秘。 笑死呢袱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的翅敌。 我是一名探鬼主播羞福,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚯涮!你這毒婦竟也來了治专?” 一聲冷哼從身側(cè)響起卖陵,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎张峰,沒想到半個(gè)月后泪蔫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喘批,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年撩荣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饶深。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡餐曹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敌厘,到底是詐尸還是另有隱情台猴,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布额湘,位于F島的核電站卿吐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锋华。R本人自食惡果不足惜嗡官,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毯焕。 院中可真熱鬧衍腥,春花似錦、人聲如沸纳猫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菠红。三九已至簸喂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侵续,已是汗流浹背倔丈。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留状蜗,地道東北人需五。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像轧坎,于是被迫代替她去往敵國和親宏邮。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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