Android的架构模式的学习和使用(MVP/MVVM)

目录

 

Android的架构模式的学习和使用(MVP/MVVM)

一、MVP模式(Kotlin编写)

1.1、例子项目目录展示:

1.2、模型层(处理网络请求获取到数据类 对应MainActivityModel,请求封装在 RetrofitUtils中)

1.3、视图层(Activity作为界面展示,接口 MainActivityView 回调界面更新)

1.4、主持层/处理层(MainPresenter 作为主持者,接口是父类 IMainActivityPresenter)

二、MVVM模式(Kotlin编写)

2.1、例子项目目录展示:

2.2、ViewModel层(QueryWeatherViewModel =>Model层这里只是简单的JavaBean,可以抽出一个接口Model来网络请求的)

2.3、视图层(这里的视图层主要是DataBinding的Layout和Activity)

三、例子项目下载(推荐百度云下载,免费)


 

Android的架构模式的学习和使用(MVP/MVVM)

通用的核心库:

    //引入rxjava
    implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

    //引入Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.14.4'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'

一、MVP模式(Kotlin编写)

1.1、例子项目目录展示:

1.2、模型层(处理网络请求获取到数据类 对应MainActivityModel,请求封装在 RetrofitUtils中)

package com.zbv.mvp_android.`interface`.main

import com.zbv.mvp_android.javabean.BannarBean
import com.zbv.mvp_android.javabean.HomeDataFirstBean
import com.zbv.mvp_android.net.ResponseBean
import com.zbv.mvp_android.net.RetrofitUtils
import io.reactivex.Observable

/**
 * author: qzx
 * Date: 2019/11/21 8:58
 */
class MainActivityModel {

    fun getBananarData(): Observable<ResponseBean<MutableList<BannarBean>>> {
        return RetrofitUtils.getInstance().getRequest()!!.getBannarDatas()
    }

    fun getHomeData(pageIndex: Int): Observable<ResponseBean<HomeDataFirstBean>> {
        return RetrofitUtils.getInstance().getRequest()!!.getHomeDatas(pageIndex)
    }

}

1.3、视图层(Activity作为界面展示,接口 MainActivityView 回调界面更新)

package com.zbv.mvp_android.`interface`.main

import com.zbv.mvp_android.javabean.BannarBean
import com.zbv.mvp_android.javabean.HomeDataSecondBean

/**
 * author: qzx
 * Date: 2019/11/21 8:49
 */
interface MainActivityView {
    fun getBananarSuccess(bananarData: MutableList<BannarBean>)
    fun getBananarFail(errorMsg: String)

    fun getHomeDataSuccess(homeData: MutableList<HomeDataSecondBean>)
    fun getHomeDataFail(errorMsg: String)
}
package com.zbv.mvp_android

import android.graphics.Rect
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.widget.Toast
import com.zbv.mvp_android.`interface`.main.IMainActivityPresenter
import com.zbv.mvp_android.`interface`.main.MainActivityView
import com.zbv.mvp_android.`interface`.main.MainPresenter
import com.zbv.mvp_android.adapter.HomeRVAdapter
import com.zbv.mvp_android.adapter.OriginalRVHomeAdapter
import com.zbv.mvp_android.bannar.GlideImageLoader
import com.zbv.mvp_android.javabean.BannarBean
import com.zbv.mvp_android.javabean.HomeDataSecondBean
import kotlinx.android.synthetic.main.activity_main.*

/**
 * Android MVC 模式中 Activity做为Controller层,也作为View层
 * 这里采用 MVP 模式 Activity作为View层,持有Presenter对象,在Presenter主持层中处理逻辑,完全隔离Model层,采用接口方式最好
 * todo presenter view model都有一个接口父类这样耦合会更低点
 * */
class MainActivity : AppCompatActivity(), MainActivityView {

    private val presenter: IMainActivityPresenter by lazy {
        MainPresenter(this)
    }

    override fun getBananarSuccess(bananarData: MutableList<BannarBean>) {
        imageUrls = bananarData

        var urls: MutableList<String> = mutableListOf()

        for (image in imageUrls!!) {
            urls.add(image.imagePath!!)
        }

        banner.setImages(urls)
        banner.start()
    }

