Architecture Components之LiveData的擴(kuò)展

最近研究了一下Architecture Components趁猴,嘗試用這個架構(gòu)寫了一個小demo刊咳,發(fā)現(xiàn)了一些問題,也有了一些心得儡司,想分享給大家娱挨。

以下關(guān)于LiveData的基礎(chǔ)使用摘自:[譯] Architecture Components 之 LiveData

LiveData

LiveData 是一個數(shù)據(jù)持有者類,它持有一個值并允許觀察該值捕犬。不同于普通的可觀察者跷坝,LiveData 遵守應(yīng)用程序組件的生命周期酵镜,以便 Observer 可以指定一個其應(yīng)該遵守的 Lifecycle。

LiveData主要方法

  • onActive()

當(dāng) LiveData 有一個處于活動狀態(tài)的觀察者時該方法被調(diào)用柴钻,這意味著需要開始從設(shè)備觀察位置更新淮韭。

  • onInactive()

當(dāng) LiveData 沒有任何處于活動狀態(tài)的觀察者時該方法被調(diào)用。由于沒有觀察者在監(jiān)聽贴届,所以沒有理由保持與 LocationManager 的連接靠粪。這是非常重要的,因?yàn)楸3诌B接會顯著消耗電量并且沒有任何好處毫蚓。

  • setValue()

調(diào)用該方法更新 LiveData 實(shí)例的值占键,并將此變更通知給處于活動狀態(tài)的觀察者。

可以通過addObserver添加數(shù)據(jù)的觀察者來更新界面绍些,展示新的數(shù)據(jù)捞慌。

  liveData.addObserver(this, location -> {
                    // 更新 UI
                });

請注意,addObserver() 方法將 LifecycleOwner 作為第一個參數(shù)傳遞(即Activity或者Fragment)柬批。這樣做表示該觀察者應(yīng)該綁定到 Lifecycle,意思是:

  • 如果 Lifecycle 不處于活動狀態(tài)(STARTED 或 RESUMED)袖订,即使該值發(fā)生變化也不會調(diào)用觀察者氮帐。

  • 如果 Lifecycle 被銷毀,那么自動移除觀察者洛姑。

LiveData 有以下優(yōu)點(diǎn):

  • 沒有內(nèi)存泄漏:因?yàn)?Observer 被綁定到它們自己的 Lifecycle 對象上上沐,所以,當(dāng)它們的 Lifecycle 被銷毀時楞艾,它們能自動的被清理参咙。

  • 不會因?yàn)?activity 停止而崩潰:如果 ObserverLifecycle 處于閑置狀態(tài)(例如:activity 在后臺時),它們不會收到變更事件硫眯。

  • 始終保持?jǐn)?shù)據(jù)最新:如果 Lifecycle 重新啟動(例如:activity 從后臺返回到啟動狀態(tài))將會收到最新的位置數(shù)據(jù)(除非還沒有)蕴侧。

  • 正確處理配置更改:如果 activity 或 fragment 由于配置更改(如:設(shè)備旋轉(zhuǎn))重新創(chuàng)建,將會立即收到最新的有效位置數(shù)據(jù)两入。

  • 資源共享:可以只保留一個 MyLocationListener 實(shí)例净宵,只連接系統(tǒng)服務(wù)一次,并且能夠正確的支持應(yīng)用程序中的所有觀察者裹纳。

  • 不再手動管理生命周期你可能已經(jīng)注意到择葡,fragment 只是在需要的時候觀察數(shù)據(jù),不用擔(dān)心被停止或者在停止之后啟動觀察剃氧。由于 fragment 在觀察數(shù)據(jù)時提供了其 Lifecycle敏储,所以 LiveData 會自動管理這一切。

問題

看了以上的介紹朋鞍,發(fā)現(xiàn)LiveData還是非常好用的已添,等同于以前用rxLifecycle來管理生命周期妥箕,但是在實(shí)際使用的時候就發(fā)現(xiàn)問題了,LiveData只能傳遞一個值酝碳,之前我們用Retrofit+OkHttp+rxJava等構(gòu)建MVP模式的應(yīng)用時矾踱,網(wǎng)絡(luò)數(shù)據(jù)請求經(jīng)常會有多種結(jié)果:(1)正常返回?cái)?shù)據(jù),(2)接口返回疏哗,錯誤結(jié)果(3)網(wǎng)絡(luò)請求失敗 (4)列表無更多數(shù)據(jù)(5)接口正常返回呛讲,無數(shù)據(jù) 。返奉。贝搁。等等情況,之前我們會在Presenter層通過回調(diào)獲得這些發(fā)生在Model層的情況芽偏,然后調(diào)用View層改變界面的方法展示給用戶雷逆,但是使用LiveData時View層可以直接通過ViewModel獲得Model提供的LiveData,有數(shù)據(jù)時可以正常顯示污尉,但是異常時就顯得力不從心了膀哲,我們只能傳遞一個值:有值或者null,無法判斷復(fù)雜的具體的情況被碗。

