注解在項(xiàng)目中的使用

很早之前寫過一篇關(guān)于MVP模式的文章,大家都知道MVP模式相對(duì)于MVC來說更加的解耦鉴扫,當(dāng)然了現(xiàn)在還有比較熱的MVVM模式等帐要,每一種新的架構(gòu)模式的產(chǎn)生則表示之前的模式在某種程度上并不能很好的滿足項(xiàng)目的需要,對(duì)于MVVM模式目前還沒有接觸蒲肋,表示以后會(huì)抽時(shí)間研究乒省。這里為什么會(huì)再次寫關(guān)于MVP模式相關(guān)的文章呢巧颈?首先,之前文章是在CSDN上寫的袖扛,這里表示懶得直接搬過來砸泛,所以做個(gè)復(fù)習(xí)十籍;其次,之前的寫法都是針對(duì)單個(gè)P來實(shí)現(xiàn)的唇礁,比如我們?cè)谝粋€(gè)activity中同時(shí)實(shí)現(xiàn)登錄和注冊(cè)勾栗,那么這個(gè)activity就需要對(duì)應(yīng)兩個(gè)presenter,那么按照之前的實(shí)現(xiàn)方式是不支持的盏筐,所以這里作為一個(gè)完善围俘。

在此之前還是先來復(fù)習(xí)下之前寫的MVP模式吧,還是用干貨集中營的api琢融,再次表示感謝
annotation_mvp.gif

大家都知道P層是作為V層和M層之間的橋梁界牡,也就是說,當(dāng)我們進(jìn)行網(wǎng)絡(luò)請(qǐng)求的操作是在M層做的漾抬,然后將請(qǐng)求得到的結(jié)果回調(diào)到P層宿亡,最后在P層再將結(jié)果回調(diào)到V層。那么會(huì)不會(huì)出現(xiàn)這樣的問題纳令,當(dāng)網(wǎng)絡(luò)請(qǐng)求結(jié)果還沒回調(diào)回來時(shí)我們的V層(activity)由于某些原因被銷毀了她混,但是P層還持有V層的引用,導(dǎo)致activity不能被gc回收泊碑,也就出現(xiàn)了內(nèi)存泄漏,當(dāng)結(jié)果回來時(shí)P層將拿到的結(jié)果再給V層毯欣,這樣也有可能會(huì)導(dǎo)致應(yīng)用閃退等馒过。所以我們應(yīng)該讓P層也擁有V層同樣的生命周期,這里我們簡單定義下presenter的基類BasePresenter

public abstract class BasePresenter<V> {
    private V mvpView;

    protected void attachView(V mvpView){
        this.mvpView = mvpView;
    }

    protected void detachView(){
        mvpView = null;
    }

    protected V getMvpView(){
        return mvpView;
    }
}

你也可以定義一個(gè)view的基類酗钞,比如請(qǐng)求加載框的顯示與隱藏腹忽,數(shù)據(jù)加載成功或者失敗的顯示以及一些通用的toast提示等等,這里就不再贅述

接下來我們定義下activity的基類

public abstract class BaseMvpActivity<V, P extends BasePresenter<V>> extends AppCompatActivity{
    private P presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //布局
        setContentView(getLayoutId());
        //獲取presenter
        presenter = createPresenter();
        if (presenter != null){
            presenter.attachView((V) this);
        }
        //初始化等操作
        initView(savedInstanceState);
        //findViewById
        findViewById();
        //設(shè)置監(jiān)聽事件
        setListener();
        //setViews
        setViews();
        //請(qǐng)求數(shù)據(jù)
        setRequestDatas();
    }

    protected abstract int getLayoutId();
    protected abstract P createPresenter();
    protected void findViewById(){}
    protected abstract void initView(Bundle savedInstanceState);
    protected void setListener(){}
    protected void setViews(){}
    protected void setRequestDatas(){}


    protected P getPresenter(){
        return presenter;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null){
            presenter.detachView();
        }
    }
}

為了方便管理各層之間定義的接口砚作,接下來我們需要定義一個(gè)契約類來統(tǒng)一管理

public class MZContract {