    override fun getBananarFail(errorMsg: String) {
        Log.e("zbv", "获取滚动Bananar失败... $errorMsg")
        Toast.makeText(this, "获取滚动Bananar失败... $errorMsg", Toast.LENGTH_SHORT).show()
    }

    override fun getHomeDataSuccess(homeData: MutableList<HomeDataSecondBean>) {
        Log.e("zbv", "home data success..." + homeData)
        items.clear()
        for (item in homeData) {
            items.add(item)
        }
        adapter?.notifyDataSetChanged()

        myAdapter?.notifyDataSetChanged()
    }

    override fun getHomeDataFail(errorMsg: String) {
        Log.e("zbv", "获取HomeData失败... $errorMsg")
        Toast.makeText(this, "获取HomeData失败... $errorMsg", Toast.LENGTH_SHORT).show()
    }

    var imageUrls: MutableList<BannarBean>? = null
    var items: MutableList<HomeDataSecondBean> = mutableListOf()

    var adapter: HomeRVAdapter? = null

    var myAdapter: OriginalRVHomeAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        presenter.getBananarData()
        presenter.getHomeData(200)


//        -----------------------轮播------------------------
        banner.setImageLoader(GlideImageLoader())
//        -----------------------轮播------------------------

        //-----------------------RecyclerView---------------

        recyclerview.layoutManager = LinearLayoutManager(this)

        recyclerview.addItemDecoration(object : RecyclerView.ItemDecoration() {
            override fun getItemOffsets(outRect: Rect, itemPosition: Int, parent: RecyclerView) {
                super.getItemOffsets(outRect, itemPosition, parent)
                outRect.set(0, 7, 0, 7)
            }
        })

        myAdapter = OriginalRVHomeAdapter(this, items)

        adapter = HomeRVAdapter(R.layout.rv_item_layout, items)
        recyclerview.adapter = myAdapter
        //-----------------------RecyclerView---------------
    }

    override fun onDestroy() {
        super.onDestroy()
        presenter.unsubscribe()
    }
}

1.4、主持层/处理层(MainPresenter 作为主持者,接口是父类 IMainActivityPresenter)

package com.zbv.mvp_android.`interface`.main

import com.zbv.mvp_android.javabean.BannarBean
import com.zbv.mvp_android.javabean.HomeDataFirstBean
import com.zbv.mvp_android.net.ResponseBean
import io.reactivex.ObservableTransformer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers

/**
 * author: qzx
 * Date: 2019/11/21 8:56
 *
 * 和模型层一样的两个方法
 */
class MainPresenter(view: MainActivityView) : IMainActivityPresenter {

    private var view: MainActivityView? = null

    init {
        this.view = view
    }

    private val model: MainActivityModel by lazy {
        MainActivityModel()
    }

    private val disposable: CompositeDisposable by lazy {
        CompositeDisposable()
    }

    /**
     * 切换线程操作
     */
    fun <T> applySchedulers(): ObservableTransformer<T, T> {
        return ObservableTransformer { observable -> observable.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()) }
    }

    /**
     * 获取首页条幅栏
     * */
    override fun getBananarData() {
        var bananarCompose = model?.getBananarData()?.compose(applySchedulers())
                .map(object : Function<ResponseBean<MutableList<BannarBean>>, MutableList<BannarBean>> {
                    override fun apply(t: ResponseBean<MutableList<BannarBean>>?): MutableList<BannarBean> {
                        return t?.data!!
                    }
                })
                .subscribe({ t ->
                    view?.getBananarSuccess(t)
                }, { throwable ->
                    view?.getBananarFail(throwable.message!!)
                })

        disposable.add(bananarCompose)
    }

    /**
     * 获取首页数据
     * */
    override fun getHomeData(pageIndex: Int) {
        var homeCompose = model?.getHomeData(pageIndex)?.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .map(object : Function<ResponseBean<HomeDataFirstBean>, HomeDataFirstBean> {
                    override fun apply(t: ResponseBean<HomeDataFirstBean>?): HomeDataFirstBean {
                        return t?.data!!
                    }
                })
                .subscribe({ t ->
                    view?.getHomeDataSuccess(t.datas!!)
                }, { throwable ->
                    view?.getHomeDataFail(throwable.message!!)
                })

        disposable.add(homeCompose)
    }

    /**
     * 取消订阅
     * */
    override fun unsubscribe() {
        disposable.dispose()
    }

}
package com.zbv.mvp_android.`interface`.main

