MVP架构

Android开发-写给两年前的自己

虽然最近一段时间资本的寒冬让大部分创业者都比较糟心,整个行业哀鸿遍野。没有融到资的公司已经倒下了,融到资的公司尚且还能苟延残喘,日子也并不好过。但是应当看到前几年移动互联网行业的繁荣也推动了技术领域的发展。我所了解的Android开发就经历过多次的变革,各种新框架以及各种新技术都在不断的出现,这些新的技术有些我们也应用到项目中来了。虽然缺乏深入的理解,我就不自量力的也来谈一谈Android的开发吧。
为什么要叫这个标题呢,是因为在这个领域曾经也是一只“萌新”,有过太多的不愉快的经历,从现在看过去的自己就想给当时的自己提一个醒。同时可也抱有一点别人能从我这里受到一些启发的心理。

MVP架构

软件架构这种东西网上一搜一大把,似乎大家永远写不厌,话永远说不完。反正我承认我自己有点啰嗦,为了以防大家跟不上我的思路,我觉得还是有必要在这一系列文章的最前面给大家扯一扯什么叫MVP。再加上架构这东西是我觉得最简单的一部分,并没有什么严格的规定,只要大家达成一个共识就行,所以先讲一下再展开我后面的一些章节。
架构最重要的目的是让大家更好的开发出高质量的软件。公司的业务总是不断向前发展的,业务的发展带来的就是代码的增长。那作为一个有责任心的程序员我们的目标就是要写出一些易读、易扩展、易调试的代码,否则你将很快就迷失在自己写出来代码里。一个坚实的架构是给软件开发带来的第一重保障。
App的核心其实就是要向用户展示一些东西,而要展示的内容要么是从Server提供给Client,要么是从本地进行加载。在这个基础上我们就划分出了两个职责:展示和内容提供。
注意:我并不想给你们定义Presenter和Model应当做什么工作 架构本身并不是不可改变的,它是为开发服务的,也许在本文中的内容并不一定适合大家的业务场景。

分解

  • View:Activity,Fragment和自定义控件,他们的职责就是展示信息给用户,同时接受用户的反馈。记住他们仅仅需要知道该显示什么就足够了,该什么时候显示不是并不是他们的职责。
  • Presenter:他控制View该什么时候交互和接受View的反馈并作出响应的处理。
  • Model:他们决定该显示什么内容

核心

  • View只能试图跟Presenter打交道,他和Presenter之间通过一些POJO或者封装的对象来进行通信。一般我们会抽象一个Interface传递给Presenter。
  • Presenter持有View的接口的引用。也通过View的一些反馈和Model进行沟通,这个期间可以执行一些耗时操作,这些操作既可以是更新数据也可以是获取数据。Presenter拿到结果后,再把Model的数据再通过接口回调给View。他可能依赖一些Network层的支撑。
  • Model他一般是一些Dao或者Manager,他可能还会依赖ORM数据持久,或者是Json的解析等工作。

这样整个架构图就清晰了,图画得有点丑,本来想用一下别人的图,但是怕版权问题。
MVP架构图

一般来讲我们的View都会提取一个Interface,我习惯按照Activity或者Fragment的名字后面加一个Viewer来进行命名。对于Presenter,我看过的许多MVP的架构实践里都提取一个Presenter的Interface来定义它的行为,但是我们在实际的工作中发现Presenter的实现其实并不一定要抽象一个接口,这个接口完全是多余的。因为Presenter的实现跟业务是强相关的,可以说它是绝无仅有的一次实现,你很难找到与它类似的一个业务场景可以进行复用。如果实在有一些地方需要用到Presenter里的某些实现,那么提取一些Helper或者Util类也是一个不错的选择。

抽象

非要找一段代码才能来解释清楚呢。

/**
 * Created by Hubert on 15/12/9.
 */
public interface Viewer {
    /**
     * Some common methods...
     */
}

