在Android Architecture Components(AAC)中ViewMode是爲界
置更改後繼續存在的對象。例如界面的旋轉致使界面配置信息改變。java
對於爲界面提供數據,咱們所知道的也有其餘的一些模式,例如MVP的Presenter與MVVM中的ViewModel。那麼咱們進行一個假設,若是Activity發生界面旋轉,此時上述的提供數據的模式會發生什麼呢?android
對於以上問題,ViewModel都可以幫咱們解決。只要Activity沒有完全被銷燬,使用的都是同一個ViewModel,同時對於它的建立與銷燬咱們無需進行維護管理,能很好的保證資源的釋放。git
下面的這張圖可以幫助咱們更好的觀察它在Activity中的生命狀態github
ViewModel貫穿Activity的整個生命週期,只有當Activity完全釋放時纔會將其銷燬。因此它可以更好的幫助咱們實現持久化數據,防止沒必要要的數據請求,提升App的性能。數據庫
是否是有點好奇了呢,下面咱們來簡單介紹它的使用,爲何說簡單呢?由於真的很簡單...
若是你已經有了解過上篇關於Lifecycle的文章(Android Architecture Components Part3:Lifecycle),那麼能夠直接跳過依賴部分,沒有的咱們繼續。segmentfault
在使用ViewModel以前,咱們須要在App或者Module的build.gradle中添加以下代碼api
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: MutableLiveData<String> by lazy { MutableLiveData<String>() } val contactsList: MutableLiveData<List<ContactsModel>> = MutableLiveData() .... .... fun getContacts(refresh: Boolean): LiveData<List<ContactsModel>> { 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來更好的管理數據的變化更新。app
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來解決。
咱們回到獲取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 public <T extends ViewModel> T create(@NonNull Class<T> 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 fun <T : ViewModel?> create(modelClass: Class<T>): 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