淺析Android 消息機制

消息機制存在的意義

  • 為什么不能在非UI線程中操作UI控件停做?
    因為Android的UI控件不是線程安全的邑跪,如果在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài),
  • 為什么不對UI控件加上鎖機制汉规?
    首先加上鎖會讓UI訪問的邏輯變得復雜恭朗;其次鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些線程的執(zhí)行
  • Android是在哪兒校驗UI操作是否是在UI線程哥攘?
//ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
    ... ...
    mThread = Thread.currentThread();
    ... ... 
}

//該方法會多個方法的開頭被調(diào)用剖煌,例如requestLayout()、invalidateChildInParent()等等... ...
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}

消息機制原理

Android系統(tǒng)主要通過Message逝淹、MessageQueue耕姊、Looper、Handler三個類來實現(xiàn)消息處理栅葡,其中Message代表消息茉兰,MessageQueue是用來描述消息隊列欣簇,Looper是用來創(chuàng)建消息隊列以及進入消息循環(huán)坯约,Handler 是用來發(fā)送消息和處理消息。(以下源碼均來自API 26 Android 8.0鬼店,源碼在線查看:http://androidxref.com),先看下整個事件的整體脈絡黔龟,建議根據(jù)圖然后去找對應的函數(shù)調(diào)用。大圖點擊

Handler.jpg

Handler

  • Handler的成員變量
//可能造成內(nèi)存泄露
private static final boolean FIND_POTENTIAL_LEAKS = false;

//是否為異步巍棱,默認為false
final boolean mAsynchronous;
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
  • Handler的創(chuàng)建和獲取
public Handler() { this(null, false); }
public Handler(Callback callback, boolean async) {
    //檢測內(nèi)存是否存在內(nèi)存泄露的可能
    ... ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        //必須先執(zhí)行Looper.prepare()的原因
        throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
  • 消息發(fā)送
//最基本的消息發(fā)送
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }
public final boolean sendEmptyMessage(int what){ return sendEmptyMessageDelayed(what, 0); }

//延時發(fā)送消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }

    //SystemClock.uptimeMisllis()是系統(tǒng)啟動至今的時間
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//在未來某一確定的時間點發(fā)送消息(發(fā)送消息最終的調(diào)用在這)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //Message中保存的Handler 對象
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • 消息處理
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 該Callback實在初始化的時候傳進來
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

//Handler 的內(nèi)部接口
public interface Callback {
    public boolean handleMessage(Message msg);
}

Looper

  • Looper的重要成員
//存儲不同線程的Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//主線程的Looper
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
  • Looper的創(chuàng)建和獲取
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        //Looper.prepare()只能執(zhí)行一次的原因
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

//準備主線程的Looper
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

//Looper構造器
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

//拿到當前線程的Looper對象
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

//拿到主線程的Looper對象
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
  • UI線程中Looper的創(chuàng)建
//ActivityThread.java
public static void main(String[] args) {
    ... ...
    Looper.prepareMainLooper();
    ... ...
    Looper.loop();
    ... ...
}
  • 消息循環(huán)
public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next();
        if (msg == null) {
            return;
        }
        ... ...
        msg.target.dispatchMessage(msg);
    }
}

MessageQueue

  • MessageQueue的重要成員
//該MessageQueue是否可退出航徙,通過Looper.prepare()創(chuàng)建的都是可退出的,UI線程的MessageQueue是不可以退出的
private final boolean mQuitAllowed;
//當前消息隊列的頭部    
Message mMessages;
//C++層NativeMessageQueue的地址
private long mPtr; 
//是否處于阻塞狀態(tài) 
private boolean mBlocked;
  • MessageQueue的創(chuàng)建
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //JNI調(diào)用初始化 C++ 層的 NativeMessageQueue.cpp
    mPtr = nativeInit();
}
  • 消息入隊
