DataBinding最佳實(shí)踐

本文會(huì)不定期更新妄辩,推薦watch下項(xiàng)目吟榴。如果喜歡請(qǐng)star,如果覺得有紕漏請(qǐng)?zhí)峤籭ssue乒验,如果你有更好的點(diǎn)子可以提交pull request尿贫。
本文的示例代碼主要是基于作者的經(jīng)驗(yàn)來(lái)編寫的电媳,若你有其他的技巧和方法可以參與進(jìn)來(lái)一起完善這篇文章。文章中大量參考和引用了DBinding權(quán)威使用指南的內(nèi)容庆亡,如果你想了解更多建議深入閱讀一下DBinding權(quán)威使用指南匾乓。

說(shuō)明:下文中vm是view model的縮寫

本文固定連接:https://github.com/tianzhijiexian/Android-Best-Practices

一、需求背景

開發(fā)者都希望可以更快更簡(jiǎn)單地編寫代碼又谋,并且還希望代碼的可維護(hù)性和健壯性能符合團(tuán)隊(duì)的期望拼缝。很多初創(chuàng)團(tuán)隊(duì)在發(fā)展多年后逐漸認(rèn)識(shí)到了早期代碼模式的弊端,并且在代碼的組織結(jié)構(gòu)上有了很多思考彰亥。
在模式方面咧七,2015年大家開始爭(zhēng)相討論mvc,mvp任斋,mvvm继阻,期間谷歌也推出了自家的數(shù)據(jù)綁定框架databinding,借此來(lái)簡(jiǎn)化代碼的編寫仁卷。在這一片百家爭(zhēng)鳴中穴翩,開發(fā)者十分希望能找到一個(gè)滿足項(xiàng)目需求并且穩(wěn)定可靠的框架來(lái)簡(jiǎn)化開發(fā)工作犬第。

二锦积、需求

開發(fā)者對(duì)于一個(gè)框架最看重的是下面幾點(diǎn):

  1. 能加快開發(fā)速度,屏蔽底層細(xì)節(jié)
  2. 代碼可讀性好歉嗓,易維護(hù)
  3. 代碼量越少越好丰介,易閱讀
  4. bug少,有不錯(cuò)的健壯性

三鉴分、實(shí)現(xiàn)

指定明確的分層

如果一個(gè)項(xiàng)目有了明確的分層結(jié)構(gòu)哮幢,那么代碼的可讀性和可維護(hù)性會(huì)上升很多,它也是一個(gè)架構(gòu)的基礎(chǔ)志珍。分層良好的的優(yōu)點(diǎn)有很多橙垢,而且即使某天要更換框架,也不會(huì)傷筋動(dòng)骨伦糯。
需要格外注意的是:只有當(dāng)一個(gè)項(xiàng)目的成員都能明確項(xiàng)目的層級(jí)后才可以談框架和模式柜某,否則一個(gè)框架再優(yōu)秀也無(wú)法在混亂中發(fā)揮出優(yōu)勢(shì)嗽元。

mvc
層名 內(nèi)容
view層 具體的view,activity喂击,fragment等剂癌,做ui展示、ui邏輯翰绊、ui動(dòng)畫
vm層 具體的視圖模型類佩谷,是view展示的數(shù)據(jù)的java映射,能被model層直接操作
model層 非ui層面的業(yè)務(wù)邏輯的實(shí)現(xiàn)监嗜。包含網(wǎng)絡(luò)請(qǐng)求谐檀,數(shù)據(jù)遍歷等操作,是很多具體類的抽象載體

DBinding是一個(gè)databinding的擴(kuò)展類秤茅,它提供了快速綁定vm和通過(guò)vm維持多個(gè)頁(yè)面之間數(shù)據(jù)同步等功能稚补,并且它還有強(qiáng)大的as插件來(lái)做支持,因此本文將選擇它作為mvvm框架框喳。

通過(guò)數(shù)據(jù)來(lái)更新UI

