ViewModel和LiveData 使用

官方代碼糠亩,建議將代碼下載下來對照著代碼閱讀纬朝。

ViewModel

舉個例子:我們在界面上有一個計時器,記錄我們在這個界面停留的時間,但是當(dāng)我們旋轉(zhuǎn)屏幕的時候鞍爱,會導(dǎo)致Activity重新創(chuàng)建實例贞滨,onCreate()方法會再次執(zhí)行,導(dǎo)致計時器會重新從0開始計時潮孽。

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.android.lifecycles.step1.ChronoActivity1">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:id="@+id/hello_textview"/>

    <Chronometer
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/hello_textview"
        android:layout_centerHorizontal="true"
        android:id="@+id/chronometer"/>
</RelativeLayout>
public class ChronoActivity1 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在onCreate()方法中開始倒計時
        Chronometer chronometer = findViewById(R.id.chronometer);
        long startTime = SystemClock.elapsedRealtime();
       //每次onCreate()方法都會重新設(shè)置base
        chronometer.setBase(startTime);
        chronometer.start();
    }
}

當(dāng)然我們可以通過其他手段解決這個問題揪荣,例如當(dāng)屏幕旋轉(zhuǎn)的時候不讓Activity重新創(chuàng)建實例筷黔。
或者我們可以在onSaveInstanceState()方法中保存相應(yīng)的數(shù)據(jù),然后當(dāng)Activity重新創(chuàng)建實例
的時候仗颈,我們在onCreate()方法中獲取保存的數(shù)據(jù)佛舱,然后設(shè)置計時器的開始時間椎例。然后我們再
看看ViewModel的表現(xiàn)。

自定義一個ChronometerViewModel繼承ViewModel

public class ChronometerViewModel extends ViewModel {

    @Nullable
    private Long mStartTime;

    @Nullable
    public Long getStartTime() {
        return mStartTime;
    }

    public void setStartTime(final long startTime) {
        this.mStartTime = startTime;
    }
}

    // 創(chuàng)建或者直接返回一個已經(jīng)存在的ViewModel
    ChronometerViewModel chronometerViewModel = ViewModelProviders.of(this)
    .get(ChronometerViewModel.class);
    if (chronometerViewModel.getStartTime() == null) {
        //chronometerViewModel如果沒設(shè)置過開始時間请祖,那么說明這個新的ViewModel,
       //所以給它設(shè)置開始時間
        long startTime = SystemClock.elapsedRealtime();
        chronometerViewModel.setStartTime(startTime);
        chronometer.setBase(startTime);
    } else {
        //否則ViewModel已經(jīng)在上個Activity的onCreate()方法中創(chuàng)建過了订歪,屏幕旋轉(zhuǎn)以后,
        //ViewModel會被保存,我們直接獲取ViewModel里持有的時間
        chronometer.setBase(chronometerViewModel.getStartTime());
    }
    chronometer.start();

這樣就可以解決屏幕旋轉(zhuǎn)以后重新從0開始計時的問題了肆捕。
我們看一下關(guān)鍵代碼

ChronometerViewModel chronometerViewModel = ViewModelProviders.of(this)
.get(ChronometerViewModel.class);

ViewModelProvidersof()方法刷晋,只看方法的注釋,不看方法體

/**
 * 創(chuàng)建一個ViewModelProvider ,只要傳入的Activity存活慎陵,ViewModelProvider 就會被一直保留
 * @param activity 一個activity, 在誰的生命周期內(nèi)眼虱,ViewModel會被保留
 * @return 一個ViewModelProvider 實例
 */
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    initializeFactoryIfNeeded(checkApplication(activity));
    return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}

ViewModelProvidersget()方法,只看方法的注釋席纽,不看方法體

//返回一個已經(jīng)存在的ViewModel或者創(chuàng)建一個新的ViewModel實例
@NonNull
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

看一下ViewModel被保留的周期

ViewModelScope.png

