带你一步一步完成Mvp高级封装

如题,MVP架构目前是最火的安卓架构了。为什么他如此受推崇呢?今天我们就来揭开它神秘的面纱。首先我们来个简易版的mvp(人懒,直接上代码了)
实现思路如下:
1、首先我们先定义一个接口,用来规定针对这个界面逻辑View需要作出的动作的接口。
2、让Activity实现这个接口中的方法,也就是V层
3、创建一个类,用来封装之前的网络请求过程,也就是M层
4、再创建一个类,用来处理M层和V层之间的通信,也就是P层

1.首先定义V层接口LoginView

/**
 * Author:liuzj
 * Time:2018/3/7
 * Description: V层接口
 */
public interface LoginView {

    /**
     * 展示加载过程
     */
    void showLoading();
    void onSuccess(String response);
    void onFaile(String error);
}

activity或者fragment去实现它

/**
 * 1.简单型mvp模式
 */
public class Demo1Activity extends AppCompatActivity implements LoginView{

    private TextView content;
    private ProgressBar loadview;
    private LoginPresenter loginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        content = (TextView) findViewById(R.id.content);
        loadview = (ProgressBar) findViewById(R.id.pb);

        //引入P层
        loginPresenter = new LoginPresenter(this);
        loginPresenter.doLogin();
    }


    @Override
    public void showLoading() {
        loadview.setVisibility(View.VISIBLE);
    }

    @Override
    public void onSuccess(String response) {
        loadview.setVisibility(View.GONE);
        content.setText(response);
    }

    @Override
    public void onFaile(String error) {
        loadview.setVisibility(View.GONE);

    }

}

2.定义M层LoginModel(数据相关)

/**
 * Author:liuzj
 * Time:2018/3/7
 * Description:M层
 */

public class LoginModel {

    public void Login(CallBack<String> callBack) {
        callBack.onResponse("你好!Mvp架构师");
    }
}
给出CallBack代码:
/**
 * Author:liuzj
 * Time:2018/3/7
 * Description: 数据回调接口
 */

public interface CallBack<T> {

    void onResponse(T response);
    void onFailure(String error);
}

3.重中之重定义P层LoginPresenter

/**
 * Author:liuzj
 * Time:2018/3/7
 * Description:
 */

public class LoginPresenter {
    /*P层要持有M层和V层的引用*/
    private LoginModel model;
    private LoginView view;
    //怎么传view进来? ——>构造方法


    public LoginPresenter(LoginView view) {
        this.model = new LoginModel();
        this.view = view;
    }

    public void doLogin() {
        /*
        * 在P层刷新UI
        * */
        view.showLoading();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                model.Login(new CallBack<String>() {
                    @Override
                    public void onResponse(String response) {
                        view.onSuccess(response);
                    }

                    @Override
                    public void onFailure(String error) {
                        view.onFaile(error);
                    }
                });
            }
        },1500);
    }
}

到这里一个简易的MVP就写好了。但是存在一个问题,假设我们在进行网络请求的过程中Activity或者说V层销毁了,而P层还持有V层的引用,这样会造成内存泄露。怎么解决?
——>我们可以在presenter里面设计一个绑定和解绑操作
改写后的presenter如下:

public class LoginPresenter2 {
    /*P层要持有M层和V层的引用*/
    private LoginModel model;
    private LoginView view;
    //怎么传view进来? ——>构造方法


    public LoginPresenter2(LoginView view) {
        this.model = new LoginModel();
        this.view = view;
    }

    public void doLogin() {
        /*
        * 在P层刷新UI
        * */
        view.showLoading();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                model.Login(new CallBack<String>() {
                    @Override
                    public void onResponse(String response) {
                        view.onSuccess(response);
                    }

                    @Override
                    public void onFailure(String error) {
                        view.onFaile(error);
                    }
                });
            }
        },1500);
    }

    /**
     * 绑定V层
     * @param view
     */
    public void attach(LoginView view) {
        this.view = view;
    }

    /**
     * 解绑
     */
    public void detach() {
        this.view = null;
    }
}