目前流行的做法都是通過(guò)數(shù)據(jù)來(lái)驅(qū)動(dòng)UI课幕,其優(yōu)點(diǎn)在于方便做單元測(cè)試和多人協(xié)作,對(duì)bug的定位也有比較好的幫助五垮。mvvm是一個(gè)抽象的概念乍惊,它目前最穩(wěn)定可靠的實(shí)現(xiàn)就是databinding,在用databinding之后放仗,我已經(jīng)很少到view層定位bug了润绎。databinding的代碼由xml代碼和java代碼構(gòu)成。
layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <!-- 定義變量: private org.kale.vm.UserViewModel user -->
        <variable
            name="user"
            type="org.kale.vm.UserViewModel"
            />
    </data>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@{user.name}"/>
</layout>  

Activity:

    private UserViewModel mUserVm = new UserViewModel();
    
     @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        DBinding.bindViewModel(this, R.layout.activity_main, mUserVm);
        mUserVm.setName("kale");  // textview中就會(huì)自動(dòng)渲染出文字了
    }

layout文件中的vm取名應(yīng)該和layout文件名字有關(guān)聯(lián)诞挨,layout文件的名字也應(yīng)該和activity的名字有關(guān)莉撇,這樣可以方便定位問(wèn)題和查找邏輯。layout中vm的參數(shù)完全可以模仿之前取id名字的思路惶傻,只不過(guò)千萬(wàn)不要加view的縮寫棍郎,出現(xiàn)tv_username或username_tv就鬧笑話了。layout文件中強(qiáng)烈不建議寫import語(yǔ)句银室,vm類名強(qiáng)制寫全稱涂佃。至于java代碼就十分簡(jiǎn)單了,沒有過(guò)多的要求蜈敢,只要對(duì)vm操作即可更新ui辜荠。

通過(guò)代碼模板快速生成layout文件

為了快速產(chǎn)生mvvm的layout文件,我利用了as提供的代碼模板功能抓狭。

快速建立layout模板
快速建立layout模板

下面就是創(chuàng)建好的代碼塊:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="org.kale.vm.UserViewModel"
            />
    </data>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@{user.name}"/>
</layout>  

通過(guò)插件自動(dòng)生成ViewModel

DBinding提供了強(qiáng)大的as插件來(lái)生成vm伯病,這樣就強(qiáng)制你不能隨意修改vm的內(nèi)容,將問(wèn)題屏蔽在了vm之外否过,這樣既加快了代碼的編寫速度又方便定位問(wèn)題午笛。

自動(dòng)生成vm
自動(dòng)生成vm

目前Dbinding的插件不能也永遠(yuǎn)不可能支持所有view的屬性的綁定膨蛮,但是你可通過(guò)配置的方式來(lái)讓其支持更多屬性,下面會(huì)演示如何給SimpleDraweeView增加的url的屬性季研。

在代碼中編寫適配器:

public class NetWorkImageViewAdapter {

    @BindingAdapter({"url"})
    public static void setUrl(SimpleDraweeView view, String url) {
        view.setImageURI(url);
    }

}

在value/dbinding_config.xml中進(jìn)行配置:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 
        For original view.
        Example: android:text="name"
    -->

    <!-- 
        For Custom view. 
        Example: app:customAttr="name"
    -->

    <string name="drawableStart">android.graphics.Bitmap</string>
    <string name="url">java.lang.String</string>

</resources>

這樣插件便會(huì)知道url對(duì)應(yīng)的類型敞葛,然后進(jìn)行生成對(duì)應(yīng)的vm的field。

歡迎你給DBinding庫(kù)提交代碼來(lái)讓庫(kù)原生支持你想要的屬性

利用ide來(lái)對(duì)vm進(jìn)行重構(gòu)操作

因?yàn)槟壳癮s對(duì)于layout中的vm的補(bǔ)全和重構(gòu)的支持力度不足与涡,所以推薦用下列方式進(jìn)行vm的重構(gòu)工作惹谐。