注意:Activity走了onDestroy()方法并不帶表這個Activity就結(jié)束了捏悬。可以通過ActivityisFinishing()方法來判斷润梯。我們發(fā)現(xiàn)过牙,在旋轉(zhuǎn)屏幕的時候isFinishing()方法返回false。在按下返回鍵的時候isFinishing()為true纺铭。

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy: " + isFinishing());
}

ViewModel:ViewModel是一個用來為Activity或者Fragment準(zhǔn)備和管理數(shù)據(jù)的類寇钉。ViewModel也可以用來處理Activity/Fragment和應(yīng)用其他部分的通信。

一個ViewModel的創(chuàng)建總是和一個作用域(一個 Fragment/Activity)有關(guān)舶赔,并且只要這個作用域存活摧莽,那么這個ViewModel會被一直保留。例如顿痪,如果作用域是一個Activity镊辕,那么ViewModel會保留直到Activity結(jié)束。

換句話說蚁袭,這意味著如果ViewModel的所有者征懈,例如一個Activity由于旋轉(zhuǎn)而被銷毀,但是ViewModel并不會銷毀揩悄,新創(chuàng)建的Activity的實例僅僅是重新關(guān)聯(lián)到已經(jīng)存在的ViewModel卖哎。
ViewModel存在的目的是為了獲取并保持對Activity/Fragment重要的信息。Activity/Fragment 應(yīng)該能夠觀察到 ViewModel的變化删性。ViewModel通常通過LiveData或者Data Binding來暴露信息亏娜。也可以通過其他任何可觀察的對象,例如RxJava中的ObserVable蹬挺。

ViewModel的唯一的作用是管理UI的數(shù)據(jù)维贺。ViewModel不能訪問UI或者持有Activity/Fragment的引用。

在Fragment之間共享ViewModel

舉個例子:在一個Activity中有兩個Fragment巴帮,每個Fragment里面都有一個SeekBar溯泣。當(dāng)其中一個SeekBar進(jìn)度改變的時候虐秋,也更新另外一個Fragment里面的SeekBar的進(jìn)度。

Activity什么也沒做垃沦,就是布局文件里有兩個Fragment

public class Activity_step5 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_step5_solution);
    }
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.android.lifecycles.step5_solution.Activity_step5">

    <fragment
        android:id="@+id/fragment1"
        android:name="com.example.android.lifecycles.step5_solution.Fragment_step5"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/fragment2"
        android:name="com.example.android.lifecycles.step5_solution.Fragment_step5"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

Fragment的實現(xiàn)

public class Fragment_step5 extends Fragment {

    private SeekBar mSeekBar;

    private SeekBarViewModel mSeekBarViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_step5, container, false);
        mSeekBar = root.findViewById(R.id.seekBar);

        mSeekBarViewModel = ViewModelProviders.of(getActivity()).get(SeekBarViewModel.class);

        subscribeSeekBar();

        return root;
    }

    private void subscribeSeekBar() {
        // Update the ViewModel when the SeekBar is changed.
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, 
boolean fromUser) {
                //如果是用戶改變了seekbar的進(jìn)度就更新ViewModel
                if (fromUser) {
                    Log.d("Step5", "Progress changed!");
                    mSeekBarViewModel.seekbarValue.setValue(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) { }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) { }
        });
        // 當(dāng)ViewModel改變了時候客给,更新seekBar的進(jìn)度
        mSeekBarViewModel.seekbarValue.observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer value) {
                if (value != null) {
                    mSeekBar.setProgress(value);
                }
            }
        });
    }
}

運行程序,可以看到當(dāng)手動改變其中一個SeekBar的進(jìn)度肢簿,另外一個也會跟著變

LiveData

上面說了靶剑,ViewModel通常通過LiveData或者Data Binding來暴露信息。也可以通過其他任何可觀察的對象池充,例如RxJava中的ObserVable抬虽。LiveData 與普通的Observable不同,LiveData是生命周期感知的纵菌,這意味著它尊重其他應(yīng)用程序組件的生命周期阐污,例如ActivityFragmentService咱圆。 LiveData生命周期感知能力確保 LiveData僅僅去更新那些處于生命周期活動狀態(tài)的觀察者笛辟。(感覺這個比較厲害了)

