成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

一點點入坑JetPack(終章):實戰(zhàn)MVVM

ZweiZhao / 710人閱讀

摘要:一點點入坑篇一點點入坑篇一點點入坑篇一點點入坑實戰(zhàn)前戲篇一點點入坑終章實戰(zhàn)相信有耐心看到這的小伙伴,完全足以通過偽代碼,感受出來以下代碼的設(shè)計思路。而這一切的實現(xiàn)并不會對外邊的邏輯產(chǎn)生影響,做到了實現(xiàn)的隔離。此外層在監(jiān)聽的結(jié)果,更新即可。

前言

這次的實戰(zhàn)篇,是這個系列的最后一篇。本文綜合前幾篇的內(nèi)容,以偽代碼為主,幫大家理解Google所推崇的MVVM。

一點點入坑JetPack:ViewModel篇

一點點入坑JetPack:Lifecycle篇

一點點入坑JetPack:LiveData篇

一點點入坑JetPack:實戰(zhàn)前戲NetworkBoundResource篇

一點點入坑JetPack(終章):實戰(zhàn)MVVM

相信有耐心看到這的小伙伴,完全足以通過偽代碼,感受出來以下代碼的設(shè)計思路。Go~

正文 一、日常業(yè)務(wù)

上代碼之前,我們思考一個小問題。我們平時的業(yè)務(wù),很重的一個部分是從一個地方獲取數(shù)據(jù),然后在UI上展示出來。因此,本節(jié)實戰(zhàn)部分的背景:從網(wǎng)絡(luò)獲取一批數(shù)據(jù),如果網(wǎng)絡(luò)請求成功,便更新到RecycleView上;如果網(wǎng)絡(luò)請求不成功,加載本地已有緩存,然后更新到RecycleView上。

是不是很簡單的需求,很多小伙伴可能順手就能寫出來:

// 網(wǎng)絡(luò)請求
loadNetwork(參數(shù), Callback(){
    // 請求成功,更新UI
	success(data){
		recyclerview.setData
	}
	// 請求失敗,讀取緩存
	error(){
		loadDB(參數(shù),Callback(){
		    // 緩存讀取成功,更新UI
			success(data){
				recyclerview.setData
			}
		})
	}
})

非常直觀且易閱讀。我們在深入想一下,如果其他頁面也有這樣的需求,是不是也要寫一份這個內(nèi)容?

這里肯定有小伙伴會指出,應(yīng)該進行封裝!沒錯,還記得上一篇文章提到的NetworkBoundResource嗎?接下來,就讓我們通過NetworkBoundResource,使用MVVM的思想去封裝這個業(yè)務(wù)。

二、走進MVVM 2.1、走進MVVM流程圖

針對MVVM官方提供的一張比較清晰的流程圖:

2.2、走進MVVM代碼

按照官方的推薦,我們需要一個Repository作為整個數(shù)據(jù)層的管理者。

例如,我們設(shè)計一個加載歌曲信息,然后更新到RecycleView上的需求。這個Repository咱們就叫,MusicRepository,表示音樂相關(guān)的數(shù)據(jù)獲取交由這個類去管理。

那么這個Repository是什么樣子的呢?

1、Repository MusicRepository
// 這里的三個參數(shù),分別是:線程池,緩存模塊,網(wǎng)絡(luò)模塊
class MusicRepository(
    val appExecutors: AppExecutors,
    val musicDao: MusicDao, // 后文會展開這個類
    val service: MusicApiService // 后文會展開這個類(具體的請求模塊)
) {

    companion object {
        val inst: MusicRepository by lazy {
	        // 這里傳入的內(nèi)容,當然是業(yè)務(wù)方自己去實現(xiàn),比如這前業(yè)務(wù)已經(jīng)存在的DB/內(nèi)存緩存模塊;封裝好的網(wǎng)絡(luò)請求模塊,比如OkHttp/Retrofit等等
            MusicRepository(xxx,xxx,xxx)
        }
    }
   
    // Parameter會在后續(xù)中展開
    fun querySongs(parameter : Parameter): LiveData> {
        return object :
            NetworkBoundResource(
                appExecutors
            ) {

            override fun saveCallResult(item: MusicResp) {
                // 網(wǎng)絡(luò)請求成功,先存入緩存模塊
                musicDao.saveDB(item)
            }

            override fun shouldFetch(data: MusicResp");: Boolean {
                return 自己的是否請求網(wǎng)絡(luò)策略
            }

            override fun loadFromDb(): LiveData {
                return musicDao.getCacheMusicResp(parameter.categoryId)
            }

            override fun createCall(): LiveData> {
	            // 調(diào)用網(wǎng)絡(luò)模塊的請求實現(xiàn)
                return service.querySongs(parameter)
            }
        }.asLiveData()
    }
}