我这里没有写请求网络如果有请求网络操作,我们还得做取消操作。跟着detach后面做取消就可以了,这里就不给出了。
改写后的Activity代码如下:

public class Demo2Activity extends AppCompatActivity implements LoginView{

    private TextView content;
    private ProgressBar loadview;
    private LoginPresenter2 loginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        content = (TextView) findViewById(R.id.content);
        loadview = (ProgressBar) findViewById(R.id.pb);

        //引入P层
        loginPresenter = new LoginPresenter2();
        loginPresenter.attach(this);
        loginPresenter.doLogin();
    }


    @Override
    public void showLoading() {
        loadview.setVisibility(View.VISIBLE);
    }

    @Override
    public void onSuccess(String response) {
        loadview.setVisibility(View.GONE);
        content.setText(response);
    }

    @Override
    public void onFaile(String error) {
        loadview.setVisibility(View.GONE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter.detach();
        /*有网络请求操作在这里要做取消操作*/
    }
}

内存泄露的问题解决了,但是开发过程中我们是会有有很多的P层和V层的,每次都要写绑定,解绑,V层再调用绑定,解绑。是不是显得代码冗余。怎么解决代码冗余? ——>抽取基类——>用Presenter基类,Activity(或者其他的view)基类来做这些事情。而且view还不能写死,我们这里采用泛型设计。

思路:

1.创建一个基类View,让所有View接口都必须实现,这个View可以什么都不做只是用来约束类型的

2.创建一个基类的Presenter,在类上规定View泛型,然后定义绑定和解绑的抽象方法,让子类去实现,对外在提供一个获取View的方法,
让子类直接通过方法来获取View

3.创建一个基类的Activity,声明一个创建Presenter的抽象方法,因为要帮子类去绑定和解绑那么就需要拿到子类的Presenter才行,但是又不能随便一个类都能绑定的,因为只有基类的Presenter中才定义了绑定和解绑的方法,所以同样的在类上可以声明泛型在,方法上使用泛型来达到目的。

4.修改Presenter和Activity中的代码,各自继承自己的基类并去除重复代码

1.创建View层顶级接口,让所有的view都去实现它

//V层的顶层接口,可不做任何事情,只是用来约束类型

public interface IMvpView {
}
2.创建一个基类的Presenter,在类上规定View泛型,然后定义绑定和解绑的方法,对外在提供一个获取View的方法,让子类直接通过方法来获取View使用即可
public abstract class AbsMvpPresenter<V extends IMvpView> {
    private V view;

    /**
     * 绑定V层
     * @param view
     */
    public void attach(V view) {
        this.view = view;
    }

    /**
     * 与V层解绑
     */
    public void detach() {
        this.view = null;
    }

    /**
     * 提供一个获取view的方法,后面的子类可通过此方法获取V层
     * @return
     */
    public V getView() {
        return this.view;
    }
}
3.创建基类Activity(或者其他的view)因为要用presenter去调用绑定和解绑,所以我们可以提供一个抽象方法交给子类去创建presenter。但是presenter又不能写死,这里采用泛型继承自AbsMvpPresenter

//规定子类具体的View需要继承自IMvpView,子类具体的presenter需要继承自AbsMvpPresenter

public abstract class AbsMvpActivity<V extends IMvpView, P extends AbsMvpPresenter<V>> extends AppCompatActivity implements IMvpView {

    private P presenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (presenter == null) {
            presenter = createPresenter();
        }

        if (presenter == null) {
            throw new NullPointerException("presenter not be null!");
        }

        presenter.attach((V)this);
    }

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

    /**
     * 对子类提供获取presenter的方法
     * @return
     */
    public P getPresenter() {
        return presenter;
    }

    /**
     * 创建presenter,让子类自己去实现,获取自己的presenter
     * @return
     */
    protected abstract P createPresenter();


}