注意:正常情況應(yīng)該在onCreate中訂閱觀察者。如果在onStart或者onResume中訂閱會有問題:

  1. 比如當(dāng)前Activity onStop了序苏,然后從onStop重新onStart的時候又會訂閱一次手幢,導(dǎo)致重復(fù)訂閱。

接著上面的例子:
我們想在Activity之外忱详,每隔一秒鐘围来,更新Activity的UI。

新建一個LiveDataTimerViewModel

public class LiveDataTimerViewModel extends ViewModel {

    private static final int ONE_SECOND = 1000;
    //新建一個LiveData實例
    private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>();

    private long mInitialTime;

    public LiveDataTimerViewModel() {
        mInitialTime = SystemClock.elapsedRealtime();
        Timer timer = new Timer();

        // 每隔一秒更新一次
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                final long newValue = 
(SystemClock.elapsedRealtime() - mInitialTime) / 1000;
                // setValue() 不能再后臺線程調(diào)用匈睁,所以使用post到主線程
                mElapsedTime.postValue(newValue);
            }
        }, ONE_SECOND, ONE_SECOND);

    }

    public LiveData<Long> getElapsedTime() {
        return mElapsedTime;
    }
}
public class ChronoActivity3 extends AppCompatActivity {

    private LiveDataTimerViewModel mLiveDataTimerViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.chrono_activity_3);

        mLiveDataTimerViewModel = ViewModelProviders.of(this)
.get(LiveDataTimerViewModel.class);

        subscribe();
    }

    /**
     * 新建一個Observer,然后訂閱 mLiveDataTimerViewModel.getElapsedTime()
     */
    private void subscribe() {
        final Observer<Long> elapsedTimeObserver = new Observer<Long>() {
            @Override
            public void onChanged(@Nullable final Long aLong) {
                String newText = ChronoActivity3.this.getResources().getString(
                        R.string.seconds, aLong);
                ((TextView) findViewById(R.id.timer_textview)).setText(newText);
                Log.d("ChronoActivity3", "Updating timer");
            }
        };

        mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);
    }
}

運行上面的代碼你發(fā)現(xiàn)监透,只有在Activity活動的時候(也就是生命周期狀態(tài)是STARTEDRESUMED的時候),log日志才會輸出航唆,當(dāng)你點擊HOME鍵或者打開其他的APP的時候胀蛮,log日志不會輸出。當(dāng)你回到Activity的時候糯钙,日志才會接著輸出粪狼。

看一下LiveDataobserve()方法

 /**
  * 將指定的觀察者添加到LifecycleOwner生命周期之內(nèi)的觀察者列表中。事件是在主線程分發(fā)的任岸。如果LiveData已經(jīng)被設(shè)置了
  * 數(shù)據(jù)再榄,那么數(shù)據(jù)會被發(fā)送給這個新添加的觀察者。
  *
  * 只有當(dāng)LifecycleOwner處在活動狀態(tài)的時候{@link Lifecycle.State#STARTED} or
  * {@link Lifecycle.State#RESUMED} 享潜,這個observer才會收到事件困鸥。
  *
  * 如果LifecycleOwner到了銷毀狀態(tài) {@link Lifecycle.State#DESTROYED},這個observer會被自動移除米碰。
  * 
  * 當(dāng)這個LifecycleOwner處于不活動的狀態(tài)的時候窝革,如果數(shù)據(jù)改變了,這個observer不會收到任何更新吕座。
  * 當(dāng)LifecycleOwner重新回到了active的狀態(tài)虐译,這個oberver會自動收到最新的數(shù)據(jù)。
  * 
  * 只要指定的LifecycleOwner沒有被銷毀吴趴,LiveData就一直持有observer和LifecycleOwner的強引用
  * 當(dāng)LifecycleOwner被銷毀了漆诽,LiveData會移除observer和LifecycleOwner的引用。
  *
  * 如果指定的LifecycleOwner已經(jīng)處于銷毀狀態(tài){@linkLifecycle.State#DESTROYED} 锣枝,方法直接返回厢拭。 
  * 
  * 如果指定的LifecycleOwner和oberver元組已經(jīng)在觀察這列表里了,方法直接返回撇叁。
  *
  * 如果observer已經(jīng)和另外一個關(guān)聯(lián)的LifecycleOwner在觀察者列表里了供鸠,
  * LiveData拋出IllegalArgumentException。
  *
  * @param owner    LifecycleOwner陨闹,用來控制observer
  * @param observer 觀察者楞捂,用來接收事件
  */