接下來咱們挨個展開上述代碼中用到的類,MusicDao一個負責我們的Cache的實現(xiàn)類:

MusicDao
object MusicDao {
    private val musicStoreSongs: MutableMap<Long, MusicResp> by lazy {
        mutableMapOf<Long, MusicResp>()
    } 

    fun updateSongsCache(categoryId: Long, data: MusicResp) {
        musicStoreSongs[categoryId] = data
    }

    fun querySongsCache(categoryId: Long): LiveData {
        val cacheSongLiveData = MutableLiveData()
        cacheSongLiveData.value = musicStoreSongs[categoryId]
        return cacheSongLiveData
    }
}

這里僅僅是實現(xiàn)了一套內(nèi)存緩存?;诖宋覀冞€可以實現(xiàn)自己的數(shù)據(jù)庫緩存,或者內(nèi)存+數(shù)據(jù)庫的二級緩存。而這一切的實現(xiàn)并不會對外邊的邏輯產(chǎn)生影響,做到了實現(xiàn)的隔離。

接下來,咱們來看看網(wǎng)絡(luò)請求的實現(xiàn)類:MusicApiService

這里涉及了協(xié)程的內(nèi)容,建議沒有相關(guān)基礎(chǔ)的小伙伴,可以看一看我之前寫過的文章。

總是在聊線程Thread,試試協(xié)程吧!

MusicApiService
object MusicApiService {
    
    override fun querySongs(parameter : Parameter): LiveData> {
        val liveData = MutableLiveData>()
        CoroutineScope(FastMain).launch {
            val resp = resp = withContext(BuzzApiPool) {
            // 這里對應(yīng)的是業(yè)務(wù)方自己的網(wǎng)絡(luò)實現(xiàn)封裝
    		val np = NetWorkManager.getInstance().networkProvider
   		    val builder = Uri.parse("服務(wù)端的請求接口")
       			 .buildUpon()
       		builder.appendQueryParameter("category_id", parameter.categoryId)
    		try {
    		    // 自己封裝的get請求
        		val json = np.networkClient.get(builder.toString())
        		// 這里封裝的是Gson把String轉(zhuǎn)成JavaBean的方法
       			val data: MusicResp = fromServerResp(json)
        		data
    		} catch (e: Exception) {
        		MusicResp(e)
    		}
    		if (resp.isSuccess)) {
                liveData.postValue(ApiSuccessResponse(resp))
            } else {
                liveData.postValue(
                    ApiErrorResponse(resp.exception ");"unknown_error"))
                )
            }
	    }
        return liveData
    }
}

有了Repository之后,我們則需要考慮一下ViewModel了。就叫MusicViewModel

2、ViewModel
class MusicViewModel :ViewModel(){
    // Parameter 偽碼
	var parameter = MutableLiveData()
	val data : LiveData> = Transformations.switchMap(parameter) { parameter->
        MusicRepository.inst.querySongs(parameter)
    }
}
3、Activity/Fragment

ViewModel這樣就夠了,接下來就是我們的UI,這里就叫MusicActivity吧。

class MusicActivity : AppCompatActivity(){
    private lateinit var musicViewModel: MusicViewModel
    