4.让presenter继承AbsMvpPresenter,activity继承AbsMvpActivity,编写代码

LoginPresenter3

public class LoginPresenter3 extends AbsMvpPresenter<LoginView3>{
    private LoginModel model;
    public LoginPresenter3() {
        this.model = new LoginModel();
    }

    public void doLogin() {
        /*
        * 在P层刷新UI
        * */
        getView().showLoading();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                model.Login(new CallBack<String>() {
                    @Override
                    public void onResponse(String response) {
                        getView().onSuccess(response);
                    }

                    @Override
                    public void onFailure(String error) {
                        getView().onFaile(error);
                    }
                });
            }
        }, 1500);
    }

}

Demo3Activity

public class Demo3Activity extends AbsMvpActivity<LoginView3, LoginPresenter3> implements LoginView3 {

    private TextView content;
    private ProgressBar loadview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        content = (TextView) findViewById(R.id.content);
        loadview = (ProgressBar) findViewById(R.id.pb);

        //引入P层
        getPresenter().doLogin();
    }


    @Override
    public void showLoading() {
        loadview.setVisibility(View.VISIBLE);
    }

    @Override
    public void onSuccess(String response) {
        loadview.setVisibility(View.GONE);
        content.setText(response);
    }

    @Override
    public void onFaile(String error) {
        loadview.setVisibility(View.GONE);
    }

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

貌似看起来已经很完美了,还能再优化吗?
我们来分析一下:

1.每个子类都要重写父类创建Presenter的方法,创建一个Presenter并返回,这一步我们也可以让父类帮忙干了,怎么做呢?
我们可以采用注解的方式,在子类上声明一个注解并注明要创建的类型,剩下的事情就让父类去做了,但是父类得考虑如果子类不想这么干怎么办,那也还是不能写死吧,可以使用策略模式加工厂模式来实现,我们默认使用这种注解的工厂 ,但是如果子类不喜欢可以通过父类提供的一个方法来创建自己的工厂。

2.Presenter真正的创建过程,我们可以将它放到真正使用Presenter的时候再创建,这样的话可以稍微优化一下性能问题

3.界面有可能会意外销毁并重建,Activity、Fragment、View都可以在销毁的时候通过onDestroy释放一些资源并在onSaveInstanceState方法中存储一些数据然后在重建的时候恢复,但是有可能Presenter中也需要释放一些资源存储一些数据,那么上面的结构就不能满足了,我们可以给Presenter增加生命周期的方法,让Presenter和V层生命周期同步就可以做到了

4.第三步中我们又给Presenter加入了一些生命周期的方法,再加上Presenter的创建绑定和解绑的方法,那么如果我们在创建一个MvpFragment基类,或者View的基类那么这么多的代码岂不是都要copy一份吗,而且看起来也很不清晰,这里我们可以采用代理模式来优化一下。

1.采用工厂模式才创建Presenter,首先创建一个工厂接口

//presenter工厂接口 指定View和Presenter只能继承自IBaseMvpView和BaseMvpPresenter
public interface IPresenterFactory<V extends IBaseMvpView, P extends BaseMvpPresenter<V>> {

    /**
     * 创建presenter的方法
     * @return
     */
    P createPresenter();
}

2..然后创建一个注解,因为我们默认使用注解来创建工厂


//创建presenter的注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
    Class<? extends BaseMvpPresenter> value();
}

注解工厂:

//工厂实现类
public class PresenterFactoryImpl<V extends IBaseMvpView, P extends BaseMvpPresenter<V>> implements IPresenterFactory<V, P> {

    private Class<P> presenterClazz;
    //根据注解创建presenter的工厂实现类
    public static <V extends IBaseMvpView, P extends BaseMvpPresenter<V>> PresenterFactoryImpl<V, P> createFactory(Class<?> viewClazz) {
        CreatePresenter annotation = viewClazz.getAnnotation(CreatePresenter.class);
        Class<P> aClass = null;
        if (annotation != null) {
            aClass = (Class<P>) annotation.value();
        }

        return aClass == null ? null : new PresenterFactoryImpl<V, P>(aClass);
    }