boolean enqueueMessage(Message msg, long when) {
    ... ...
    synchronized (this) {
        ... ...
        msg.when = when;
        Message p = mMessages; 
        boolean needWake;

        //頭部等于空到踏,或者入隊消息的時間小于當前時間就插到隊首
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //尋找隊尾或者在自己發(fā)送時間之后的位置,然后插入隊列中
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; 
            prev.next = msg;
        }
        if (needWake) {
            //JNI調(diào)用尚猿,喚醒NativeMessageQueue
            nativeWake(mPtr);
        }
    }
    return true;
}
  • 消息出隊
Message next() {    
    //執(zhí)行下一條消息時還需要等待的時間窝稿,當為-1時代表消息隊列中沒有消息,則無限等待
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ... ...
        //JNI調(diào)用凿掂,阻塞操作伴榔,當?shù)却齨extPollTimeoutMillis時長,或者消息隊列被喚醒庄萎,都會返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            ... ...
            if (msg != null) {
                if (now < msg.when) {
                    //當異步消息觸發(fā)時間大于當前時間踪少,則設置下一次輪詢的超時時長
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 獲取一條消息,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                     //成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
                    return msg;
                }
            } else {
                //沒有消息
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出糠涛,返回null(調(diào)用了quit()方法)
            if (mQuitting) {
                dispose();
                return null;
            }
            ... ...
        }
        ... ...
    }
}

NativeMessageQueu

//Java層中MessageQueue的nativaInit()方法
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ... ...
    //將NativeMessageQueue對象的指針強轉(zhuǎn)為Java中的long類型并返回
    return reinterpret_cast<jlong>(nativeMessageQueue);
}
  • NativeMessageQueue的構造器
//和Java層的MessageQueue的構造器很類似
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    //獲取當前線程的Looper援奢,類似Java層的myLooper()
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

Looper.cpp

  • 構造器
Looper::Looper(bool allowNonCallbacks)  {
    //構造一個喚醒事件的文件描述符,能在用戶態(tài)用做事件wait/notify機制忍捡,通過內(nèi)核取喚醒用戶態(tài)的事件集漾。
    //這個對象保存了一個內(nèi)核維護的uint64_t類型的整型counter。這個counter初始值被參數(shù)一指定锉罐,一般初值設置為0帆竹。
    //read:如果計數(shù)值counter的值不為0,讀取成功脓规,獲得到該值栽连。如果counter的值為0,非阻塞模式,會直接返回失敗秒紧,并把errno的值指紋EINVAL绢陌。
    //如果為阻塞模式,一直會阻塞到counter為非0位置熔恢。
    //write:會增加8字節(jié)的整數(shù)在計數(shù)器counter上脐湾,如果counter的值達到0xfffffffffffffffe時,就會阻塞叙淌。直到counter的值被read秤掌。阻塞和非阻塞情況同上面read一樣。
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    ... ...
}
  • 喚醒鎖
void Looper::wake() {
    uint64_t inc = 1;
    //向喚醒事件的文件中寫入數(shù)據(jù)
    write(mWakeEventFd, &inc, sizeof(uint64_t));
}
  • 輪詢
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for(;;){
        ... ... 
        if (result != 0) {
            ... ...
            return result;
        }
        result = pollInner(timeoutMillis);
    }
}
int Looper::pollInner(int timeoutMillis) {
    ... ...
    //等待被喚醒鹰霍,喚醒時間為timeoutMillis闻鉴,如果timeoutMillis<0,則等待I/O事件喚醒
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ... ...
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken(); //已經(jīng)喚醒了茂洒,則讀取并清空管道數(shù)據(jù)
            }
        } else {
            ... ...
        }
        ... ...
    }
    ... ...     
    return result;
}
void Looper::awoken() {
    uint64_t counter;
    //不斷讀取管道數(shù)據(jù)孟岛,目的就是為了清空管道內(nèi)容
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}

疑點

