摘要:在中是為界置更改后繼續存在的對象。由于的特性是對數據進行持久化,所以它不能持有與相關的引用,防止內存泄露,因此這里使用與應用生命周期綁定的。創建好了,接下來只剩下在中進行使用。得到后,剩下的就是對數據的操作與響應。
在Android Architecture Components(AAC)中ViewMode是為界
置更改后繼續存在的對象。例如界面的旋轉導致界面配置信息改變。
對于為界面提供數據,我們所知道的也有其他的一些模式,例如MVP的Presenter與MVVM中的ViewModel。那么我們進行一個假設,如果Activity發生界面旋轉,此時上述的提供數據的模式會發生什么呢?
對于Activity的重建,為了提供ui所需的數據,我們必須重新獲取數據(網絡或者本地數據庫),如果需要保存數據,也要重新進行保存操作。
在對數據進行操作時,你必須要處理一些可能造成的內存泄露問題。
對于以上問題,ViewModel都能夠幫我們解決。只要Activity沒有徹底被銷毀,使用的都是同一個ViewModel,同時對于它的創建與銷毀我們無需進行維護管理,能很好的保證資源的釋放。
下面的這張圖能夠幫助我們更好的觀察它在Activity中的生命狀態
ViewModel貫穿Activity的整個生命周期,只有當Activity徹底釋放時才會將其銷毀。所以它能夠更好的幫助我們實現持久化數據,防止不必要的數據請求,提高App的性能。
是不是有點好奇了呢,下面我們來簡單介紹它的使用,為什么說簡單呢?因為真的很簡單...依賴
如果你已經有了解過上篇關于Lifecycle的文章(Android Architecture Components Part3:Lifecycle),那么可以直接跳過依賴部分,沒有的我們繼續。
在使用ViewModel之前,我們需要在App或者Module的build.gradle中添加如下代碼
dependencies { def lifecycle_version = "1.1.1" // ViewModel and LiveData implementation "android.arch.lifecycle:extensions:$lifecycle_version" annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" }使用
依賴已經準備完畢,我們可以直接通過如下代碼使用
class ContactsViewModel(application: Application, private val defTitle: String = "Contacts") : AndroidViewModel(application) { val message: MutableLiveDataby lazy { MutableLiveData () } val contactsList: MutableLiveData > = MutableLiveData() .... .... fun getContacts(refresh: Boolean): LiveData
> { message.value = "" if (refresh) { getDataFromRemote() } else if (contactsList.value == null || contactsList.value?.size ?: 0 <= 0) { message.value = "數據請求中,請稍后!" if (mLocalData.isEmpty()) { getDataFromLocal() } } return contactsList } ... ... }
我們創建ContactsViewModel,讓它繼承于AndroidViewModel,它是對抽象ViewModel的擴展,使用它時需要傳入Application對象,方便一些資源的獲取。由于ViewModel的特性是對數據進行持久化,所以它不能持有與Activity相關的引用(Context),防止內存泄露,因此這里使用與應用生命周期綁定的Application。
在ContactsViewModel中我們結合MutableLiveData來更好的管理數據的變化更新。
ViewModel創建好了,接下來只剩下在Activity中進行使用。
class ContactsActivity : AppCompatActivity() { private lateinit var mViewModel: ContactsViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_contacts_layout) setupViewModel() } private fun setupViewModel() { mViewModel = ViewModelProviders.of(this)[ContactsViewModel::class.java] //active STARTED、RESUMED mViewModel.getContacts(true).observe(this, Observer { //todo ... }) mViewModel.message.observe(this, Observer { //todo ... }) } }
實際上我們只需一行代碼就可以獲取到ViewModel的實例,通過ViewModelProviders.of()方法傳入Activity對象,它會返回一個ViewModelProvider對象,然后我們再使用它的get()來根據不同的ViewModel的Class對象來獲取到相應的ViewModel實例。
只要Activity對象沒有改變,同時都是同一個ViewModel的Class對象,那么我們無論何時獲取的都是同一個ViewModel實例。這就實現了在Activity中的ViewModel持久化特性。由于ViewModel是同一個,自然它里面的數據也是同一份。
得到ViewModel后,剩下的就是對數據的操作與響應。這里結合了LiveData所以方便了許多。
LiveData之間已經有詳細介紹,如需了解可以查看文章末的鏈接。ViewModelProvider
到這里我想你心中可能會有如下幾個疑問
ViewModel它是如何初始化的,對象是如何實例化的
如何向ViewModel中傳遞初始化的參數
這兩個疑問都將由ViewModelProvider來解決。
我們回到獲取ViewModelProvider的ViewModelProviders.of()方法,進入源碼查看。
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(ViewModelStores.of(activity), factory); }
我們也沒有發現我們想要的代碼,但我們發現factory。我們在獲取ViewModel時并沒有傳入factory,所以它會走空判斷里面的代碼,創建一個默認的factory。那么我們再進入ViewModelProvider中查看靜態內部類AndroidViewModelFactory
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { ... ... @NonNull @Override publicT create(@NonNull Class modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InvocationTargetException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } return super.create(modelClass); } }
我們只看關鍵代碼,它繼承于NewInstanceFactory,其中只有一個方法create()。AndroidViewModelFactory重新實現了create(),在重寫的方法中我們找到了我們想要的方法調用。在這里它通過Class的getConstructor()方法獲取匹配的Constructor對象,然后再通過newInstance()方法來獲取匹配的對象實例。這樣我們所需要的ViewModel實例就創建了,第一個疑問就此解決。
至于第二個疑問,細心的話不難發現,上面在調用newInstance()方法時已經傳了一個初始化的參數mApplication。所以如果我們要再傳入其它自定義的初始化參數,只需實現我們自己的create()方法。要自定義create()方法,我們就要自定義一個factory,繼承NewInstanceFactory類。
下面是實現ContactsViewModel在初始化時傳入特定的title值
class ContactsFactory(private val application: Application) : ViewModelProvider.NewInstanceFactory() { companion object { @SuppressLint("StaticFieldLeak") private var instance: ContactsFactory? = null fun getInstance(application: Application): ContactsFactory { if (instance == null) { instance = ContactsFactory(application) } return instance as ContactsFactory } } override funcreate(modelClass: Class ): T { if (modelClass.isAssignableFrom(ContactsViewModel::class.java)) { return ContactsViewModel(application, "Factory Contacts") as T } return super.create(modelClass) } }
重點自然是create()方法,通過isAssignableFrom()方法判斷需要實例化的類型,由于我們能夠明確到具體的類,所以可以直接使用正常的類實例化操作。已經有了factory,最后在獲取ViewModel時傳入即可
mViewModel = ViewModelProviders.of(this, ContactsFactory.getInstance(application))[ContactsViewModel::class.java]
ViewModel就是這么簡單,但它對數據的持久化卻異常突出。是否已經迫不及待了呢?趕緊來試試它的特性吧。
總結最后Android Architecture Components(AAC)系列到此就告一段落了,讓我們來回顧一下AAC。我們通過Room可以快速方便的實現本地數據存儲;結合LiveData來觀測數據的更新變化與及時反映到UI層;同時使用Lifecycle來讓我們的組件或數據容器的具備生命感知能力,幫助我們的減少生命狀態的處理與異常錯誤的發生;最后將界面數據存儲到ViewModel中,使得數據達到持久化,減少不必要的數據請求與資源消耗。
下面的能夠初步體現使用AAC后的App項目架構形態
最后感謝大家對AAC架構系列的支持!如果感覺不錯的話,可以幫忙點贊收藏一下或者關注我的公眾號。同時文章中的代碼都可以在Github中獲取到。使用時請將分支切換到feat_architecture_components
相關文章Android Architecture Components Part1:Room
Android Architecture Components Part2:LiveData
Android Architecture Components Part3:Lifecycle
私人博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/11893.html
摘要:例如,在方面它主要能夠幫助你解決以下兩個問題在主線程中執行耗時任務導致的主線程阻塞,從而使發生。提供主線程安全,同時對來自于主線程的網絡回調磁盤操提供保障。在線程通過從數據庫取數據,一旦數據返回,在主線程進行處理。 showImg(https://segmentfault.com/img/bVbuqpM?w=800&h=320); 今天我們來聊聊Kotlin Coroutine,如果你...
閱讀 2290·2023-04-26 00:01
閱讀 796·2021-10-27 14:13
閱讀 1810·2021-09-02 15:11
閱讀 3381·2019-08-29 12:52
閱讀 528·2019-08-26 12:00
閱讀 2568·2019-08-26 10:57
閱讀 3405·2019-08-26 10:32
閱讀 2848·2019-08-23 18:29