Android MVP框架学习

来源:互联网 发布:免费的linux云主机 编辑:程序博客网 时间:2024/05/14 04:49

什么是MVP框架

做Android开发也有好几年时间了,最近接触了Android开发的MVP模式,MVP即Model、View、Presenter的缩写。如果有过一些项目开发经验的人,在项目功能越来越多,逻辑越来越复杂的时候,代码一定会写得越来越乱,乱到自己都很难看下去了(本人在做项目时深有体会)。因为Android以前的开发模式比较类似于MVC框架,XML布局为View层,数据实体为Model层,Activity为Controller层,当一个Activity中的逻辑比较多的时候,Activity中的代码过千行是非常容易达到的,而且很多逻辑和View交互的代码都在Activity中,这样的开发模式,导致View和Controller耦合得非常紧,不仅代码写着看着乱,而且也非常不便于后期的维护和扩展。MVP开发模式的推出,不仅非常有效的解决了View和Controller耦合的问题,而且Activity被彻底当作View来看待了,模型和视图的交互是Presenter和View通过接口来完成,代码层次也分明了,后期的扩展和修改也很方便了。

举个栗子

光说无用,举个例子来说明最实在最容易理解了,下面的一个Demo是我在学习MVP框架时写的,希望自己写得没错(如果有错希望大神指出)
这个项目名为Test MVP,主要功能是实现下载文件,页面上有一个下载按钮,还有一个进度条,下图是项目的目录结构:
这里写图片描述
下面上布局文件的代码:

<?xml version="1.0" encoding="utf-8"?><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.test.testmvp.MainActivity">    <Button        android:id="@+id/download_btn"        android:onClick="handleDownloadBtnClick"        android:text="CLICK TO DOWNLOAD"        android:layout_width="match_parent"        android:layout_height="wrap_content" />    <ProgressBar        android:layout_below="@+id/download_btn"        android:layout_marginTop="20dp"        android:visibility="gone"        android:id="@+id/progress_bar"        android:max="100"        style="@android:style/Widget.ProgressBar.Horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content" /></RelativeLayout>

布局文件很简单,就没必要多说明了。
在view包中,主要是一个View的接口文件IDownloadView.java,代码如下:

package com.test.testmvp.view;/** * Created by yubo on 2017/3/26. * view层的抽象接口,需要Activity去实现该接口(所以Activity当作view看待) */public interface IDownloadView {    void initViews();    void showProgressBar();    void hideProgressBar();    void updateProgressBar(int progress);    void showToast(String msg);}

这个View的接口文件,定义了一系列有关View的操作的方法,由于MVP模式中将Activity完全视为View来看待,所以我们的Activity需要实现这个IDownloadView接口,关于Activity的具体实现,放在后面贴代码。
下面说presenter包,由于本Demo中主要就是一个下载的逻辑,所以IDownloadPresenter接口中,主要就是一个下载的方法,代码如下:

package com.test.testmvp.presenter;import com.test.testmvp.listener.OnDownloadListener;import java.io.File;/** * Created by yubo on 2017/3/26. */public interface IDownloadPresenter {    //下载文件的逻辑,由实现类去实现该方法,listener用于监听下载过程    void startDownload(String urlStr, File saveFile, OnDownloadListener listener);}

下载的IDownloadPresenter接口的startDownload方法有三个参数,第一个是下载地址,第二个是下载的文件,第三个是一个下载的监听器,OnDownloadListener代码如下:

package com.test.testmvp.listener;/** * Created by yubo on 2017/3/26. * 下载的监听器,处理下载的不同阶段 */public interface OnDownloadListener {    void onStartDownload();    void onProgressUpdate(int progress);    void onDownloadSuccess();    void onDownloadError(Exception e);}

主要就是监听开始下载、下载进度、下载完成、下载出错这4个过程。有了IDownloadPresenter接口,还需要一个实现类DownloadPresenterImpl,这个实现类的代码如下:

package com.test.testmvp.presenter;import android.app.Activity;import android.content.Context;import com.test.testmvp.listener.OnDownloadListener;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;/** * Created by yubo on 2017/3/26. */public class DownloadPresenterImpl implements IDownloadPresenter {    private Context mContext;    public DownloadPresenterImpl(Context context) {        this.mContext = context;    }    @Override    public void startDownload(final String urlStr, final File saveFile, final OnDownloadListener listener) {        if (listener == null) {            throw new IllegalArgumentException("OnDownloadListener should not be null!");        }        listener.onStartDownload();        new Thread(new Runnable() {            @Override            public void run() {                InputStream inputStream = null;                FileOutputStream fileOutputStream = null;                try {                    URL url = new URL(urlStr);                    URLConnection conn = url.openConnection();                    inputStream = conn.getInputStream();                    fileOutputStream = new FileOutputStream(saveFile);                    int totalSize = inputStream.available();                    int hasDownload = 0;                    int hasRead = 0;                    byte[] buf = new byte[512];                    while ((hasRead = inputStream.read(buf)) > 0) {                        fileOutputStream.write(buf, 0, hasRead);                        hasDownload += hasRead;                        final int progress = (int) (hasDownload * 100.0f / totalSize);                        listener.onProgressUpdate(progress);                        ((Activity)mContext).runOnUiThread(new Runnable() {                            @Override                            public void run() {                                listener.onProgressUpdate(progress);                            }                        });                    }                    ((Activity)mContext).runOnUiThread(new Runnable() {                        @Override                        public void run() {                            listener.onDownloadSuccess();                        }                    });                } catch (final MalformedURLException e) {                    e.printStackTrace();                    ((Activity)mContext).runOnUiThread(new Runnable() {                        @Override                        public void run() {                            listener.onDownloadError(e);                        }                    });                } catch (final IOException e) {                    e.printStackTrace();                    ((Activity)mContext).runOnUiThread(new Runnable() {                        @Override                        public void run() {                            listener.onDownloadError(e);                        }                    });                } finally {                    if (inputStream != null) {                        try {                            inputStream.close();                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                    if (fileOutputStream != null) {                        try {                            fileOutputStream.close();                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                }            }        }).start();    }}

逻辑和视图的解耦,就是靠的Presenter完成的(要是用以前的类似MVC的开发模式,那Presenter中的代码就全部写到Activity中去了)。下面还有一个DownloadFileModel类,就是对下载的文件做了一个简单的封装,代码如下:

package com.test.testmvp.model;import java.io.File;import java.io.IOException;/** * Created by yubo on 2017/3/26. */public class DownloadFileModel {    private String fileName;    private String fileSavePath;    private File file;    public DownloadFileModel(String fileName, String fileSavePath) {        this.fileName = fileName;        this.fileSavePath = fileSavePath;        this.file = new File(fileSavePath + File.separator + fileName);        if (this.file.exists()) {            this.file.delete();        }        try {            this.file.createNewFile();        } catch (IOException e) {            e.printStackTrace();        }    }    public File getFile() {        return this.file;    }    public String getFilePath() {        return this.file.getAbsolutePath();    }}

下面上MainActivity的代码:

package com.test.testmvp;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ProgressBar;import android.widget.Toast;import com.test.testmvp.listener.OnDownloadListener;import com.test.testmvp.model.DownloadFileModel;import com.test.testmvp.presenter.DownloadPresenterImpl;import com.test.testmvp.view.IDownloadView;public class MainActivity extends AppCompatActivity implements IDownloadView {    //文件下载地址,可能已失效    private static final String DOWNLOAD_FILE_URL = "http://do.xiazaiba.com/route.php?ct=stat&ac=stat&g=aHR0cDovL2FwcHMud2FuZG91amlhLmNvbS9yZWRpcmVjdD9zaWduYXR1cmU9YzkxNGQ2OSZ1cmw9aHR0cCUzQSUyRiUyRmRvd25sb2FkLmVvZW1hcmtldC5jb20lMkZhcHAlM0ZjaGFubmVsX2lkJTNEMTAwJTI2Y2xpZW50X2lkJTI2aWQlM0QyNDQwMDMmcG49Y29tLnVzb2Z0LmFwcC51aSZtZDU9OTI4MWIxZjU1YmM1YzExNmYyMThmZjYwY2FhZWEwMmImYXBraWQ9MTQyODY1ODgmdmM9MTEmc2l6ZT0zNDE0MTEw";    //下载的文件名    private static final String DOWNLOAD_FILE_NAME = "test.apk";    //下载的文件存放路径    private static final String DOWNLOAD_FILE_SAVE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();    //进度条控件    private ProgressBar mProgressBar;    //处理下载的Presenter    private DownloadPresenterImpl downloadPresenter;    //下载的文件Model    private DownloadFileModel downloadFileModel;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();        downloadFileModel = new DownloadFileModel(DOWNLOAD_FILE_NAME, DOWNLOAD_FILE_SAVE_PATH);        downloadPresenter = new DownloadPresenterImpl(this);    }    @Override    public void initViews() {        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);    }    //处理下载按钮点击事件    public void handleDownloadBtnClick(View view) {        downloadPresenter.startDownload(DOWNLOAD_FILE_URL, downloadFileModel.getFile(), new OnDownloadListener() {            @Override            public void onStartDownload() {                showProgressBar();            }            @Override            public void onProgressUpdate(int progress) {                updateProgressBar(progress);            }            @Override            public void onDownloadSuccess() {                hideProgressBar();                showToast("download success, path: " + downloadFileModel.getFilePath());            }            @Override            public void onDownloadError(Exception e) {                hideProgressBar();                showToast(e.getMessage());            }        });    }    @Override    public void showProgressBar() {        mProgressBar.setVisibility(View.VISIBLE);    }    @Override    public void hideProgressBar() {        mProgressBar.setVisibility(View.GONE);    }    @Override    public void updateProgressBar(int progress) {        mProgressBar.setProgress(progress);    }    @Override    public void showToast(String msg) {        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();    }}

可以看到Activity实现了IDownloadView接口,并实现了接口里的方法,Activity持有了一个Presenter的引用和一个Model的引用,Activity作为View来看待,当按钮有点击事件时,会调用Presenter的startDownload方法来处理业务逻辑,而视图的更新,是在startDownload方法的回调接口中,调用IDownloadView接口的方法去完成的,不会存在逻辑和视图的直接交互,这样也就达到了低耦合的目的。
OK了,整篇学习笔记就到这里了,希望自己的理解没有错误,也是参考了博客大神的博文来学习的,这里贴出博客大神的文章:
http://blog.csdn.net/lmj623565791/article/details/46596109

本篇学习笔记中的代码已上传到github,点击这里查看代码

0 0