    public PresenterFactoryImpl(Class<P> presenterClazz) {
        this.presenterClazz = presenterClazz;
    }

    @Override
    public P createPresenter() {
        try {
            return this.presenterClazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Presenter 创建失败!检查是否声明了@CreatePresenter(xxPresenter.class)注解");
        }
    }
}


3.我们说了不能写死这个工厂,那么我们需要使用者可以自定义,那么我们还需要给使用者提供一个设置的方法,我们定义一个接口提供设置工厂、获取工厂、获取Presenter的方法,然后让V层来实现这个接口,这样V层的子类就可以通过相应的方法使用了

//提供代理接口给V层使用 交给V层实现
public interface IPresenterProxy<V extends IBaseMvpView, P extends BaseMvpPresenter<V>> {

    /**
     * 设置presenter创建工厂
     * @param factory
     */
    void setPresenterFactory(IPresenterFactory<V, P> factory);

    IPresenterFactory<V, P> getPresenterFactory();

    /**
     * 获取创建的presenter
     * @return
     */
    P getPresenter();

}

4.给Presenter增加生命周期的方法
//指定绑定的view都要继承自IBaseMvpView

public class BaseMvpPresenter<V extends IBaseMvpView> {
//presenter也需要根据view的生命来释放资源,保存数据等,所以这里增加生命周期方法
    private V view;

    /**
     * 创建presenter时调用
     * @param savedState
     */
    public void onCreatePresenter(Bundle savedState) {

    }

    /**
     * 绑定V层
     * @param view
     */
    public void onAttachView(V view) {
        this.view = view;
    }

    /**
     * 与V层解绑
     */
    public void onDetachView() {
        this.view = null;
    }

    /**
     * presenter被销毁时调用
     */
    public void onDestoryPresenter() {

    }

    /**
     * 在Presenter意外销毁的时候被调用,它的调用时机和Activity、Fragment、View中的onSaveInstanceState时机相同
     *
     * @param outState
     */
    public void onSaveInstanceState(Bundle outState) {

    }

    /**
     * 提供一个获取view的方法,后面的子类可通过此方法获取V层
     * @return
     */
    public V getView() {
        return this.view;
    }

}

5.创建一个代理来管理Presenter的生命周期方法

//presenter代理的实现类  用来管理生命周期和view之间的关联
public class BaseMvpPresenterProxyImpl<V extends IBaseMvpView, P extends BaseMvpPresenter<V>> implements IPresenterProxy<V, P> {
    /**
     * 获取onSaveInstanceState中bundle的key
     */
    private static final String PRESENTER_KEY = "presenter_key";
    /**
     * Presenter工厂类
     */
    private IPresenterFactory<V, P> mFactory;
    private P mPresenter;
    private Bundle mBundle;
    private boolean mIsAttchView;

    public BaseMvpPresenterProxyImpl(IPresenterFactory<V, P> mFactory) {
        this.mFactory = mFactory;
    }

    @Override
    public void setPresenterFactory(IPresenterFactory<V, P> factory) {
        if (mPresenter != null) {
            throw new IllegalArgumentException("这个方法只能在getPresenter()之前调用,如果Presenter已经创建则不能再修改");
        }
        this.mFactory = factory;
    }

    @Override
    public IPresenterFactory<V, P> getPresenterFactory() {
        return this.mFactory;
    }

    @Override
    public P getPresenter() {
        if (mFactory != null) {
            if (mPresenter == null) {
                mPresenter = mFactory.createPresenter();
                mPresenter.onCreatePresenter(mBundle == null ? null : mBundle.getBundle(PRESENTER_KEY));
            }
        }

        return mPresenter;
    }

