成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

ZweiZhao / 592人閱讀

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

前言

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

一點(diǎn)點(diǎn)入坑JetPack:ViewModel篇

一點(diǎn)點(diǎn)入坑JetPack:Lifecycle篇

一點(diǎn)點(diǎn)入坑JetPack:LiveData篇

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

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

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

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

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

是不是很簡(jiǎn)單的需求,很多小伙伴可能順手就能寫出來(lái):

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

非常直觀且易閱讀。我們?cè)谏钊胂胍幌?,如果其他?yè)面也有這樣的需求,是不是也要寫一份這個(gè)內(nèi)容?

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

二、走進(jìn)MVVM 2.1、走進(jìn)MVVM流程圖

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

2.2、走進(jìn)MVVM代碼

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

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

那么這個(gè)Repository是什么樣子的呢?

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

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

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

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

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

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

接下來(lái)咱們挨個(gè)展開上述代碼中用到的類,MusicDao一個(gè)負(fù)責(zé)我們的Cache的實(shí)現(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
    }
}

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

接下來(lái),咱們來(lái)看看網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)類:MusicApiService

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

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

MusicApiService
object MusicApiService {
    
    override fun querySongs(parameter : Parameter): LiveData> {
        val liveData = MutableLiveData>()
        CoroutineScope(FastMain).launch {
            val resp = resp = withContext(BuzzApiPool) {
            // 這里對(duì)應(yīng)的是業(yè)務(wù)方自己的網(wǎng)絡(luò)實(shí)現(xiàn)封裝
    		val np = NetWorkManager.getInstance().networkProvider
   		    val builder = Uri.parse("服務(wù)端的請(qǐng)求接口")
       			 .buildUpon()
       		builder.appendQueryParameter("category_id", parameter.categoryId)
    		try {
    		    // 自己封裝的get請(qǐng)求
        		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這樣就夠了,接下來(lái)就是我們的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)
		}
		// 通過(guò)LiveData通知MusicRepository進(jìn)行網(wǎng)絡(luò)請(qǐng)求
		musicViewModel.parameter.value=Parameter(categoryId = xx) //本次請(qǐng)求的參數(shù)
	}
}

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

對(duì)于UI層來(lái)說(shuō):

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

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

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

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

這樣你會(huì)發(fā)現(xiàn),對(duì)于Activity/Fragment來(lái)說(shuō),它就只是View層了,一點(diǎn)邏輯操作都沒有。

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

2.3、存在問(wèn)題

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

1、性能問(wèn)題

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

沒錯(cuò)!這是一個(gè)嚴(yán)重的問(wèn)題,但實(shí)際上Google早在很久之前就提供了一個(gè)類DiffUtil,這個(gè)類可以說(shuō)完美的幫我們?cè)谶@套設(shè)計(jì)里,搞定了RecycleView空刷的性能消耗。

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

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

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

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

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

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

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

尾聲

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7372.html

相關(guān)文章

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

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

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

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

    cuieney 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<