final-architecture.png

上圖是官方提供的Architecture Components架構(gòu)示意圖某宪,其中的依賴關(guān)系應(yīng)該是單向的(隱藏的觀察、被觀察的關(guān)系不算)锐朴,即Activity/Fragment持有ViewModel的引用兴喂,ViewModel持有Repository,提供LiveData焚志。衣迷。。官方也提到ViewModel僅僅是一個LiveData的容器酱酬,不應(yīng)該持有Activity/Fragment的引用壶谒。

基于以上這種情況下如何根據(jù)不同的情況改變界面那?例如:彈出吐司岳悟,對話框佃迄,顯示網(wǎng)絡(luò)異常等等那?贵少?呵俏?啰嗦了這么半天,終于引出今天的話題——擴(kuò)展LiveData 以滿足需求滔灶。

擴(kuò)展

先來分析一下LiveData普碎,當(dāng)添加了觀察者,一旦調(diào)用setValue方法录平,觀察者的onChanged方法就會接受到新的值麻车。傳遞的值是通過LiveData<T>泛型定義缀皱。
既然是要區(qū)分類型,一開始的思路是將泛型定義成自定義的ActionEntity<T>动猬,有點(diǎn)類似網(wǎng)絡(luò)接口返回啤斗,id區(qū)分類型,extra附帶額外數(shù)據(jù)赁咙,original是原始的value數(shù)據(jù)钮莲,即將真實(shí)數(shù)據(jù)包裝一層,通過id來區(qū)分不同的情況:

public class ActionEntity<T> {
    public static final int ACTION = 0x1;
    public static final int VALUE = 0x2;

    public int type;

    public int id;
    public Object[] extra;
    public T original;

    public ActionEntity(T original) {
        this.original = original;
        type = VALUE;
    }

    public ActionEntity(int id, Object[] extra) {
        this.id = id;
        this.extra = extra;
        type = ACTION;
    }
}

接下來就是修改LiveData的setValue方法實(shí)現(xiàn)自動裝包彼水,讓使用時感知不到包裝的存在

import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.MediatorLiveData;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

/**
 * Created by hubert
 * <p>
 * Created on 2017/12/7.
 */

public class ActionLiveData2<T> extends MediatorLiveData<ActionEntity<T>> implements ActionCreator {

    @Override
    public void setAction(int id, Object... args) {
        super.setValue(new ActionEntity<T>(id, args));
    }

    public void setValue_(T value) {
        super.setValue(new ActionEntity<T>(value));
    }

    public void postValue_(T value) {
        super.postValue(new ActionEntity<T>(value));
    }

    public void observe_(@NonNull LifecycleOwner owner, @NonNull ActionObserver2<T> observer) {
        super.observe(owner, observer);
    }

    public void observeForever_(@NonNull ActionObserver2<T> observer) {
        super.observeForever(observer);
    }

    public void removeObserver_(@NonNull ActionObserver2<T> observer) {
        super.removeObserver(observer);
    }

    @Nullable
    public T getValue_() {
        ActionEntity<T> entity = super.getValue();
        return entity == null ? null : entity.original;
    }
}

修改Observer的onChanged方法實(shí)現(xiàn)自動拆包

import android.arch.lifecycle.Observer;
import android.support.annotation.Nullable;

/**
 * Created by hubert
 * <p>
 * Created on 2017/12/7.
 */

public abstract class ActionObserver2<T> implements Observer<ActionEntity<T>>, ActionHandler {

    @Override
    public final void onChanged(@Nullable ActionEntity<T> entity) {
        if (entity != null) {
            if (entity.type == ActionEntity.VALUE) {
                onChanged_(entity.original);
            } else {
                onAction(entity.id, entity.extra);
            }
        }
    }

    public abstract void onChanged_(T entity);
}