1.改名和改包名
如果要改vm的包名或改vm的類名的時(shí)候,最快捷的方式是進(jìn)入到這個(gè)類的實(shí)體中驼卖,通過(guò)ide的重構(gòu)工具進(jìn)行修改氨肌。這樣所有的改動(dòng)會(huì)自動(dòng)同步到使用了這個(gè)類的xml文件中去。當(dāng)然酌畜,你也可以在這個(gè)類被調(diào)用的地方通過(guò)重構(gòu)工具進(jìn)行改名怎囚。

改vm名和改包名
改vm名和改包名

2.刪除
刪除某個(gè)vm也是一樣的,仍舊是對(duì)java類進(jìn)行操作桥胞。刪除的時(shí)候注意排查下用到的地方恳守,以免出錯(cuò),這個(gè)排查工作真必須是手工做的贩虾。

刪除vm
刪除vm

3.給vm中的字段改名
我們先來(lái)看下插件會(huì)通過(guò)我們的xml生成什么東西:

package org.kale.vm;
public class UserviewModel extends BaseviewModel {
    private java.lang.CharSequence name;
    public final void setName(java.lang.CharSequence name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
    @Bindable
    public final java.lang.CharSequence getName() {
        return this.name;
    }
}

這里有我們定義的name字段和其get和set方法催烘。如果我們突然想把這個(gè)“name”改名為“nickname”,或者是刪除這個(gè)name字段呢缎罢?最好的做法就是直接重構(gòu)name這個(gè)字段伊群。
下面為了演示方便,減少干擾選項(xiàng)策精,我把name這個(gè)過(guò)于通用的字母先改成了nickname舰始,現(xiàn)在我將演示如何將nickname改為name。

字段改名
字段改名

4.從vm中刪除字段
因?yàn)閍s對(duì)于databinding的支持力度很低(未來(lái)或許就可以通過(guò)重構(gòu)工具來(lái)做了)咽袜,所以在重構(gòu)字段的時(shí)候只能我們自己去排查了丸卷。我的排查方案是通過(guò)檢索當(dāng)前類使用到的地方,來(lái)看下使用當(dāng)前類的xml中有沒有使用過(guò)我準(zhǔn)備刪除的字段酬蹋,如果有就進(jìn)行處理及老,如果沒有就直接刪除抽莱,以此來(lái)避免刪除后出現(xiàn)程序出錯(cuò)的問(wèn)題范抓。

刪除字段
刪除字段

禁止在layout中寫復(fù)雜邏輯

databinding原生提供了在xml中寫java語(yǔ)句的能力,也就是它允許你再xml中寫邏輯食铐。這點(diǎn)在DBinding中是強(qiáng)烈禁止的匕垫,如果你是通過(guò)dbinding的插件來(lái)生成vm的,那么你會(huì)發(fā)現(xiàn)你幾乎找不到在xml中寫java邏輯的需求虐呻。
至于這么做的原因是為了方便定位問(wèn)題象泵,一旦你將邏輯寫的四分五裂寞秃,那么出現(xiàn)了bug后開發(fā)者能否在第一時(shí)間知道具體邏輯這個(gè)先不談,就說(shuō)引起bug的可能性就有多個(gè)偶惠,試錯(cuò)和排查都會(huì)花很多的時(shí)間春寿。
如果你的團(tuán)隊(duì)協(xié)作,你把一些邏輯寫到了java中忽孽,一些寫到了xml中绑改,閱讀代碼的人必須要能理解這些才能真正的了解你的意圖,此外layout文件是具備復(fù)用能力的兄一,一旦你要復(fù)用layout厘线,那么這些xml中的邏輯便成了其無(wú)法復(fù)用的根源,因此我強(qiáng)烈禁止在xml中寫java邏輯出革。
在實(shí)際使用中我會(huì)發(fā)現(xiàn)我們經(jīng)常會(huì)根據(jù)字段來(lái)判斷是否要讓view顯示或隱藏造壮,如果都在java代碼中寫感覺會(huì)比較重一些。于是我嘗試在xml中寫了判斷是否顯示的邏輯骂束,后來(lái)發(fā)現(xiàn)即使layout被復(fù)用了耳璧,這種邏輯也是必然存在的,即使遇到不存在的情況轉(zhuǎn)為java代碼實(shí)現(xiàn)也是很簡(jiǎn)單的展箱。在定位問(wèn)題方面楞抡,如果知道xml中有這個(gè)邏輯的話也還好,所以我目前唯一能允許的就是在xml中寫控制view是否顯示的邏輯代碼析藕,其余的邏輯代碼一律禁止召廷。如果你也準(zhǔn)備這么寫,請(qǐng)務(wù)必讓你的團(tuán)隊(duì)接受并了解這種機(jī)制账胧,否則會(huì)給別人帶來(lái)困擾的竞慢。這里我仍舊是通過(guò)代碼模板的方式進(jìn)行快速編寫:

