Android架构模式mvc,mvp,mvvm的优缺点总结与应用中mvvm接入教程_android mvvm的缺点-程序员宅基地

技术标签: android  

首先谈下Android种常见的几种项目的架构模式的优缺点:

一.MVC (Model-View-Presenter):作用是数据模型与业务和展示逻辑解耦,在客户端应用开发中,就是将模型(M-数据)、视图(V-页面)之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。他们的调用流程是:

  1. View 传送指令到 Controller ;

  2. Controller 完成业务逻辑后,要求 Model 改变状态 ;

  3. Model 将新的数据发送到 View,用户得到反馈。

 优点是:耦合性低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码。重用性高

 缺点是:view和vontroller连接过于紧密,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

二.MVP:(Model-View-Presenter)是MVC的改良模式,由IBM的子公司Taligent提出。和MVC的相同之处在于:Controller/Presenter负责业务逻辑,Model管理数据,View负责显示只不过是将 Controller 改名为 Presenter,同时改变了通信方向。

mvp的互相调用特点是:1.View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。2.逻辑代码都在presenter中

mvp优点:

  1. 模型与视图完全分离,我们可以修改视图而不影响模型;
  2. 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部;
  3. 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁; 

mvp缺点: view和presenter使用接口量很大,视图和Presenter的交互会过于频繁,一旦视图变更了,presenter也需要变更。

三.MVVM(Model-View-ModelView) :MVVM是由微软提出来的,MVVM相对于MVC的改进是对VM/P和view做了双向的数据和命令绑定。

优点:利用观察者模式和数据绑定,减少写回调接口的过程,代码和逻辑和更简洁清晰

缺点:随着后期功能不断增加,viewmodel中的代码量会越来越多。

 

项目中接入mvvm的代码示例:

第一步:项目的build.gradle文件中加入如下dataBinding依赖和打开dataBinding开关:

android {
  '''
  dataBinding {
        enabled = true
    }
  ...

}



dependencies {

    // databinding库
    kapt "com.android.databinding:compiler:$gradlePluginVersion"
}

第二步:建立自己的model,viewmodel,view三个层的目录,然后分别创建各个层下面对应业务的文件,

首先从model层开始:和mvp,mvc一样,model层里面的类负责封装解析json数据的模型类,

我这里在model目录下创建了一个WallpaperlistBeanF的实体类:

class WallpaperlistBeanF(
        @SerializedName("code") var code: Int,
        @SerializedName("data")var data: List<Data>,
        @SerializedName("msg") var msg: String

) : Serializable {
        data class Data(
                @SerializedName("id") var id: Int,
                @SerializedName("wname") var wname: String,
                @SerializedName("wcategory") var wcategory: String,
                @SerializedName("wurl_preview") var wurl_preview: String,
                @SerializedName("wurl_middle") var wurl_middle: String,
                @SerializedName("wurl_master") var wurl_master: String,
                @SerializedName("whot") var whot: Int,
                @SerializedName("wjurisdiction") var wjurisdiction: Int,
                @SerializedName("wpush_date") var wpush_date: String,
                @SerializedName("wadvert") var wadvert: Boolean,
                @SerializedName("wis_dynamic") var wis_dynamic: Boolean,
                @SerializedName("wcreate_date") var wcreate_date: String,
                @SerializedName("wshow") var wshow: Boolean

        ):Serializable {
                override fun toString(): String {
                        return "Data(id=$id, wname='$wname', wcategory='$wcategory', wurl_preview='$wurl_preview', wurl_master='$wurl_master', whot=$whot, wjurisdiction=$wjurisdiction, wpush_date='$wpush_date', wadvert=$wadvert, wis_dynamic=$wis_dynamic, wcreate_date='$wcreate_date', wshow=$wshow)"
                }
        }

        override fun toString(): String {
                return "WallpaperlistBeanF(code=$code, data=$data, msg='$msg')"
        }
}

接下来写viewmodel层的代码:

先贴一段我自己做的网络请求方面的各种returnCode的封装,里面包含了标识首次请求,下拉刷新,上拉加载更多和各种请求反馈状态值的静态常量:

public class RequestApi {

    public static int RequestParmError_Code = -1; // 请求参数异常状态值
    public static int NotLoad_Code = 0; // 没有调用请求状态
    public static int NetWorkError_Code = 1;// 网络异常状态值
    public static int NoMoreData_Code = 2;// 没有更多数据状态值
    public static int LoadDataSuccess_Code = 3;// 初次加载数据成功状态值
    public static int LoadDataFail_Code = 4;// 初次加载数据失败状态值
    public static int RefreshDataSuccess_Code = 5;// 刷新数据成功状态值
    public static int LoadMoreSuccess_Code = 6;// 加载更多数据成功状态值

    public static int FirstLoadData = 8;// 第一次加载状态值
    public static int RefreshData = 9;// 刷新数据状态值
    public static int LoadMoreData = 10;// 加载更多状态值

}

viewmodel代码:

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel
import android.content.Context
import android.databinding.ObservableInt
import android.util.Log
import android.view.View
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

// 我这里是使用的封装好的retrofit2网络请求,你在使用时换成你自己的网络封装需要,然后有几次需要导的包你用不到,我给删了import路径了
class WallpaperListActViewModel:ViewModel(){
    private var TAG :String = "WallpaperListActivity"
    var loadStatusView = ObservableInt(View.VISIBLE)
    var category :String = ""
    var apiversion :String = "2.0"

    // 网络请求状态值的观察者变量,不同的请求反馈都会改变这个值,当这个观察者变量发生变化的时候它的观察者会得到通知
    var loadState:MutableLiveData<Int>  = MutableLiveData<Int>()

    companion object {
        var page_on :Int = 0
    }

init {
    loadState.value = RequestApi.NotLoad_Code
}

    fun loadData( requestCode:Int,context:Context){
        if (StringUtils.StringIsNull(category)){
            if (NetWorkUnits.isNetworkAvailable(context)){
                if (requestCode==RequestApi.FirstLoadData||requestCode==RequestApi.RefreshData){
                    page_on = 0
                }else{
                    page_on++
                }

                var server = MyAPP.Companion.initRetrofitInstance().create(IPServer::class.java)
                server.getWallpaperListData(category,page_on.toString(),apiversion)
                        .subscribeOn(Schedulers.newThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe({result ->
                            if (result.code==0){
                                loadStatusView.set(View.GONE)

                                if (requestCode==RequestApi.FirstLoadData){
                                    StackSave.wallpaperBeanList.clear()
                                    StackSave.wallpaperBeanList.addAll(result.data.toMutableList())
                                    loadState.postValue(RequestApi.LoadDataSuccess_Code)
                                    loadStatusView.set(View.GONE)
                                }else if (requestCode==RequestApi.RefreshData){
                                    StackSave.wallpaperBeanList = result.data.toMutableList()
                                    loadState.postValue(RequestApi.RefreshDataSuccess_Code)
                                }else if (requestCode==RequestApi.LoadMoreData){
                                    StackSave.wallpaperBeanList.addAll(result.data.toMutableList())
                                    loadState.postValue(RequestApi.LoadMoreSuccess_Code)
                                }
                                Log.d(TAG,"请求成功----->"+result.data.toString())
                            }else if(result.code==1){
                                loadState.postValue(RequestApi.RequestParmError_Code)
                            }

                        },{error ->
                            loadStatusView.set(View.GONE)
                            Log.d(TAG,"请求失败--> error message:"+error.cause)
                            if (requestCode==RequestApi.FirstLoadData){
                                loadState.postValue(RequestApi.LoadDataFail_Code)
                            }else if (requestCode==RequestApi.RefreshData){
                            }else if (requestCode==RequestApi.LoadMoreData){
                            }

                        })
            }else{
                loadState.postValue(RequestApi.NetWorkError_Code)
            }
        }

    }


}

最后view层实现:

需要先在xml代码中加入一个layout标签,并指定对应的viewmodel文件路径:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="wallpaperListActViewModel"
            type="你的包名.viewmodel.WallpaperListActViewModel" />
    </data>

     <--  在这里写你的布局代码,这里做了省略 -->
      


</layout>

activity中的代码:

class WallpaperListActivity: Activity() {

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

         initView()
         initData()

    }


        fun initData() {
        category = intent.getStringExtra("category")
        tv_wallpaper_title.text = category
        isVideo = intent.getBooleanExtra("isVideo",false)
        wallpaperListActViewModel?.category = category
        wallpaperListActViewModel?.loadData(RequestApi.FirstLoadData,this)

        // 对viewmodel中的进行加载状态值的变量进行观察监听变化,并当发生变化的时候判断并做对应的页面状态更新
        wallpaperListActViewModel?.loadState?.observe(this, Observer {state ->
            when(state){
                RequestApi.NetWorkError_Code ->{ Toast.makeText(this,"Request parameter exception", Toast.LENGTH_SHORT).show()}
                RequestApi.RequestParmError_Code ->{ Toast.makeText(this,"Net Work Error", Toast.LENGTH_SHORT).show()}
                RequestApi.LoadDataSuccess_Code ->{
                    adapter = WallpaperListAdapter(this,StackSave.wallpaperBeanList,isVideo,category)
                    swipe_target.adapter = adapter
                    wallpaperlist_load_status.setHide()
                }
                RequestApi.RefreshDataSuccess_Code ->{
                    adapter = WallpaperListAdapter(this,StackSave.wallpaperBeanList,isVideo,category)
                    swipe_target.adapter = adapter
                    walpaperlist_swipeToLoad?.isRefreshing = false
                }
                RequestApi.LoadMoreSuccess_Code ->{
                    lastItemsSize = StackSave.wallpaperBeanList.size
                    var itee: SimpleItemAnimator = swipe_target!!.itemAnimator as SimpleItemAnimator
                    itee.supportsChangeAnimations = false  //取消RecyclerView的动画效果
                    adapter!!.notifyItemRangeChanged(lastItemsSize, lastItemsSize)
                    walpaperlist_swipeToLoad?.isLoadingMore = false
                }
                RequestApi.NoMoreData_Code ->{  Toast.makeText(this,"No more data", Toast.LENGTH_SHORT).show() }
                RequestApi.LoadDataFail_Code ->{ wallpaperlist_load_status.setFailRefresh() }
            }
        })
    }




  @SuppressLint("NewApi")
    fun initView() {
       // 做databinding初始化
        initDataBinding()
        var layoutManager = GridLayoutManager(this,3)
        swipe_target.layoutManager = layoutManager as RecyclerView.LayoutManager?
       
        //添加过渡滑动
        walpaperlist_swipeToLoad.setRefreshCompleteDelayDuration(800)
        // 下拉刷新
        walpaperlist_swipeToLoad.setOnRefreshListener {
          wallpaperListActViewModel?.loadData(RequestApi.RefreshData,this)
        }
        // 下拉加载更多
        walpaperlist_swipeToLoad.setOnLoadMoreListener {
            wallpaperListActViewModel?.loadData(RequestApi.LoadMoreData,this)
        }
        wallpaperlist_load_status.setOnRefreshListener {  wallpaperListActViewModel?.loadData(RequestApi.FirstLoadData,this) }
        rl_back_btn.setOnClickListener {
            finish()
        }
        wallpaperlist_load_status.setLoading()
    }

    private fun initDataBinding() {
        dataBinding =  DataBindingUtil.setContentView(this,R.layout.activity_wallpaper_list)
        wallpaperListActViewModel = ViewModelProviders.of(this).get(WallpaperListActViewModel::class.java)
        dataBinding?.wallpaperListActViewModel = wallpaperListActViewModel
    }



}

好了,到此简单的mvvm的使用流程就完成了,欢迎大家看后进行留言评论交流探讨,谢谢!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wjj1996825/article/details/87784331

智能推荐

React学习记录-程序员宅基地

文章浏览阅读936次,点赞22次,收藏26次。React核心基础

Linux查磁盘大小命令,linux系统查看磁盘空间的命令是什么-程序员宅基地

文章浏览阅读2k次。linux系统查看磁盘空间的命令是【df -hl】,该命令可以查看磁盘剩余空间大小。如果要查看每个根路径的分区大小,可以使用【df -h】命令。df命令以磁盘分区为单位查看文件系统。本文操作环境:red hat enterprise linux 6.1系统、thinkpad t480电脑。(学习视频分享:linux视频教程)Linux 查看磁盘空间可以使用 df 和 du 命令。df命令df 以磁..._df -hl

Office & delphi_range[char(96 + acolumn) + inttostr(65536)].end[xl-程序员宅基地

文章浏览阅读923次。uses ComObj;var ExcelApp: OleVariant;implementationprocedure TForm1.Button1Click(Sender: TObject);const // SheetType xlChart = -4109; xlWorksheet = -4167; // WBATemplate xlWBATWorksheet = -4167_range[char(96 + acolumn) + inttostr(65536)].end[xlup]

若依 quartz 定时任务中 service mapper无法注入解决办法_ruoyi-quartz无法引入ruoyi-admin的service-程序员宅基地

文章浏览阅读2.3k次。上图为任务代码,在任务具体执行的方法中使用,一定要写在方法内使用SpringContextUtil.getBean()方法实例化Spring service类下边是ruoyi-quartz模块中util/SpringContextUtil.java(已改写)import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.s..._ruoyi-quartz无法引入ruoyi-admin的service

CentOS7配置yum源-程序员宅基地

文章浏览阅读2w次,点赞10次,收藏77次。yum,全称“Yellow dog Updater, Modified”,是一个专门为了解决包的依赖关系而存在的软件包管理器。可以这么说,yum 是改进型的 RPM 软件管理器,它很好的解决了 RPM 所面临的软件包依赖问题。yum 在服务器端存有所有的 RPM 包,并将各个包之间的依赖关系记录在文件中,当管理员使用 yum 安装 RPM 包时,yum 会先从服务器端下载包的依赖性文件,通过分析此文件从服务器端一次性下载所有相关的 RPM 包并进行安装。_centos7配置yum源

智能科学毕设分享(算法) 基于深度学习的抽烟行为检测算法实现(源码分享)-程序员宅基地

文章浏览阅读828次,点赞21次,收藏8次。今天学长向大家分享一个毕业设计项目毕业设计 基于深度学习的抽烟行为检测算法实现(源码分享)毕业设计 深度学习的抽烟行为检测算法实现通过目前应用比较广泛的 Web 开发平台,将模型训练完成的算法模型部署,部署于 Web 平台。并且利用目前流行的前后端技术在该平台进行整合实现运营车辆驾驶员吸烟行为检测系统,方便用户使用。本系统是一种运营车辆驾驶员吸烟行为检测系统,为了降低误检率,对驾驶员视频中的吸烟烟雾和香烟目标分别进行检测,若同时检测到则判定该驾驶员存在吸烟行为。进行流程化处理,以满足用户的需要。

随便推点

STM32单片机示例:多个定时器同步触发启动_stm32 定时器同步-程序员宅基地

文章浏览阅读3.7k次,点赞3次,收藏14次。多个定时器同步触发启动是一种比较实用的功能,这里将对此做个示例说明。_stm32 定时器同步

android launcher分析和修改10,Android Launcher分析和修改9——Launcher启动APP流程(转载)...-程序员宅基地

文章浏览阅读348次。出处 : http://www.cnblogs.com/mythou/p/3187881.html本来想分析AppsCustomizePagedView类,不过今天突然接到一个临时任务。客户反馈说机器界面的图标很难点击启动程序,经常点击了没有反应,Boss说要优先解决这问题。没办法,只能看看是怎么回事。今天分析一下Launcher启动APP的过程。从用户点击到程序启动的流程,下面针对WorkSpa..._回调bubbletextview

Ubuntu 12 最快的两个源 个人感觉 163与cn99最快 ubuntu安装源下包过慢_un.12.cc-程序员宅基地

文章浏览阅读6.2k次。Ubuntu 12 最快的两个源 个人感觉 163与cn99最快 ubuntu下包过慢 1、首先备份Ubuntu 12.04源列表 sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup (备份下当前的源列表,有备无患嘛) 2、修改更新源 sudo gedit /etc/apt/sources.list (打开Ubuntu 12_un.12.cc

vue动态路由(权限设置)_vue动态路由权限-程序员宅基地

文章浏览阅读5.8k次,点赞6次,收藏86次。1.思路(1)动态添加路由肯定用的是addRouter,在哪用?(2)vuex当中获取到菜单,怎样展示到界面2.不管其他先试一下addRouter找到router/index.js文件,内容如下,这是我自己先配置的登录路由现在先不管请求到的菜单是什么样,先写一个固定的菜单通过addRouter添加添加以前注意:addRoutes()添加的是数组在export defult router的上一行图中17行写下以下代码var addRoute=[ { path:"/", name:"_vue动态路由权限

JSTL 之变量赋值标签-程序员宅基地

文章浏览阅读8.9k次。 关键词: JSTL 之变量赋值标签 /* * Author Yachun Miao * Created 11-Dec-06 */关于JSP核心库的set标签赋值变量,有两种方式: 1.日期" />2. 有种需求要把ApplicationResources_zh_CN.prope

VGA带音频转HDMI转换芯片|VGA转HDMI 转换器方案|VGA转HDMI1.4转换器芯片介绍_vga转hdmi带音频转换器,转接头拆解-程序员宅基地

文章浏览阅读3.1k次,点赞3次,收藏2次。1.1ZY5621概述ZY5621是VGA音频到HDMI转换器芯片,它符合HDMI1.4 DV1.0规范。ZY5621也是一款先进的高速转换器,集成了MCU和VGA EDID芯片。它还包含VGA输入指示和仅音频到HDMI功能。进一步降低系统制造成本,简化系统板上的布线。ZY5621方案设计简单,且可以完美还原输入端口的信号,此方案设计广泛应用于投影仪、教育多媒体、视频会议、视频展台、工业级主板显示、手持便携设备、转换盒、转换线材等产品设计上面。1.2 ZY5621 特性内置MCU嵌入式VGA_vga转hdmi带音频转换器,转接头拆解

推荐文章

热门文章

相关标签