不知道你有沒有注意到很多方法最后都有_崔拥,這是由于對T泛型的數(shù)據(jù)做了改變,新包裝的方法無法使用原方法名凤覆,只能通過添加下劃線來區(qū)別原方法链瓦。
這樣修改的話直接使用的話是能達(dá)到我的目的,但是通常情況下LiveData會通過Transformations進(jìn)行轉(zhuǎn)換盯桦,這時候泛型將很難定義慈俯,而且無法申明我們定義的類,因?yàn)門ransformations的switchMap方法返回的必須是LiveData<T>拥峦,泛型只能定義成LiveData<ActionEntity<T>>肥卡,雖然我的定義的ActionLiveData2<T>也是繼承自LiveData<ActionEntity<T>>,但是并不相等笆铝汀!>疚浮璃哟!
于是自己都寫不下去了?? 并且這樣的命名也非常的讓我不爽,于是廢棄了這種方式喊递。

接著我又有了一個想法??随闪,直接讓LiveData傳遞Action,就像傳遞Value一樣地傳遞:通過setAction設(shè)置事件骚勘,并且在Observer中onAction方法中接受事件作出處理铐伴。這個可以有!仿照value是怎么傳遞的不就行了嘛俏讹!??
然后我仔細(xì)分析了LiveData的源碼当宴,了解了其中是如何傳遞Value的,但是其中處理value的方法基本都是private的泽疆,子類無法使用户矢。因此,我只能按照同樣的邏輯來實(shí)現(xiàn)Action的傳遞:


import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.arch.lifecycle.Observer;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Created by hubert on 2017/12/6.
 * <p>
 */
public class ActionLiveData<T> extends MutableLiveData<T> implements ActionCreator {

    private Set<ActionObserver<T>> actionObservers = new HashSet<>();
    private boolean active;//父類中有這個屬性殉疼,但是也是private

    private ActionEntity actionEntity;

    private Handler handler;
    private Runnable actionRun = new Runnable() {
        @Override
        public void run() {
            dispatchAction();
        }
    };

    private void dispatchAction() {
        if (active) {
            for (ActionObserver<T> actionObserver : actionObservers) {
                actionObserver.onAction(actionEntity.id, actionEntity.extra);
            }
        }
    }

