首先谈下Android种常见的几种项目的架构模式的优缺点:
一.MVC (Model-View-Presenter):作用是数据模型与业务和展示逻辑解耦,在客户端应用开发中,就是将模型(M-数据)、视图(V-页面)之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。他们的调用流程是:
View 传送指令到 Controller ;
Controller 完成业务逻辑后,要求 Model 改变状态 ;
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优点:
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,
@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 = MutableLiveData()
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文件路径:
<-- 在这里写你的布局代码,这里做了省略 -->
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的使用流程就完成了,欢迎大家看后进行留言评论交流探讨,谢谢!