/**
 * author: qzx
 * Date: 2019/11/22 13:42
 */
interface IMainActivityPresenter {
    fun unsubscribe()

    fun getBananarData()

    fun getHomeData(pageIndex: Int)
}

 

二、MVVM模式(Kotlin编写)

2.1、例子项目目录展示:

2.2、ViewModel层(QueryWeatherViewModel =>Model层这里只是简单的JavaBean,可以抽出一个接口Model来网络请求的)

package com.zbv.androidmvvm.viewmodel

import android.databinding.ObservableBoolean
import android.databinding.ObservableField
import android.util.Log
import com.zbv.androidmvvm.model.WeatherData
import com.zbv.androidmvvm.model.WeatherInfo
import com.zbv.androidmvvm.net.RetrofitUtil
import com.zbv.androidmvvm.net.WeatherRequest
import io.reactivex.ObservableTransformer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

/**
 * author: qzx
 * Date: 2019/11/22 9:07
 */
class QueryWeatherViewModel {

    val loading: ObservableBoolean = ObservableBoolean(false)
    val loadingSuccess: ObservableBoolean = ObservableBoolean(false)
    val loadingFailure: ObservableBoolean = ObservableBoolean(false)

    val city: ObservableField<String> = ObservableField()
    val cityId: ObservableField<String> = ObservableField()
    val temp1: ObservableField<String> = ObservableField()
    val temp2: ObservableField<String> = ObservableField()
    val weather: ObservableField<String> = ObservableField()
    val time: ObservableField<String> = ObservableField()

    private var call: Call<WeatherData>? = null

    //rxjava 管理生命周期
    private val disposable: CompositeDisposable by lazy {
        CompositeDisposable()
    }


    fun queryWeather() {
        loading.set(true)
        loadingSuccess.set(false)
        loadingFailure.set(false)

//        //方式一
//        call = RetrofitUtil.getInstance().retrofit?.create(WeatherRequest::class.java)?.queryWeather()
//        //方式二
//        call = RetrofitUtil.getInstance().getRequest(WeatherRequest::class.java)?.queryWeather()
//
//        call?.enqueue(object : Callback<WeatherData> {
//            override fun onFailure(call: Call<WeatherData>, t: Throwable) {
//                if (call.isCanceled) {
//                    Log.e("zbv", "user canceled request")
//                } else {
//                    loading.set(false)
//                    loadingFailure.set(true)
//                }
//            }
//
//            override fun onResponse(call: Call<WeatherData>, response: Response<WeatherData>) {
//                val weatherInfo: WeatherInfo? = response.body()?.weatherinfo
//
//                city.set(weatherInfo?.city)
//                cityId.set(weatherInfo?.cityid)
//                temp1.set(weatherInfo?.temp1)
//                temp2.set(weatherInfo?.temp2)
//                weather.set(weatherInfo?.weather)
//                time.set(weatherInfo?.ptime)
//
//                loading.set(false)
//                loadingSuccess.set(true)
//            }
//        })


        //方式三 rxjava
        var compose = RetrofitUtil.getInstance().getRequest(WeatherRequest::class.java)?.queryWeather2()
                ?.compose(applySchedulers())
                ?.map(object : Function<WeatherData, WeatherInfo> {
                    override fun apply(t: WeatherData?): WeatherInfo? {
                        return t?.weatherinfo
                    }
                })
                ?.subscribe({ t ->
                    city.set(t.city)
                    cityId.set(t.cityid)
                    temp1.set(t.temp1)
                    temp2.set(t.temp2)
                    weather.set(t.weather)
                    time.set(t.ptime)

                    loading.set(false)
                    loadingSuccess.set(true)
                }, { throwable ->
                    loading.set(false)
                    loadingFailure.set(true)
                })
        disposable.add(compose)

    }

    /**
     * 取消请求,或者在生命周期结束时调用,结束进行中的请求操作等
     * */
    fun cancelRequest() {
        call?.cancel()

        if (!disposable.isDisposed) {
            disposable.dispose()
        }
    }

    /**
     * 切换线程操作
     */
    fun <T> applySchedulers(): ObservableTransformer<T, T> {
        return ObservableTransformer { observable -> observable.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()) }
    }
}

