Base封装(一)--我的最简MVP架构
绪论
最近懒癌症犯了,好久都没写博客了,当然也在做一些东西,也在整理自己一直以来使用的一些技术点,从Retrofit到OkGO,从ListView到RecycleView,从Java到Kotlin….总之一直在尝试新的技术,今天分享一下自己一直所用的MVP,整理完了分享给大家,有不合适或者不正确的地方还希望大家多多指正,共同交流。
对了 打一波广告 我的新的个人博客 http://hankkin.cn/
好了接下来开始我们的MVP
背景
众所周知 MVP这种架构模式已经出现很久了,大体时间应该是2014年吧,现在网上的关于MVP的文章也很多,各式各样的关于MVP的架构知识都涌现出来,可想而知现在这种架构有多么火,还有目前风头正劲的MVVM,当然我并不觉得我现在写MVP有些晚,因为每个人都有每个人的架构,每个人都可以根据自己的逻辑封装出来自己的架构模式,今天我介绍的便是我自己通过项目总结出来的MVP
什么是MVP
MVP知识点
MVP - Model-View-Presenter
MVP和MVC的区别仅仅在于P和Control,MVC中View和Model是互通的可以互相通信,在Android中View一般代表着我们的xml进行界面的描述,而对于模型Model部分则大多对应于本地的数据文件或网络获取的数据体,很多情况下我们对这些数据的处理也会在这一层中进行,最后的控制器Controller则当之无愧的是右Activity承担。
而MVP中view通过presenter访问model,大大的减小了耦合性,业务逻辑都交给P处理,通过P访问V层更改UI。MVP模式可以分离显示层与逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。
这里不多介绍MVC了,相信大家都很熟悉
MVP的优缺点
优点:
- 降低耦合度,实现了M层和V层的完全分离,可以修改V层不影响M层
- 模块职责划分明显,层次清晰
- P层可以复用,一个P可以对应多个V,不需要修改P的逻辑
- 单元测试更加简单方便
- 代码灵活度高
缺点:
- V层和P层交互频繁
- 代码量多,类变多了
总结
- M层负责存储、检索、操纵数据,代表着一类组件或者类,这些组件或类可以向外部提供数据,同时也能从外部获取数据将数据存储起来
- V层负责将数据UI呈现给用户。一般的视图UI只包含界面,并不包含界面逻辑,V层收P层控制,在Android中一般是Activity、Fragment、View、ViewGroup。。。
- P层作为V层和M层的中间枢纽,处理用户交互的业务逻辑
MVP实现
1.基本实现
我们都知道一般MVP架构一共需要以下四步:
- 定义一个interface接口XView,对应的Activity,Fragment实现这个interface
- 编写Molde,里面的业务逻辑主要包括网络请求获取数据,数据库读取等耗时操作,通过M层回调给P层通知V层更新UI
- 编写Presenter,P层持有V和M的引用,实现P层的回调,并且回调给V层更新
- Activity中调用P执行业务逻辑,更新UI
具体代码就不贴了,相信了解过MVP的都会写基本的代码
但是问题也就出来了,由于P层需要和V层进行通信,更新UI时需要持有V层的view对象,那么我们每个P里面一般都用构造去初始化这个View,类多了之后感觉很烦,而View层里的一些常用的方法我们也可以封到base里面,比如loading的显示隐藏,空布局和错误布局的显示…
2.Base封装
1.BaseView
package com.hankkin.xlibrary.mvp; import android.view.View; /** * Created by hankkin on 2017/3/29. */ public interface BaseView { /** * 显示loading框 */ void showProgress(); /** * 隐藏loading框 */ void hideProgress(); void toast(CharSequence s); void toast(int id); void toastLong(CharSequence s); void toastLong(int id); /** * 显示空数据布局 */ void showNullLayout(); /** * 隐藏空数据布局 */ void hideNullLayout(); /** * 显示异常布局 * @param listener */ void showErrorLayout(View.OnClickListener listener); void hideErrorLayout(); }
2.BasePresenter
package com.hankkin.xlibrary.mvp; /** * Created by hankkin on 2017/3/29. */ public abstract class BasePresent<T>{ public T view; public void attach(T view){ this.view = view; } public void detach(){ this.view = null; } }
我们在BasePresenter里面去初始化View对象,同时提供释放View对象以防止内存溢出
3.MvpActivity
package com.hankkin.hlibrary.base; import android.os.Bundle; import android.support.annotation.Nullable; import com.lzy.okgo.OkGo; /** * Created by hankkin on 2017/3/29. */ public abstract class MvpActivity<V,P extends BasePresent<V>> extends BaseAcitvity{ protected P presenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = initPresenter(); } @Override protected void onResume() { super.onResume(); presenter.attach((V) this); } @Override protected void onDestroy() { presenter.detach(); OkGo.getInstance().cancelTag(this); super.onDestroy(); } public abstract P initPresenter(); }
这样我们在Activity中初始化P,并且连接V,在onDestroy()生命周期中释放P中引用的V。
Example
我们按照功能模块来构造我们的MVP,可能大家注意到了没有M层啊,是的,这里我把M层舍弃掉了,把业务逻辑、网络请求直接放在了P层,大大减少了类的数量,这样我们每个功能模块只需要新建一个View和一个Presenter就可以满足了,特殊的需求再通过特殊方法来处理,下面我们举一个简单的例子:
网络请求我用的 jeasonlzy 大神的OKGo3,刚出锅没几天,尝试一下,个人认为封装的非常非常好,继承了Rx,Retrofit,相信你会喜欢的。
https://github.com/jeasonlzy/okhttp-OkGo
好了下面看我们的例子吧:
项目结构
看一下项目结构
HomeView
我用的Gank.io里面的一个接口获取数据,首先我们定义我们的HomeView,里面有两个方法获取数据成功和获取失败
package com.hankkin.mvpdemo.home; import com.hankkin.hlibrary.BaseView; /** * Created by hankkin on 2017/6/19. */ public interface HomeView extends BaseView{ void getDataHttp(String data); void getDataHttpFail(String msg); }
HomePresenter
然后我们定义HomePresenter,里面只有我们的网络请求,因为我们的BasePresenter持有View对象,所以在回调中直接调用HomeView的两个成功失败的方法
package com.hankkin.mvpdemo.home; import com.hankkin.hlibrary.BasePresent; import com.lzy.okgo.OkGo; import com.lzy.okgo.callback.StringCallback; import com.lzy.okgo.model.Response; /** * Created by hankkin on 2017/6/19. */ public class HomePresenter extends BasePresent<HomeView> { public void getGankData(){ OkGo.<String>get("http://gank.io/api/data/Android/10/1") .tag(this) .execute(new StringCallback() { @Override public void onSuccess(Response<String> response) { view.getDataHttp(response.body()); } @Override public void onError(Response<String> response) { super.onError(response); view.getDataHttpFail(response.message()); } }); } }
Activity
最后看一下Activity,我们的Activity继承了MVPActivity并实现了HomeView,同时将泛型对象设为我们的HomeView和HomePresenter,这样我们就可以直接调用P层的网络请求方法,同时也能回调更新UI
package com.hankkin.mvpdemo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.hankkin.hlibrary.MvpActivity; import com.hankkin.mvpdemo.home.HomePresenter; import com.hankkin.mvpdemo.home.HomeView; import static com.hankkin.mvpdemo.R.id.btn_get; public class MainActivity extends MvpActivity<HomeView,HomePresenter> implements HomeView{ private TextView mTextMessage; private Button btnGet; private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: mTextMessage.setText(R.string.home); return true; case R.id.navigation_dashboard: mTextMessage.setText(R.string.control); return true; case R.id.navigation_notifications: mTextMessage.setText(R.string.notification); return true; } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextMessage = (TextView) findViewById(R.id.message); btnGet = (Button) findViewById(btn_get); BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); btnGet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showProgress(); presenter.getGankData(); } }); } @Override public HomePresenter initPresenter() { return new HomePresenter(); } @Override public void getDataHttp(String data) { mTextMessage.setText(data); hideProgress(); } @Override public void getDataHttpFail(String msg) { toast(msg); } @Override public void toast(CharSequence s) { toast("获取成功"); } }
结论
对于BaseActivity我在之前的文章里面已经介绍了,还不了解的请看
Android谈谈封装那些事–BaseActivity和BaseFragment(一)
Android谈谈封装那些事–BaseActivity和BaseFragment(二)
也已经优化过了相关的封装逻辑,也会在接下来的文章继续介绍的。
下一篇文章我会继续介绍我的封装之路,近期会将我的HLibrary提到我的Github上,大家可以star一下我的Github。
代码已经上传到我的Github
https://github.com/Hankkin/MvpDemo
好了是不是很简单呢?小伙伴们如果有啥好的建议或者觉得不妥的地方希望及时指正,共同交流,谢谢。
其实MVP有好多种,这里给大家推荐几个我觉得比较好的
http://www.jianshu.com/p/3a17382d44de#
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
帮程序员减压放松的10个良心网站
程序员们工作之余,不妨放下微博跟朋友圈,今天推荐的网站,利用代入感强的图片与音频,迅速帮你抹平焦虑,获得平和心态,特别献需求改千遍的程序员们。 1.Calm 这是同类型中最火的网站了,站如其名,「平和」,通过自然的图像(阳光下的暖流、淙淙的小溪等)与缓缓的音乐,帮你在短时间内放松下来。 左侧有时间设定,从2分钟到20分钟,右底部可以改变音频、图像,调节音量等。还有IOS客户端下载呦。 2. Do Nothing For 2 Minutes 「木头人,两分钟」,这是一个简单到极致的网站,当你打开的时候,自动开始计时,这时间你不能触碰键盘和鼠标,否则2分钟会重置。 你需要做的,就是放下手头的工作,静静地享受潮声,这也很棒,不是吗?两分钟足够你冷静下来,休息一下了。 3. Get Relaxed 如果两分钟不足以让你彻底放松,试试这个。如下图,打开网站后,头枕着双手往后仰,欣赏自然风光,聆听网站为你精心挑选的音乐。 图像3秒一换,有15种,每种持续大概2 – 4分钟,现在,开始吧! 提醒:网站有简陋广告,稍微影响体验。 4. LoungeV Studio 前三个都是图像,现在来个新鲜的。这个...
- 下一篇
Appium Android Driver 简单修改
【注】文中提起的 Appium 版本為 v1.6.5 对 Appium Driver 进行简单的修改,并更新方式如下: 修改 appium-android-driver 中文件,比如去掉对 app 依赖的错误日志 $ vim appium-android-driver/lib/driver.js 具体修改如下: diff --git a/lib/driver.js b/lib/driver.js index 2e60375..fbe2035 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -444,7 +444,8 @@ class AndroidDriver extends BaseDriver { if ((!caps.browserName || !helpers.isChromeBrowser(caps.browserName)) && !caps.app && !caps.appPackage) { let msg = 'The desired capabilities must includ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Hadoop3单机部署,实现最简伪集群
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装