	override fun onCreate(savedInstanceState: Bundle"); {
		setContentView(R.layout.xxx)
		musicViewModel = ViewModelProviders.of(this).get(MusicViewModel::class.java)
		
		musicViewModel.data.observe(this, Observer { musicResp->
			// 這里監(jiān)聽的數(shù)據(jù)就是MusicRepository返回的MusicResp
			adapter.setData(musicResp)
		}
		// 通過LiveData通知MusicRepository進行網(wǎng)絡(luò)請求
		musicViewModel.parameter.value=Parameter(categoryId = xx) //本次請求的參數(shù)
	}
}

到這里,我們最基本的使用,就完成了。

對于UI層來說:

它只需要在自己需要請求數(shù)據(jù)的時候通過MusicViewModel給“parameter”這個LiveData賦一個真正的請求參數(shù)就可以了。

Transformations.switchMap(參數(shù))會收到變換然后執(zhí)行MusicRepository.inst.querySongs(請求參數(shù)),之后的所有邏輯全部交由MusicRepository去處理。

至于怎么加載網(wǎng)絡(luò),怎么處理緩存,都是有各個獨立的模塊實現(xiàn)的。

此外UI層在監(jiān)聽musicViewModel.data的結(jié)果,更新UI即可。

這樣你會發(fā)現(xiàn),對于Activity/Fragment來說,它就只是View層了,一點邏輯操作都沒有。

當然這是理想狀態(tài),畢竟PM擁有無窮的想象力,什么樣的需求都會存在。

2.3、存在問題

我猜理解清楚這套設(shè)計的小伙伴,一定會之處問題所在。那就是:

1、性能問題

adapter.setData(musicResp),這就意味著,每次數(shù)據(jù)回調(diào)回來RecycleView都會更新,這樣就產(chǎn)生了很多無用的刷新。 而且這里是監(jiān)聽這個數(shù)據(jù)對象,如果想進行局部刷新,那么Activity/Fragment中勢必要做很多額外的邏輯操作...

沒錯!這是一個嚴重的問題,但實際上Google早在很久之前就提供了一個類DiffUtil,這個類可以說完美的幫我們在這套設(shè)計里,搞定了RecycleView空刷的性能消耗。

如果有必要,下篇文章可以聊一聊DiffUtil和Immutable、Mutable的理念

2、額外的業(yè)務(wù)邏輯

畢竟有些時候,我們沒辦法這么直來直去的加載數(shù)據(jù)。更多的時候,我們需要在業(yè)務(wù)回來時進行一系列的額外代碼:比如數(shù)據(jù)的變換、邏輯的判斷...

數(shù)據(jù)變換:這類操作,可以使用函數(shù)式編程的思想,很方便的在ViewModel中完成并通過LiveData通知給observe方。

邏輯的判斷:這部分內(nèi)容,并不屬于MVVM(數(shù)據(jù)驅(qū)動)的部分。所以至于它還需要仁者見仁智者見智的封裝...

想了很久,還是覺得在此就停下實戰(zhàn)篇的內(nèi)容。因為我以為這已經(jīng)夠了,如果能消化這整個系列的內(nèi)容,我相信該怎么使用JetPack,小伙伴們心中已經(jīng)有了自己的想法~

當然,小伙伴們?nèi)绻惺裁锤}的操作,歡迎留言交流呦~

尾聲

JetPack系列的文章,到此便告一段落了。不知道一路追過來的朋友們是否有收獲。

下一個長篇系列會是什么內(nèi)容,暫時還沒有想好。大家有啥感興趣的,可以留言給點建議~

我是一個應(yīng)屆生,最近和朋友們維護了一個公眾號,內(nèi)容是我們在從應(yīng)屆生過渡到開發(fā)這一路所踩過的坑,以及我們一步步學習的記錄,如果感興趣的朋友可以關(guān)注一下,一同加油~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/7372.html

相關(guān)文章

  • 新風向!成就了Android,熱門框架排第一,你還是不夠了解它!

    摘要:由于長期苦惱于第三方庫選擇的廣大開發(fā)者而言,這也是谷歌為我們提供的一盞明燈。手機淘寶構(gòu)架演化實踐淘寶相信都不陌生了從年開始,從萬增長到超過億,面臨的問題包括研發(fā)支撐所需要解決的事情各不相同。 ...

    sixgo 評論0 收藏0
  • 史上最優(yōu)雅的在VM層取消Coroutine的方式

    摘要:問題為了防止銷毀時異步任務(wù)仍然在進行所導致的內(nèi)存泄露,我們都會在方法中去取消異步任務(wù)??偨Y(jié)層可以天然自動監(jiān)視銷毀,我一直在找尋如何優(yōu)雅的自動取消異步任務(wù),在目前來看是最佳的方案。協(xié)程絕對是最先進的,效率最高,最優(yōu)雅的技術(shù)棧組合。前提 在Android MVVM模式,我使用了Jetpack包中的ViewModel來實現(xiàn)業(yè)務(wù)層,當然你也可以使用DataBinding,關(guān)于Android業(yè)務(wù)層架構(gòu)...

    cuieney 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<