    //view  interface
    public interface MZViewI{
        void onSuccess(List<MZBean> lists);
        void onFailed(String msg);
    }
    //presenter  interface
    public interface MZPresenterI{
        void setMZDatas(Context context, String category, int count, int size);
    }

    //model
    public interface MZModelI{
        void requestDatas(Context context, String category, int count, int size, ResultStatuI resultStatu);
    }

    public interface ResultStatuI{
        void onSuccess(List<MZBean> lists);
        void onFailed(String msg);
    }
}

看下我們進(jìn)行網(wǎng)絡(luò)請(qǐng)求的model層的處理

public class InfoModel implements MZContract.MZModelI{
    @Override
    public void requestDatas(Context context, String category, int count, int size, final MZContract.ResultStatuI resultStatu) {
        RxRetrofitManger.getInstance().getService()
                .getMZInfo(category, count, size)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new RxObserver<BaseBean<List<MZBean>>>(context, false) {
                    @Override
                    public void onSuccess(BaseBean<List<MZBean>> response) {
                        resultStatu.onSuccess(response.getResults());
                    }

                    @Override
                    public void onFailed(BaseBean<List<MZBean>> response) {
                        resultStatu.onFailed("獲取數(shù)據(jù)失敗");
                    }
                });
    }
}

代碼中簡單封裝了RxJava+OkHttp+Retrofit實(shí)現(xiàn)的網(wǎng)絡(luò)請(qǐng)求框架窘奏,感興趣的可以自己查看下代碼。

接著我們?cè)賮砜聪潞蚼odel交互的presenter的具體實(shí)現(xiàn)類

public class MZPresenter extends BasePresenter<MZContract.MZViewI> implements MZContract.MZPresenterI{
    private InfoModel model;

    public MZPresenter(){
        model = new InfoModel();
    }

    @Override
    public void setMZDatas(Context context, String category, int count, int size) {
        if (getMvpView() != null){
            model.requestDatas(context, category, count, size, new MZContract.ResultStatuI() {
                @Override
                public void onSuccess(List<MZBean> lists) {
                    if (getMvpView() != null){
                        getMvpView().onSuccess(lists);
                    }
                }

                @Override
                public void onFailed(String msg) {
                    if (getMvpView() != null){
                        getMvpView().onFailed(msg);
                    }
                }
            });
        }
    }
}

可以看出葫录,我們?cè)谶M(jìn)行網(wǎng)絡(luò)請(qǐng)求以及結(jié)果回調(diào)時(shí)都進(jìn)行了判斷着裹,如果view不存在了就不再進(jìn)行后續(xù)操作。其實(shí)你還可以定義個(gè)方法去取消網(wǎng)絡(luò)請(qǐng)求等等米同,這里示例只是用來簡單解釋說明骇扇,如需更加詳細(xì)、完善的想法面粮,可自行添加少孝。

接著我們?cè)賮砜聪聉層和p層是如何交互的

public class MainActivity extends BaseMvpActivity<MZContract.MZViewI, MZPresenter> implements MZContract.MZViewI{

    private RecyclerView recyclerView;
    private MZListsAdapter mzAdapter;
    private List<MZBean> lists;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected MZPresenter createPresenter() {
        return new MZPresenter();
    }

    @Override
    protected void initView(Bundle savedInstanceState) {
    }

    @Override
    protected void findViewById() {
        recyclerView = findViewById(R.id.mz_recycler_view);
    }

    @Override
    protected void setViews() {
        lists = new ArrayList<>();
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(gridLayoutManager);

        mzAdapter = new MZListsAdapter(this, lists);
        recyclerView.setAdapter(mzAdapter);
    }

    @Override
    protected void setRequestDatas() {
        getPresenter().setMZDatas(this,"福利", 10, 1);
    }

    @Override
    public void onSuccess(List<MZBean> lists) {
        if (lists != null && lists.size() > 0){
             mzAdapter.setDatas(lists);
        }
    }