快速寫visibility的邏輯
快速寫visibility的邏輯

利用b代替findViewById

在mvvm時(shí)代,我們是否需要id呢治泥?其實(shí)筹煮,我們?nèi)耘f需要id,只是不再需要findViewById了居夹! 這在DBinding的demo中就有這樣的體現(xiàn):

public abstract class BaseActivity<T extends ViewDataBinding> extends AppCompatActivity{

    protected EventViewModel viewEvents = new EventViewModel();

    protected T b;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bindViews();
        beforeSetViews();
        setViews();
        doTransaction();
    }

    protected Activity getActivity() {
        return this;
    }

    @LayoutRes
    protected abstract int getLayoutResId();

    protected void bindViews(){
        b = DBinding.bind(this, getLayoutResId());
    }

    protected abstract void beforeSetViews();
    
    protected abstract void setViews();

    protected abstract void doTransaction();
}

在子類中败潦,只需要寫好泛型就行:

public class MainActivity extends BaseActivity<ActivityMainBinding> {}

利用ViewEvent類做事件的統(tǒng)一管理

一個(gè)頁(yè)面中會(huì)有多個(gè)view,Button和EditText肯定是會(huì)產(chǎn)生事件的准脂,在mvvm中我采用的是事件設(shè)置的代碼在xml中劫扒,事件處理代碼在java中的思路。

 <ImageView
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:src="@{user.pic , default= @drawable/speed_icon}"
    android:onClick="@{event.onClick}"
    />
viewEvents.setOnClick(v -> {
    if (v == b.userInfoInclude.headPicIv) {
       // do something
    }
});

定位問(wèn)題的思路是這樣的狸膏,首先你肯定不會(huì)懷疑button不會(huì)產(chǎn)生事件(如果真的有沟饥,那么你的開發(fā)真的開小差了),一般都是按鈕被點(diǎn)擊后的事件觸發(fā)的代碼產(chǎn)生了問(wèn)題,所以大多數(shù)情況下只需要在觸發(fā)的那段java代碼中下斷點(diǎn)就行了贤旷。

利用vm做全局的數(shù)據(jù)同步

兩個(gè)頁(yè)面間
vm自身的自動(dòng)綁定特性會(huì)讓兩個(gè)頁(yè)面共用數(shù)據(jù)變得十分簡(jiǎn)單广料,可以通過(guò)viewModel.toSerializable()來(lái)將其序列化,然后在接收的地方通過(guò):

NewsviewModel vm = NewsviewModel.toviewModel(getIntent().getSerializableExtra(KEY)); 

得到它,現(xiàn)在你就可以方便的利用上個(gè)頁(yè)面?zhèn)鱽?lái)的vm進(jìn)行l(wèi)ayout層面的綁定了幼驶。

注意:
雖然這種方式十分簡(jiǎn)單艾杏,但不要濫用,它僅僅針對(duì)于兩頁(yè)面有有共同vm的情況盅藻,其他情況我還是推薦通過(guò)回調(diào)糜颠、廣播、事件總線等方式去做萧求。要記得vm雖好其兴,但它不是萬(wàn)能的。