    /**
     * 绑定Presenter和view
     *
     * @param mvpView
     */
    public void onAttachView(V mvpView) {
        if (getPresenter() != null && !mIsAttchView) {
            getPresenter().onAttachView(mvpView);
            mIsAttchView = true;
        }
    }

    /**
     * 销毁Presenter持有的View
     */
    private void onDetachView() {
        if (getPresenter() != null && mIsAttchView) {
            getPresenter().onDetachView();
            mIsAttchView = false;
        }
    }

    /**
     * 销毁Presenter
     */
    public void onDestroy() {
        if (getPresenter() != null) {
            onDetachView();
            getPresenter().onDestoryPresenter();
            mPresenter = null;
        }
    }

    /**
     * 意外销毁的时候调用
     *
     * @return Bundle,存入回调给Presenter的Bundle和当前Presenter的id
     */
    public Bundle onSaveInstanceState() {
        Bundle bundle = new Bundle();
        if (getPresenter() != null) {
            Bundle presenterBundle = new Bundle();
            //回调Presenter
            getPresenter().onSaveInstanceState(presenterBundle);
            bundle.putBundle(PRESENTER_KEY, presenterBundle);
        }
        return bundle;
    }

    /**
     * 意外关闭恢复Presenter
     *
     * @param savedInstanceState 意外关闭时存储的Bundler
     */
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        mBundle = savedInstanceState;
    }
}

6.最后V层实现,首先实现设置工厂的接口,然后创建一个代理并传入默认工厂,在V层生命周期中使用代理去实现管理Presenter的生命周期(Activity继承这个Base类就可以了)

//使用代理模式来代理Presenter的创建、销毁、绑定、解绑以及Presenter的状态保存,其实就是管理Presenter的生命周期
public abstract class BaseMvpActivity<V extends IBaseMvpView, P extends BaseMvpPresenter<V>>
        extends AppCompatActivity implements IPresenterProxy<V, P> {
    // 根据V层的生命周期用presenter代理来管理presenter的生命周期
    private static final String KEY_SAVE_PRESENTER = "key_save_presenter";
    /**
     * 创建被代理对象,传入默认Presenter的工厂
     */
    private BaseMvpPresenterProxyImpl<V, P> presenterProxy = new BaseMvpPresenterProxyImpl<>(PresenterFactoryImpl.<V, P>createFactory(getClass()));

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

        if (savedInstanceState != null) {
            //重新创建时获取之前保存的Bundle
            presenterProxy.onRestoreInstanceState(savedInstanceState.getBundle(KEY_SAVE_PRESENTER));
        }
        presenterProxy.onAttachView((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenterProxy.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //存入这个Bundle
        outState.putBundle(KEY_SAVE_PRESENTER, presenterProxy.onSaveInstanceState());
    }

    @Override
    public void setPresenterFactory(IPresenterFactory<V, P> factory) {
        presenterProxy.setPresenterFactory(factory);
    }

    @Override
    public IPresenterFactory<V, P> getPresenterFactory() {
        return presenterProxy.getPresenterFactory();
    }

    @Override
    public P getPresenter() {
        return presenterProxy.getPresenter();
    }
}

最后看一下怎么使用?

@CreatePresenter(LoginPresenter.class)
public class MvpActivity extends BaseMvpActivity<LoginView, LoginPresenter> implements LoginView {

    private TextView content;
    private ProgressBar pb;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp);
        content = (TextView) findViewById(R.id.content);
        pb = (ProgressBar) findViewById(R.id.pb);
        getPresenter().doLogin();

    }

    @Override
    public void showLoading() {
        pb.setVisibility(View.VISIBLE);
    }

    @Override
    public void onSuccess(String response) {
        pb.setVisibility(View.GONE);
        content.setText(response);
    }

    @Override
    public void onFaile(String error) {
        pb.setVisibility(View.GONE);
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }
}

集成Lce的流程,我就不讲了,我的Demo里面有并且已经封装成了一个lib库,你用的时候直接引进即可。
代码传送门: https://github.com/liu20160703/mvpLib

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容