    @Override
    public void onFailed(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

我們?cè)赩層通過createPresenter()進(jìn)行和P層的綁定,對(duì)于解除綁定我們放在了base基類中去操作了熬苍,同時(shí)在解綁時(shí)我們也可以再增加個(gè)取消網(wǎng)絡(luò)請(qǐng)求的操作等稍走。

從上面的案例可以看到并不能滿足一個(gè)V對(duì)應(yīng)多個(gè)P的情況,所以,接下來我們就通過注解的方式實(shí)現(xiàn)這種需求婿脸,關(guān)于注解我們可以看下這位大牛的文章粱胜,講解的很清楚深入理解Java注解類型(@Annotation),還要感謝L_Xian提供的通過注解的方式實(shí)現(xiàn)一個(gè)V對(duì)應(yīng)多個(gè)P的案例盖淡,之前也學(xué)習(xí)過注解的一些相關(guān)知識(shí)年柠,剛好通過這個(gè)案例加以實(shí)踐。首先我們先看下修改之后的View層都發(fā)生了哪些變化

@CreatePresenter(presenters = {MZPresenter.class, ArticlePresenter.class})
public class MainActivity extends BaseMvpActivity implements MZContract.MZViewI, View.OnClickListener{

    private LinearLayout llTitle;
    private RecyclerView recyclerView;
    private MZListsAdapter mzAdapter;
    private List<MZBean> lists;
    private ArticleListsAdapter articleAdapter;
    private List<MZBean> articleLists;
    private boolean isShowArticle;

    @PresenterFiles
    MZPresenter presenter;
    @PresenterFiles
    ArticlePresenter articlePresenter;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    /*@Override
    protected MZPresenter createPresenter() {
        return new MZPresenter();
    }*/

    @Override
    protected void initView(Bundle savedInstanceState) {
    }

    @Override
    protected void findViewById() {
        llTitle = findViewById(R.id.ll_title);
        recyclerView = findViewById(R.id.mz_recycler_view);
    }

    @Override
    protected void setViews() {
        lists = new ArrayList<>();
        articleLists = new ArrayList<>();
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(gridLayoutManager);

        mzAdapter = new MZListsAdapter(this, lists);
        recyclerView.setAdapter(mzAdapter);
    }

    @Override
    protected void setListener() {
        llTitle.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.ll_title:
                getArticleDatas();
                break;
        }
    }

    private void getArticleDatas() {
        isShowArticle = true;
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);

        articleAdapter = new ArticleListsAdapter(this, articleLists);
        recyclerView.setAdapter(articleAdapter);
        //請(qǐng)求
        articlePresenter.setMZDatas(this, "Android", 10, 1);
    }

    @Override
    protected void setRequestDatas() {
        presenter.setMZDatas(this,"福利", 10, 1);
        //getPresenter().setMZDatas(this,"福利", 10, 1);
    }

    @Override
    public void onSuccess(List<MZBean> lists) {
        if (lists != null && lists.size() > 0){
            if (isShowArticle){
                articleAdapter.setArticleDatas(lists);
            }else {
                mzAdapter.setDatas(lists);
            }
        }
    }

    @Override
    public void onFailed(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

由于獲取妹紙字段和獲取文章字段一樣褪迟,所以為了偷懶在很多地方都共用了冗恨,這不重要,重要的是理解注解的實(shí)現(xiàn)方式味赃。從上面代碼中可以看出掀抹,之前我們是通過createPresenter()方法去new出我們要和view層進(jìn)行綁定的presenter實(shí)例對(duì)象,現(xiàn)在我們是通過@CreatePresenter(presenters = {MZPresenter.class, ArticlePresenter.class})注解的方式將我們的presenter傳過去心俗,我們會(huì)在注解處理器中對(duì)傳進(jìn)來的這些presenter進(jìn)行實(shí)例化傲武,這種使用方式和使用阿里開源的ARouter使用類似,其次是我們發(fā)現(xiàn)多了兩個(gè)加了@PresenterFiles注解的成員變量城榛,這個(gè)@PresenterFiles注解和Android系統(tǒng)自帶的@Test注解類似揪利,是一個(gè)標(biāo)記注解,我們會(huì)在注解處理器中通過反射先找到所有的成員變量字段狠持,然后判斷每個(gè)字段上是否存在@PresenterFiles注解疟位,存在的話就通過key找到存儲(chǔ)的相對(duì)應(yīng)的value值,這個(gè)value值也就是前面?zhèn)鬟M(jìn)去的presenter實(shí)例對(duì)象(前面我們說了@CreatePresenter注解喘垂,其作用就是去實(shí)例化傳進(jìn)去的這些presenter甜刻,通過map去進(jìn)行以類名為key,實(shí)例化對(duì)象為value的存儲(chǔ))正勒,所以要注意下這個(gè)key必須要保持一致得院,因?yàn)槲覀兌际怯妙惷鳛閗ey。