2.3、视图层(这里的视图层主要是DataBinding的Layout和Activity)

package com.zbv.androidmvvm

import android.databinding.DataBindingUtil
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.zbv.androidmvvm.databinding.ActivityMainBinding
import com.zbv.androidmvvm.viewmodel.QueryWeatherViewModel

class MainActivity : AppCompatActivity() {

    private var viewmodel: QueryWeatherViewModel? = null

    private var mDataBinding: ActivityMainBinding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //创建DataBinding
        mDataBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        //创建ViewModel
        viewmodel = QueryWeatherViewModel()
        //绑定ViewModel
        mDataBinding?.viewModel = viewmodel
    }

    override fun onDestroy() {
        super.onDestroy()
        viewmodel?.cancelRequest()
    }
}

附上绑定的Layout:

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

    <data>

        <import type="android.view.View" />

        <variable
            name="viewModel"
            type="com.zbv.androidmvvm.viewmodel.QueryWeatherViewModel" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="@dimen/default_content_padding">

        <Button
            android:id="@+id/btn_query_weather"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:enabled="@{viewModel.loading ? false : true}"
            android:onClick="@{() -> viewModel.queryWeather()}"
            android:text="@string/query_weather" />

        <RelativeLayout
            android:id="@+id/vg_weather_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/btn_query_weather"
            android:layout_marginTop="@dimen/query_weather_margin"
            android:visibility="@{viewModel.loadingSuccess ? View.VISIBLE : View.GONE}">

            <TextView
                android:id="@+id/tv_city"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/city"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_city_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_city"
                android:layout_toRightOf="@id/tv_city"
                android:text="@{viewModel.city}"
                tools:text="杭州" />

            <TextView
                android:id="@+id/tv_city_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_city"
                android:layout_marginTop="@dimen/query_weather_margin"
                android:text="@string/city_id"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_city_id_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_city_id"
                android:layout_toRightOf="@id/tv_city_id"
                android:text="@{viewModel.cityId}"
                tools:text="101210101" />

            <TextView
                android:id="@+id/tv_temp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_city_id"
                android:layout_marginTop="@dimen/query_weather_margin"
                android:text="@string/temperature"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_temp1_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_temp"
                android:layout_toRightOf="@id/tv_temp"
                android:text="@{viewModel.temp1}"
                tools:text="5℃" />

            <TextView
                android:id="@+id/tv_tilde"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_temp"
                android:layout_toRightOf="@id/tv_temp1_value"
                android:text="@string/tilde" />

            <TextView
                android:id="@+id/tv_temp2_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_temp"
                android:layout_toRightOf="@id/tv_tilde"
                android:text="@{viewModel.temp2}"
                tools:text="10℃" />

            <TextView
                android:id="@+id/tv_weather"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_temp"
                android:layout_marginTop="@dimen/query_weather_margin"
                android:text="@string/weather"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_weather_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_weather"
                android:layout_toRightOf="@id/tv_weather"
                android:text="@{viewModel.weather}"
                tools:text="晴" />

            <TextView
                android:id="@+id/tv_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_weather"
                android:layout_marginTop="@dimen/query_weather_margin"
                android:text="@string/release_time"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_time_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/tv_time"
                android:layout_toRightOf="@id/tv_time"
                android:text="@{viewModel.time}"
                tools:text="10:00" />
        </RelativeLayout>

        <ProgressBar
            android:id="@+id/pb_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}" />

        <TextView
            android:id="@+id/tv_query_failure"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/query_failure"
            android:visibility="@{viewModel.loadingFailure ? View.VISIBLE : View.GONE}" />
    </RelativeLayout>
</layout>

重要一点是Kotlin需要引入(app中的build.gradle):

apply plugin: 'kotlin-kapt'

 

三、例子项目下载(推荐百度云下载,免费)

CSDN下载:MVP例子项目、 MVVM例子项目

百度云免费下载:

MVVM例子项目  链接:https://pan.baidu.com/s/1IAlfZnZNyo6JNEP7xgTZFQ    提取码:6awq

MVP例子项目 链接:https://pan.baidu.com/s/14bpT3xPCDN0dd7MeJi_wTA  提取码:g2xl 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值