@MainThread
public void observe(LifecycleOwner owner, @NonNull Observer<T> observer) {
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        //LifecycleOwner處于銷毀狀態(tài),直接返回趋厉。
        return;
    }
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && existing.owner != wrapper.owner) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

注意寨闹,AppCompatActivitySupportActivity的子類,而SupportActivity實現(xiàn)了LifecycleOwner君账。所以AppCompatActivity是一個生命周期持有者繁堡。

public class SupportActivity extends Activity implements LifecycleOwner {
...
}

訂閱生命周期事件

我們已經(jīng)知道SupportActivity是一個生命周期持有者了。一個生命周期持有者會在不同的生命周期發(fā)出不同的生命周期事件乡数。我們可以觀察這些事件椭蹄,并根據(jù)這些事件,進(jìn)行基于生命周期的操作净赴。

LifecycleOwner 獲取Lifecycle的方法

public interface LifecycleOwner {
    /**
     * 返回當(dāng)前生命周期持有者的生命周期
     */
    @NonNull
    Lifecycle getLifecycle();
}

Lifecycle類用來定義具有Android生命周期的對象塑娇。

Lifecycle類的內(nèi)部類Lifecycle.Event是一個枚舉類,定義了生命周期持有者發(fā)出的所有事件類型

枚舉值 描述
ON_ANY 可以用來匹配任何事件
ON_CREATE onCreate事件
ON_DESTROY onDestroy事件
ON_PAUSE onPause事件
ON_RESUME onResume事件
ON_CREATE onCreate事件
ON_START onStart事件
ON_STOP onStop事件

Lifecycle類的內(nèi)部類Lifecycle.State也是一個枚舉類劫侧,定義了生命周期持有者所有的生命周期狀態(tài)埋酬。如下圖所示。

state.png

舉個例子

我們想在Activity活動的時候烧栋,注冊一個LocationListener來獲取位置信息写妥,然后在onPause的時候,移除監(jiān)聽器审姓,那我們可以通過Activity的生命周期事件來實現(xiàn)珍特。

自定義的LocationListener