接下來我們就來看下這兩個(gè)注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
    Class<?>[] presenters();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PresenterFiles {
}

以及處理這倆注解的處理器

/**
 * 運(yùn)行時(shí)注解處理器
 */

public class PresenterCreator {
    private Activity activity;
    private Fragment fragment;
    private Class<?> mClass;
    private HashMap<String, BasePresenter> map = new HashMap<>();

    public static PresenterCreator init(Activity activity){
        return new PresenterCreator(activity, null);
    }
    public static PresenterCreator init(Fragment fragment){
        return new PresenterCreator(null, fragment);
    }

    private PresenterCreator(Activity activity, Fragment fragment){
        if (activity != null){
            mClass = activity.getClass();
            dealPresenterAnnotationOfType(activity);
            dealPresenterOfField(activity);
        }

        if (fragment != null){
            mClass = fragment.getClass();
            dealPresenterAnnotationOfType(fragment);
            dealPresenterOfField(fragment);
        }

    }

    private <P extends BasePresenter> void dealPresenterAnnotationOfType(Object view){
        CreatePresenter createPresenter = mClass.getAnnotation(CreatePresenter.class);
        if (createPresenter != null){
            Class<P>[] presenterClass = (Class<P>[]) createPresenter.presenters();
            for (Class<P> clazz : presenterClass){
                try {
                    P presenter = clazz.newInstance();
                    if (presenter != null){
                        map.put(clazz.getCanonicalName(), presenter);
                        //P和V綁定
                        presenter.attachView(view);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private <P extends BasePresenter> void dealPresenterOfField(Object view){
        //首先通過反射先獲取所有成員字段
        Field[] fields = mClass.getDeclaredFields();
        for (Field field : fields){
            //再獲取每一個(gè)字段上面的注解
            Annotation[] anns = field.getDeclaredAnnotations();
            if (anns.length < 1){//如果此字段上沒有注解章贞,則會(huì)返回一個(gè)長度為0的數(shù)組
                continue;
            }
            //因?yàn)橐粋€(gè)字段上可能會(huì)有多個(gè)注解
            if (anns[0] instanceof PresenterFiles){
                P presenter = (P) map.get(field.getType().getCanonicalName());
                if (presenter != null){
                    try {
                        //給字段賦值
                        field.setAccessible(true);
                        field.set(view, presenter);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public void detachView(){
        for (Map.Entry<String, BasePresenter> entry : map.entrySet()){
            BasePresenter presenter = entry.getValue();
            if (presenter != null){
                presenter.detachView();
            }
        }
    }
}

然后我們?cè)赽aseactivity基類中去啟動(dòng)這個(gè)注解處理器

public abstract class BaseMvpActivity extends AppCompatActivity{
    //private P presenter;
    private PresenterCreator presenterCreator;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //布局
        setContentView(getLayoutId());
        //獲取presenter
        presenterCreator = PresenterCreator.init(this);
       /* presenter = createPresenter();
        if (presenter != null){
            presenter.attachView((V) this);
        }*/
        //初始化等操作
        initView(savedInstanceState);
        //findViewById
        findViewById();
        //設(shè)置監(jiān)聽事件
        setListener();
        //setViews
        setViews();
        //請(qǐng)求數(shù)據(jù)
        setRequestDatas();
    }

    protected abstract int getLayoutId();
    //protected abstract P createPresenter();
    protected void findViewById(){}
    protected abstract void initView(Bundle savedInstanceState);
    protected void setListener(){}
    protected void setViews(){}
    protected void setRequestDatas(){}


    /*protected P getPresenter(){
        return presenter;
    }*/

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenterCreator != null){
            presenterCreator.detachView();
        }
        /*if (presenter != null){
            presenter.detachView();
        }*/
    }
}

看到這里你可以能有些疑慮祥绞,這種方式會(huì)導(dǎo)致性能損耗吧?是的鸭限,因?yàn)槲覀兌x的這些注解@Retention(RetentionPolicy.RUNTIME)可以看出是運(yùn)行時(shí)注解就谜,期間通過反射進(jìn)行一系列的操作必然會(huì)導(dǎo)致性能上有所損耗,那應(yīng)該怎么辦呢里覆?相信大家都用過黃油刀ButterKnife丧荐,他們有個(gè)@BindView注解,點(diǎn)擊進(jìn)去查看下這個(gè)注解定義的是@Retention(CLASS)喧枷,也就是編譯時(shí)注解虹统,導(dǎo)致可能會(huì)在編譯期時(shí)間會(huì)稍微長些弓坞,但是在運(yùn)行時(shí)是不會(huì)有性能上的損耗的,那么他們是怎么做的呢车荔?以后我會(huì)抽個(gè)時(shí)間研究下渡冻,等研究明白了再對(duì)此方式進(jìn)行優(yōu)化吧,最后引用zejian_博客中的關(guān)于@Retention中的三個(gè)可取值進(jìn)行解釋說明

@Retention用來約束注解的生命周期忧便,分別有三個(gè)值族吻,源碼級(jí)別(source),類文件級(jí)別(class)或者運(yùn)行時(shí)級(jí)別(runtime)珠增,其含有如下:
SOURCE:注解將被編譯器丟棄(該類型的注解信息只會(huì)保留在源碼里超歌,源碼經(jīng)過編譯后,注解信息會(huì)被丟棄蒂教,不會(huì)保留在編譯好的class文件里)
CLASS:注解在class文件中可用巍举,但會(huì)被VM丟棄(該類型的注解信息會(huì)保留在源碼里和class文件里,在執(zhí)行的時(shí)候凝垛,不會(huì)加載到虛擬機(jī)中)懊悯,請(qǐng)注意,當(dāng)注解未定義Retention值時(shí)梦皮,默認(rèn)值是CLASS炭分,如Java內(nèi)置注解,@Override剑肯、@Deprecated捧毛、@SuppressWarnning等
RUNTIME:注解信息將在運(yùn)行期(JVM)也保留,因此可以通過反射機(jī)制讀取注解的信息(源碼退子、class文件和執(zhí)行的時(shí)候都有注解的信息),如SpringMvc中的@Controller型将、@Autowired寂祥、@RequestMapping等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末七兜,一起剝皮案震驚了整個(gè)濱河市丸凭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腕铸,老刑警劉巖惜犀,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狠裹,居然都是意外死亡虽界,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門涛菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莉御,“玉大人撇吞,你說我怎么就攤上這事〗甘澹” “怎么了牍颈?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長琅关。 經(jīng)常有香客問我煮岁,道長,這世上最難降的妖魔是什么涣易? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任画机,我火速辦了婚禮,結(jié)果婚禮上都毒,老公的妹妹穿的比我還像新娘色罚。我一直安慰自己,他們只是感情好账劲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布戳护。 她就那樣靜靜地躺著,像睡著了一般瀑焦。 火紅的嫁衣襯著肌膚如雪腌且。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天榛瓮,我揣著相機(jī)與錄音铺董,去河邊找鬼。 笑死禀晓,一個(gè)胖子當(dāng)著我的面吹牛精续,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粹懒,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼重付,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了凫乖?” 一聲冷哼從身側(cè)響起确垫,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帽芽,沒想到半個(gè)月后删掀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡导街,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年披泪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搬瑰。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡付呕,死狀恐怖计福,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徽职,我是刑警寧澤象颖,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站姆钉,受9級(jí)特大地震影響说订,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜潮瓶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一陶冷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毯辅,春花似錦埂伦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胀莹,卻和暖如春基跑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背描焰。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工媳否, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荆秦。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓篱竭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親步绸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掺逼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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