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 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章