private class MyLocationListener implements LocationListener {
        @Override
        public void onLocationChanged(Location location) {
            //位置改變的時候,改變界面上的經(jīng)緯度
            TextView textView = findViewById(R.id.location);
            textView.setText(location.getLatitude() + ", " + location.getLongitude());
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        @Override
        public void onProviderEnabled(String provider) {
            Toast.makeText(LocationActivity.this,
                    "Provider enabled: " + provider, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onProviderDisabled(String provider) {
        }
    }

要觀察生命周期事件魔吐,首先要實現(xiàn)LifecycleObserver接口扎筒。
BoundLocationListener類實現(xiàn)了 LifecycleObserver接口莱找,當(dāng)生命周期持有者LifecycleOwner 處于ON_RESUME的狀態(tài)的時候,我們獲取定位服務(wù)嗜桌,并在位置變化的時候通知LocationListener更新信息奥溺。當(dāng)生命周期持有者LifecycleOwner 處于ON_PAUSE的狀態(tài)的時候我們移除LocationListener

public class BoundLocationManager {

    public static void bindLocationListenerIn(LifecycleOwner lifecycleOwner,
                                              LocationListener listener, Context context) {
        new BoundLocationListener(lifecycleOwner, listener, context);
    }

    @SuppressWarnings("MissingPermission")
    static class BoundLocationListener implements LifecycleObserver {
        private final Context mContext;
        private LocationManager mLocationManager;
        private final LocationListener mListener;

        public BoundLocationListener(LifecycleOwner lifecycleOwner,
                                     LocationListener listener, Context context) {
            mContext = context;
            mListener = listener;
            //注釋1處骨宠,觀察LifecycleOwner的生命周期事件
            lifecycleOwner.getLifecycle().addObserver(this);
        }
        
        //通過注解處理不同的生命周期事件
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        void addLocationListener() {
            mLocationManager =
                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
 mListener);
            Log.d("BoundLocationMgr", "Listener added");

            // Force an update with the last location, if available.
            Location lastLocation = mLocationManager.getLastKnownLocation(
                    LocationManager.GPS_PROVIDER);
            if (lastLocation != null) {
                mListener.onLocationChanged(lastLocation);
            }
        }
        
        //通過注解處理不同的生命周期事件
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        void removeLocationListener() {
            if (mLocationManager == null) {
                return;
            }
            mLocationManager.removeUpdates(mListener);
            mLocationManager = null;
            Log.d("BoundLocationMgr", "Listener removed");
        }
    }
}

注釋1處浮定,在BoundLocationListener的構(gòu)造函數(shù)中,觀察LifecycleOwner的生命周期事件层亿。然后使用注解在收到Lifecycle.Event.ON_RESUME事件的時候添加位置監(jiān)聽桦卒。在收到Lifecycle.Event.ON_RESUME事件的時候移除位置監(jiān)聽。

然后將生命周期持有者和生命周期事件觀察者綁定匿又。這里忽略定位權(quán)限的處理方灾。

public class LocationActivity extends AppCompatActivity {
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.location_activity);
            bindLocationListener();
        }

 private void bindLocationListener() {
        BoundLocationManager.bindLocationListenerIn(this, mGpsListener, 
                     getApplicationContext());
    }

}

運行程序,不斷旋轉(zhuǎn)手機的時候碌更,輸出如下

D/BoundLocationMgr: Listener added
D/BoundLocationMgr: Listener removed
D/BoundLocationMgr: Listener added
D/BoundLocationMgr: Listener removed

參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迎吵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子针贬,更是在濱河造成了極大的恐慌击费,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桦他,死亡現(xiàn)場離奇詭異蔫巩,居然都是意外死亡,警方通過查閱死者的電腦和手機快压,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門圆仔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蔫劣,你說我怎么就攤上這事坪郭。” “怎么了脉幢?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵歪沃,是天一觀的道長。 經(jīng)常有香客問我嫌松,道長沪曙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任萎羔,我火速辦了婚禮液走,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己缘眶,他們只是感情好嘱根,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巷懈,像睡著了一般该抒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砸喻,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天柔逼,我揣著相機與錄音蒋譬,去河邊找鬼割岛。 笑死,一個胖子當(dāng)著我的面吹牛犯助,可吹牛的內(nèi)容都是我干的癣漆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剂买,長吁一口氣:“原來是場噩夢啊……” “哼惠爽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瞬哼,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤婚肆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坐慰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體较性,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年结胀,在試婚紗的時候發(fā)現(xiàn)自己被綠了赞咙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡糟港,死狀恐怖攀操,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秸抚,我是刑警寧澤速和,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站剥汤,受9級特大地震影響健芭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秀姐,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一慈迈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦痒留、人聲如沸谴麦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匾效。三九已至,卻和暖如春恤磷,著一層夾襖步出監(jiān)牢的瞬間面哼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工扫步, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留魔策,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓河胎,卻偏偏與公主長得像闯袒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子游岳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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