这个Viewer是我们用来定义View层行为的一个基础类,它里面可以封装一些View层通用的基础行为。

/**
 * Created by Hubert on 15/12/9.
 */
public class Presenter<V extends Viewer> {

    V mViewer;

    public Presenter(V viewer) {
        mViewer = viewer;
    }


    protected V getViewer() {
        return mViewer;
    }
}

这个Presenter是我们实现Presenter的基类,所有的Presenter都应当继承它并实现它的构造函数。

/**
 * Created by Hubert on 15/12/9.
 */
public abstract class BaseActivity<T extends Presenter> extends AppCompatActivity {

    private T mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = newPresenter();
    }

    /**
     * Provide your own presenter for this activity.
     *
     * @return Presenter of specific activity.
     */
    protected abstract T newPresenter();

    protected T getPresenter() {
        return mPresenter;
    }

}

为了简化开发,我们一般会实现一个基础的BaseActivity,这个Activity有一个泛型参数是T,这个T是继承于Presenter的,同时它实现了Viewer的接口,newPresenter是一个抽象方法,要求它的子类必须有自己的实现。
以上这些实现是我们MVP整个架构的一个框架,它基本上把View和Presenter的职责区分清楚和切割干净了。

实现

下面是一个具体的实现。

/**
 * Created by Hubert on 16/7/7.
 */
public class MVPDemoActivity extends BaseActivity<MVPDemoPresenter> implements MVPDemoViewer {


    private TextView mTvResponse;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp_demo);
        mTvResponse = (TextView) findViewById(R.id.tv_response);

        // Load data from local
        getPresenter().loadDataFromLocal();
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Call load data
        getPresenter().loadDataFromServer();
    }

    /**
     * Provide your own presenter for this activity.
     *
     * @return Presenter of specific activity.
     */
    @Override
    protected MVPDemoPresenter newPresenter() {
        return new MVPDemoPresenter(this);
    }

    /**
     * On response received from server.
     *
     * @param response
     */
    @Override
    public void onResponseReceived(String response) {
        mTvResponse.setText(response);
    }
}

我们的Activity首先在onCreate的时候试图加载本地数据,并在onResume的时候向Presenter发起一个交互想要从服务器加载数据到本地。

/**
 * Created by Hubert on 16/7/7.
 */
public class MVPDemoPresenter extends Presenter<MVPDemoViewer> {

    private MVPDemoModel mModel;

    public MVPDemoPresenter(MVPDemoViewer viewer) {
        super(viewer);
        mModel = new MVPDemoModel();
    }

    public void loadDataFromLocal() {
        displayData(mModel.loadDataFromLocal());
    }

    public void loadDataFromServer() {
        AsyncTask task = new AsyncTask<Object, Object, String>() {
            @Override
            protected String doInBackground(Object[] params) {
                OkHttpClient client = new OkHttpClient();
                try {
                    Response response = client.newCall(new Request.Builder().url("http://baidu.com").build()).execute();
                    return response.toString();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(String response) {
                mModel.saveData(response);
                displayData(response);
            }
        };
        task.execute();
    }

    private void displayData(String response) {
        getViewer().onResponseReceived(response);
    }
}

Presenter实现了一个通过服务器加载数据的方法,为了简单我们直接通过OkHttp请求了百度这个网页的数据。
在OkHttp返回后将数据通过Viewer回调给了Activity。
Model的实现如下。

/**
 * Created by Hubert on 16/7/7.
 */
public class MVPDemoModel {

    private String data = "Make some noise.";

    /**
     * I'm a database query method.
     *
     * @return
     */
    public String loadDataFromLocal() {
        return data;
    }

    /**
     * I'm a database save method.
     *
     * @param data
     */
    public void saveData(String data) {
        this.data = data;
    }
}

注意:这里也许有一点争议那就是网络请求究竟是放到Model层还是放在Presenter,根据不同的业务需要可能有不同的考虑。但是在我们的实践中还是把网络请求直接放到了Presenter实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值