自動(dòng)同步點(diǎn)贊事件
自動(dòng)同步點(diǎn)贊事件

多個(gè)頁(yè)面間
我們經(jīng)常會(huì)有一些全局的數(shù)據(jù)夸政,比如紅點(diǎn)消息和用戶信息元旬,這些數(shù)據(jù)我們通常會(huì)產(chǎn)生一個(gè)靜態(tài)的對(duì)象進(jìn)行存儲(chǔ),以用戶信息舉例守问,我們完全可以讓所有用到當(dāng)前用戶信息的頁(yè)面用同一個(gè)vm匀归,這樣就再也不用考慮多個(gè)頁(yè)面用戶信息不同步的情況了。至于什么東西可以用這種方式做全局同步耗帕,什么不可以穆端,這個(gè)就只能看業(yè)務(wù)和團(tuán)隊(duì)成員的把控能力了。

通過(guò)注冊(cè)數(shù)據(jù)監(jiān)聽來(lái)解耦view層

在mvvm中我們應(yīng)該把所有數(shù)據(jù)同步的事情交給框架仿便,而不是自己去維護(hù)体啰。將view層的邏輯(如:動(dòng)畫,控件A文字的改變引起的控件B改變等)獨(dú)立寫出嗽仪,在model中獨(dú)立寫出數(shù)據(jù)對(duì)vm產(chǎn)生影響的邏輯荒勇,下面舉個(gè)例子:

    /**
     * 數(shù)據(jù)改變后ui會(huì)做一些改變。
     * 應(yīng)該利用對(duì)vm的字段監(jiān)聽的方式做處理闻坚,不應(yīng)該在數(shù)據(jù)改變時(shí)沽翔,通過(guò)開發(fā)者做ui層面的更新。
     *
     * @param bind 為什么不是單一監(jiān)聽器窿凤,而是觀察者模式仅偎?
     *             因?yàn)闀?huì)有多個(gè)東西對(duì)同一個(gè)數(shù)據(jù)進(jìn)行監(jiān)聽,如果是單一的就沒辦法實(shí)現(xiàn)這個(gè)功能雳殊。
     */
    public void notifyData(final NewsItemBinding bind) {
        mviewModel.addOnPropertyChangedCallback((sender, propertyId)-> {
                // 監(jiān)聽title的改變橘沥,然后設(shè)置文字
                if (propertyId == kale.db.BR.title) {
                    // do change view
                }
            }
        });
    }

在數(shù)據(jù)來(lái)的時(shí)候,數(shù)據(jù)僅僅對(duì)vm進(jìn)行綁定相种,不用考慮ui層面的邏輯:

    ///////////////////////////////////////////////////////////////////////////
    // 這里就僅僅做數(shù)據(jù)和ui的綁定工作了威恼,不用想ui層面的任何邏輯
    ///////////////////////////////////////////////////////////////////////////
    /**
     * 將ViewModel和model的數(shù)據(jù)進(jìn)行同步
     * model模型可能很復(fù)雜,但viewModel的模型很簡(jiǎn)單寝并,這里就是做二者的轉(zhuǎn)換箫措。
     */
    @Override
    public void handleData(NewsInfo data, int pos) {
        mviewModel.setTitle(String.format(data.title,"kale"));
    }

禁止一切容易出錯(cuò)的操作

forbid