Looper.loop()已經(jīng)進入死循環(huán),那么主線程的其他操作是如何執(zhí)行的督勺?

  • 首先從線程的角度分析:一個線程是會在什么時間結束渠羞?很明顯是該線程中的代碼執(zhí)行完成后就會結束次询,然后該線程就會被JVM回收盏触。那么如何保證一個線程永遠處于運行狀態(tài)?貌似只有死循環(huán)。所以這么設計肯定是合理的授艰,不然主線程早已被回收了,之所以一直存在就是因為Looper.loop()一直在執(zhí)行糟需。
  • 這時候第二個問題就出現(xiàn)了谷朝,如果Looper.loop()一直在阻塞等待,那么UI的繪制是如何執(zhí)行杈帐?那么只能從MainLooper的源頭查起,沒錯就是ActivityThread:
final ApplicationThread mAppThread = new ApplicationThread();
final H mH = new H();
public static void main(String[] args) {
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    //這時候就是進入死循環(huán)累铅,一旦退出循環(huán)直接拋出異常
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
//ActivityThread的內(nèi)部類站叼,在這個類中有許多發(fā)送消息的方法,并且從方法中的參數(shù)可以看出都是與UI線程相關的操作投储,并且我們知道Binder的服務端都是運行在Binder線程池中
private class ApplicationThread extends IApplicationThread.Stub {
    @Override
    public final void scheduleLaunchActivity(... ...) {
        ... ...
        sendMessage(H.LAUNCH_ACTIVITY, r);
    }    

    public final void schedulePauseActivity(... ...) {
        ... ...
        sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, ... ...);               
    }
    ... ...
}
//ActivityThread的內(nèi)部類阔馋,該類繼承自Handler,并且處理的消息都是與UI線程相關
private class H extends Handler {
    switch (code) {
        case LAUNCH_ACTIVITY: 
            ... ...
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            break;
        case PAUSE_ACTIVITY: 
            handlePauseActivity(... ...);
            break;
        ... ...
    }
}

所以根據(jù)以上代碼冲泥,可以大致分析出凡恍,UI線程本質(zhì)的的所有操作都是有MainHanlder來進行處理怔球,而發(fā)送這些消息的操作就分別在不同的子線程中,例如與生命周期相關的都是在ApplicationThread的線程中闽巩,下面從網(wǎng)上盜了張圖:Android中為什么主線程不會因為Looper.loop里的死循環(huán)卡死担汤?

image.png

所以說主線程不能獨活,必須依賴于其他線程來給主線程發(fā)送消息隅很,而我們自己在主線程里面發(fā)送消息無疑是來自于另一個線程先發(fā)的消息率碾,例如我們經(jīng)常會在Activity的生命周期中進行發(fā)送消息,而這些生命周期方法的執(zhí)行也是來自與另一個線程的消息绒尊,就相當于我們在handlerMessage方法中再次發(fā)送消息下面的Demo可以說明上述結論是正確的仔粥。

public class MainActivity extends AppCompatActivity {
    private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("TestHandler","Start Thread One");
                Looper.prepare();
                handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Log.d("TestHandler",msg.what+" = "+System.currentTimeMillis());
                        if (msg.what == 3){
                            Log.d("TestHandler","send Message!");
                            sendEmptyMessageDelayed(4,1000);
                            sendEmptyMessage(5);
                        }
                    }
                };
                Looper.loop();
                handler.sendEmptyMessageDelayed(1,500);
                handler.sendEmptyMessage(2);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("TestHandler","Start Thread Two");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.sendEmptyMessage(3);
            }
        }).start();
    }
}

//輸出
03-21 22:21:31.132 21995-22032/? D/TestHandler: Start Thread One
03-21 22:21:31.133 21995-22033/? D/TestHandler: Start Thread Two
03-21 22:21:31.633 21995-22032/? D/TestHandler: 3 = 1521642091633
03-21 22:21:31.633 21995-22032/? D/TestHandler: send Message!
03-21 22:21:31.633 21995-22032/? D/TestHandler: 5 = 1521642091633
03-21 22:21:32.635 21995-22032/com.whf.test D/TestHandler: 4 = 1521642092635?

