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,点击这里查看代码
- Android MVP框架学习
- Android学习笔记MVP模式框架
- Android 框架 MVC、MVP、MVVM学习笔记
- android MVP框架
- Android的MVP框架
- android 框架 MVP
- android项目框架MVP
- Android MVP框架详解
- Android 框架MVP详解
- Android MVP框架
- Android 框架MVP
- Android MVP框架解析
- 【项目架构】Android MVP 和MVVM框架模式 学习实例Demo之mvp篇
- 初试Android的MVP框架
- Android 初识 MVC、MVP框架
- 实现Android的MVP框架
- Android之MVC MVP框架
- Android pluginMVPM 可插拔MVP框架
- PAT乙级—数组循环右移
- 微信小程序wx.redirectTo、wx.navigateTo跳转失败
- 三分钟学会使用Chart.js,让我们在前端可视化数据
- Android中鲜为人知的 API
- 九度OJ 1107 搬水果(Huffman编码)
- Android MVP框架学习
- 用AndroidStudio的Monkey工具进行UI自动化测试--基础
- linux 同步IO: sync msync、fsync、fdatasync与 fflush
- MyEclipse从数据库反向生成Java实体类
- 键盘录入三个数据,返回三个数中的最大值
- 链表划分
- English in March(2017)
- 20170326——第一篇博客,我的心情很复杂
- Apache Shiro 权限框架