強(qiáng)類型語(yǔ)言和弱類型語(yǔ)言的一個(gè)差異(僅僅是差異)就是在于IDE可以幫你做很多限制,databinding本身是相當(dāng)靈活的衬潦,支持雙向綁定斤蔓,支持xml中寫邏輯等操作,但是我這里利用插件或者是其他的方式強(qiáng)烈禁止在xml中寫方法和特殊邏輯镀岛,對(duì)于import我只允許了View這一個(gè)類的import弦牡。對(duì)于雙向綁定,我建議你在編碼的時(shí)候就應(yīng)該有所警惕漂羊,最好能有注釋驾锰,方便你的同伴進(jìn)行定位問(wèn)題。
如果你是一人開發(fā)一個(gè)不需要維護(hù)的應(yīng)用走越,那么xml中隨便你怎么寫椭豫,但如果你是團(tuán)隊(duì)開發(fā),你會(huì)發(fā)現(xiàn)那些在xml中的邏輯很可能是團(tuán)隊(duì)合作的災(zāi)難旨指。當(dāng)然了赏酥,如果你已經(jīng)通過(guò)某種文檔或者是其他的標(biāo)準(zhǔn)化方式來(lái)限制和規(guī)定xml中的邏輯格式,那么我倒是覺得是可行的谆构。

自由是在限制之中的裸扶,如果沒有限制那么就沒有社會(huì)。

四搬素、總結(jié)

我經(jīng)歷了項(xiàng)目從mvc到mvp呵晨,然后變成mvvm,最后到mvpvm的各個(gè)階段熬尺,在每個(gè)階段中我也花了大量的時(shí)間去發(fā)現(xiàn)問(wèn)題解決問(wèn)題何荚,為后續(xù)的擴(kuò)展和靈活性做了很多的工作。在做這些事情的時(shí)候我漸漸發(fā)現(xiàn)猪杭,無(wú)論你采用什么模式餐塘,你都必須有明確的分層的概念,其實(shí)大到分層小到單一職責(zé)概念皂吮,都是在提升代碼可維護(hù)性戒傻。在現(xiàn)在這個(gè)時(shí)期,我的建議是中小型公司可以放心嘗試databinding蜂筹,大型公司的話因?yàn)轶w量和人員的問(wèn)題很難會(huì)改變模式需纳。當(dāng)然了,如果目前你的代碼本身就有很好的可維護(hù)性艺挪,我也不建議因?yàn)榧夹g(shù)的新穎而動(dòng)項(xiàng)目不翩,因?yàn)槲覀兊哪康牟皇菄L鮮和炫技,而是為了解決問(wèn)題!

話說(shuō)口蝠,你寫了多少年的findViewById器钟?

developer_kale@foxmail.com
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妙蔗,隨后出現(xiàn)的幾起案子傲霸,更是在濱河造成了極大的恐慌,老刑警劉巖眉反,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昙啄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡寸五,警方通過(guò)查閱死者的電腦和手機(jī)梳凛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)梳杏,“玉大人伶跷,你說(shuō)我怎么就攤上這事∶啬” “怎么了叭莫?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)烁试。 經(jīng)常有香客問(wèn)我雇初,道長(zhǎng),這世上最難降的妖魔是什么减响? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任靖诗,我火速辦了婚禮,結(jié)果婚禮上支示,老公的妹妹穿的比我還像新娘刊橘。我一直安慰自己,他們只是感情好颂鸿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布促绵。 她就那樣靜靜地躺著,像睡著了一般嘴纺。 火紅的嫁衣襯著肌膚如雪败晴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天栽渴,我揣著相機(jī)與錄音尖坤,去河邊找鬼。 笑死闲擦,一個(gè)胖子當(dāng)著我的面吹牛慢味,可吹牛的內(nèi)容都是我干的场梆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼纯路,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼或油!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起感昼,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤装哆,失蹤者是張志新(化名)和其女友劉穎罐脊,沒想到半個(gè)月后定嗓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萍桌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年宵溅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片上炎。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恃逻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藕施,到底是詐尸還是另有隱情寇损,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布裳食,位于F島的核電站矛市,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏诲祸。R本人自食惡果不足惜浊吏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望救氯。 院中可真熱鬧找田,春花似錦、人聲如沸着憨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甲抖。三九已至底桂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惧眠,已是汗流浹背籽懦。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氛魁,地道東北人暮顺。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓厅篓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捶码。 傳聞我的和親對(duì)象是個(gè)殘疾皇子羽氮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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