    @Override
    protected void onActive() {
        super.onActive();
        active = true;
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        active = false;
    }

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        super.observe(owner, observer);
        if (observer instanceof ActionObserver) {
            actionObservers.add((ActionObserver<T>) observer);
        }
    }

    @Override
    public void removeObserver(@NonNull Observer<T> observer) {
        super.removeObserver(observer);
        if (observer instanceof ActionObserver) {
            actionObservers.remove(observer);
        }
    }
 
      /**
     * 設(shè)置事件
     * @param id 事件id
     * @param args 可選的參數(shù)
     */
    @Override
    public void setAction(int id, Object... args) {
        actionEntity = new ActionEntity(id, args);
        if (isMainThread()) {
            dispatchAction();
        } else {
            if (handler == null) {
                handler = new Handler(Looper.getMainLooper());
            }
            handler.post(actionRun);
        }
    }

    public boolean isMainThread() {
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

Action相關(guān)接口的聲明:

public interface ActionCreator {

    void setAction(int id, Object... args);
}

public interface ActionHandler {

    void onAction(int id, Object... args);
}

public interface ActionObserver<T> extends Observer<T>, ActionHandler {

}

使用時傳入ActionObserver復(fù)寫onAction(int id, Object... args)接收Action事件

actionLiveData.observe(this, new ActionObserver<Integer>() {
      @Override
       public void onAction(int id, Object... args) {
           if (id == 1) {
               //do something
           }
       }
 
      @Override
       public void onChanged(@Nullable Integer integer) {
           //the original value
       }
 });

完美梯浪!馬上把demo中的LiveData換成ActionLiveData跑上~ 沒有反應(yīng)??捌年,怎么可能,我的邏輯...應(yīng)該完美挂洛!?? 各種斷點(diǎn)找原因礼预,到底為什么沒有傳遞過來。虏劲。托酸。

最終被我發(fā)現(xiàn)了!

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

我的LiveData是通過Transformations.switchMap進(jìn)行轉(zhuǎn)換伙单,或者說傳遞获高,即讓一個LiveData(view層中獲得的那個,我們簡稱小v)觀察另一個LiveData(M層生成的小m)吻育,請求數(shù)據(jù)只會改變小m的value念秧,switchMap方法內(nèi)部其實(shí)就是給小m設(shè)置一個Observer,當(dāng)小m的value改變時會調(diào)用該Observer的onChanged方法布疼,在該方法中調(diào)用小v的setValue摊趾,這樣View層的Observer的onChanged方法就可以改變界面。
其中只處理了value的傳遞游两,并且返回的對象是方法內(nèi)部new的這個final MediatorLiveData<Y> result = new MediatorLiveData<>();砾层,我們自己新添加的action當(dāng)然不會被傳遞啦。
??既然這樣贱案,那我們就自己傳遞吧肛炮。還是仿照MediatorLiveData中傳遞方式,新增一個ActionSource類:


/**
 * Created by hubert on 2017/12/6.
 * <p>
 * LiveData本身只能有一種泛型的數(shù)據(jù)宝踪,在(接口)數(shù)據(jù)返回時只能設(shè)置有值或者null來判斷侨糟,
 * 無法傳遞其他信息,如需要提示網(wǎng)絡(luò)瘩燥,數(shù)據(jù)錯誤等情況秕重,為每一種情況定義一個LiveData又太過于繁瑣。
 * 基于以上考慮對LiveData進(jìn)行擴(kuò)展厉膀,使其支持傳遞自定義Action溶耘,
 * 通過調(diào)用{@code setAction(int id, Object... args)}發(fā)送事件。
 * 并在observe方法中傳入{@link ActionObserver}用于接收action事件作出處理服鹅。
 * <pre>
 * actionLiveData.observe(this, new ActionObserver<Integer>() {
 *      {@literal @}Override
 *      public void onAction(int id, Object... args) {
 *          if (id == 1) {
 *              //do something
 *          }
 *      }
 *
 *      {@literal @}Override
 *      public void onChanged(@Nullable Integer integer) {
 *          //the original value
 *      }
 * });
 * </pre>
 */
public class ActionLiveData<T> extends MediatorLiveData<T> implements ActionCreator {

    private Set<ActionObserver<T>> actionObservers = new HashSet<>();
    private boolean active;

    private ActionEntity actionEntity;

    private Handler handler;
    private Runnable actionRun = new Runnable() {
        @Override
        public void run() {
            dispatchAction();
        }
    };

    /**
     * 通知Observer更新事件
     */
    private void dispatchAction() {
        if (active) {
            for (ActionObserver<T> actionObserver : actionObservers) {
                actionObserver.onAction(actionEntity.id, actionEntity.extra);
            }
        }
    }

    @Override
    protected void onActive() {
        super.onActive();
        active = true;
        for (Map.Entry<ActionLiveData<?>, ActionSource<?>> entry : mHandlers.entrySet()) {
            entry.getValue().plug();
        }
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        active = false;
        for (Map.Entry<ActionLiveData<?>, ActionSource<?>> entry : mHandlers.entrySet()) {
            entry.getValue().unplug();
        }
    }

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        super.observe(owner, observer);
        if (observer instanceof ActionObserver) {
            actionObservers.add((ActionObserver<T>) observer);
        }
    }

    @Override
    public void removeObserver(@NonNull Observer<T> observer) {
        super.removeObserver(observer);
        if (observer instanceof ActionObserver) {
            actionObservers.remove(observer);
        }
    }

    /**
     * 設(shè)置事件
     * @param id 事件id
     * @param args 可選的參數(shù)
     */
    @Override
    public void setAction(int id, Object... args) {
        actionEntity = new ActionEntity(id, args);
        if (isMainThread()) {
            dispatchAction();
        } else {
            if (handler == null) {
                handler = new Handler(Looper.getMainLooper());
            }
            handler.post(actionRun);
        }
    }

    public boolean isMainThread() {
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

    /****支持Transformations的轉(zhuǎn)換***/

    private Map<ActionLiveData<?>, ActionSource<?>> mHandlers = new HashMap<>();

    @Override
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<S> onChanged) {
        super.addSource(source, onChanged);
        if (source instanceof ActionLiveData && onChanged instanceof ActionObserver) {
            addActionObserver((ActionLiveData<S>) source, (ActionObserver<S>) onChanged);
        }
    }

    protected <S> void addActionObserver(ActionLiveData<S> source, ActionObserver<S> actionObserver) {
        ActionSource<S> actionSource = new ActionSource<>(source, actionObserver);
        ActionSource<?> existing = mHandlers.put(source, actionSource);
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            actionSource.plug();
        }
    }

    @Override
    public <S> void removeSource(@NonNull LiveData<S> toRemote) {
        super.removeSource(toRemote);
        if (toRemote instanceof ActionLiveData) {
            removeActionSource(toRemote);
        }
    }

    protected <S> void removeActionSource(@NonNull LiveData<S> toRemote) {
        ActionSource<?> source = mHandlers.remove(toRemote);
        if (source != null) {
            source.unplug();
        }
    }

    public static class ActionSource<T> implements ActionHandler {

        ActionLiveData<T> actionLiveData;
        ActionObserver<T> actionObserver;

        public ActionSource(ActionLiveData<T> actionLiveData, ActionObserver<T> actionObserver) {
            this.actionLiveData = actionLiveData;
            this.actionObserver = actionObserver;
        }

        void plug() {
            actionLiveData.observeForever(actionObserver);
        }

        void unplug() {
            actionLiveData.removeObserver(actionObserver);
        }

        @Override
        public void onAction(int id, Object... args) {
            actionObserver.onAction(id, args);
        }
    }
}