內(nèi)存泄露問題

  • 為什么會發(fā)生內(nèi)存泄露矮湘?
    • Java 回收機制是根據(jù)可達性來判別口糕,即一個對象如果是沒有被其他對象引用(即不可達的),這時候在GC的過程就會把這個對象列為可回收的十办,在下一次的GC的時候就會執(zhí)行回收。
    • 一個非靜態(tài)內(nèi)部類會持有外部類的引用向族。
    • Message對象的taget屬性引用了Handler對象棠绘。
      基于以上三點可知,當一個Activity中有一個非靜態(tài)的Hanlder類夜矗,該Handler類就會持有該Activity的引用让虐,如果當該Activitv退出的時候,其內(nèi)部的Handler發(fā)送的Message還沒有被處理完对扶,而這些Message對象都會持有該Activity的引用惭缰,所以就會導致Activity無法被回收,從而造成內(nèi)存泄露逞泄。
  • Handler如何避免內(nèi)存泄露拜效?
    • 以靜態(tài)內(nèi)部類或外部類的形式存在紧憾,不要在Activity中寫非靜態(tài)的Handler類
    • Handler的靜態(tài)內(nèi)部類訪問外部類時采用弱引用
    • 在Activity回收前昌渤,清空MessageQueue中存儲的該Handler發(fā)送的Message,通過調(diào)用Handler的removeCallbackAndMessages()方法般眉,該方法原理如下:
//Handle
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}
//MessageQueue
void removeCallbacksAndMessages(Handler h, Object object) {
    synchronized (this) {
        Message p = mMessages;     
        while (p != null && p.target == h && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

HandlerThread


public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    public int getThreadId() {
        return mTid;
    }
}

IntentService

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            //stopSelf(int)來保證你當前停止Service的請求是基于上一個請求的柿汛。也就是說络断,當你調(diào)用stopSelf(int)项玛,你把startID傳給了對應的要停止的Service,
            //這個startID是上一個請求的StartID襟沮!!如果沒有第二個請求來,那么這個Service就會死掉膀跌,但是如果這個Service已經(jīng)又接受到一個新的啟動請求之后硅则,
            //你才調(diào)用stopSelf(int),那么你傳遞給stopSelf()的ID是上一個請求的ID暑认,而當前Service的startID已經(jīng)更新為新的請求的ID大审,造成兩個ID不對應,stopSelf()失效粮彤,
            //那么Service就不會停止姜骡。這樣就避免了將后面的請求終止
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末圈澈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子递递,更是在濱河造成了極大的恐慌,老刑警劉巖登舞,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疙剑,居然都是意外死亡稽煤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門轧简,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匾二,“玉大人,你說我怎么就攤上這事皮璧》址桑” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵讯檐,是天一觀的道長染服。 經(jīng)常有香客問我,道長柳刮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任痢毒,我火速辦了婚禮蚕甥,結果婚禮上,老公的妹妹穿的比我還像新娘夷家。我一直安慰自己敏释,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布义屏。 她就那樣靜靜地躺著蜂大,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奶浦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天隙咸,我揣著相機與錄音成洗,去河邊找鬼。 笑死充包,一個胖子當著我的面吹牛遥椿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播修壕,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼慈鸠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了青团?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤芦昔,失蹤者是張志新(化名)和其女友劉穎娃肿,沒想到半個月后珠十,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焙蹭,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嫂伞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撰豺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拼余。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寡润,靈堂內(nèi)的尸體忽然破棺而出舅柜,到底是詐尸還是另有隱情,我是刑警寧澤致份,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站绍载,受9級特大地震影響滔蝉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝠引,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一螃概、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吊洼,春花似錦尝盼、人聲如沸腺阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樱调,卻和暖如春笆凌,著一層夾襖步出監(jiān)牢的瞬間士葫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工爪模, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屋灌。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓应狱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親除嘹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349