定義ActionTransformations修改Transformations的邏輯凳兵,以實(shí)現(xiàn)action事件的傳遞:


import android.arch.core.util.Function;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

/**
 * Created by hubert on 2017/12/7.
 * <p>
 * 用于ActionLiveData的轉(zhuǎn)換,實(shí)現(xiàn)Action的傳遞
 */

public class ActionTransformations {

    @MainThread
    public static <X, Y> ActionLiveData<Y> map(@NonNull ActionLiveData<X> source,
                                               @NonNull final Function<X, Y> func) {
        final ActionLiveData<Y> result = new ActionLiveData<>();
        result.addSource(source, new ActionObserver<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }

            @Override
            public void onAction(int id, Object... args) {
                result.setAction(id, args);
            }
        });
        return result;
    }

    @MainThread
    public static <X, Y> ActionLiveData<Y> switchMap(@NonNull LiveData<X> trigger,
                                                     @NonNull final Function<X, ActionLiveData<Y>> func) {
        final ActionLiveData<Y> result = new ActionLiveData<>();
        result.addSource(trigger, new Observer<X>() {
            ActionLiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                ActionLiveData<Y> newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new ActionObserver<Y>() {
                        @Override
                        public void onAction(int id, Object... args) {
                            result.setAction(id, args);
                        }

                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
}

使用時也沒有多大的改變菱魔,只要把LiveData替換成ActionLiveData即可留荔。開始盡情的傳遞事件吧~ ??

這個擴(kuò)展是本人根據(jù)需求獨(dú)創(chuàng)的,如果覺得好不要吝惜你的“喜歡”哦。 當(dāng)然由于水平有限聚蝶,如果入不了你的眼杰妓,那肯定是比我厲害的大牛,有更好的建議或者思路可以提點(diǎn)我一下??碘勉,讓我也學(xué)習(xí)學(xué)習(xí)~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巷挥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子验靡,更是在濱河造成了極大的恐慌倍宾,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胜嗓,死亡現(xiàn)場離奇詭異高职,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辞州,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門怔锌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人变过,你說我怎么就攤上這事埃元。” “怎么了媚狰?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵岛杀,是天一觀的道長。 經(jīng)常有香客問我崭孤,道長类嗤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任辨宠,我火速辦了婚禮土浸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘彭羹。我一直安慰自己,他們只是感情好泪酱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布派殷。 她就那樣靜靜地躺著,像睡著了一般墓阀。 火紅的嫁衣襯著肌膚如雪毡惜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天斯撮,我揣著相機(jī)與錄音经伙,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛帕膜,可吹牛的內(nèi)容都是我干的枣氧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垮刹,長吁一口氣:“原來是場噩夢啊……” “哼达吞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荒典,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酪劫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寺董,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體覆糟,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年遮咖,在試婚紗的時候發(fā)現(xiàn)自己被綠了滩字。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盯滚,死狀恐怖踢械,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情魄藕,我是刑警寧澤内列,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站背率,受9級特大地震影響话瞧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寝姿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一交排、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饵筑,春花似錦埃篓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玄帕,卻和暖如春部脚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裤纹。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工委刘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓锡移,卻偏偏與公主長得像呕童,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子罩抗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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