亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android開(kāi)發(fā)Jetpack組件ViewModel使用講解

 更新時(shí)間:2023年05月12日 08:39:40   作者:Android技術(shù)棧  
這篇文章主要介紹了Android?Jetpack架構(gòu)組件?ViewModel詳解,ViewModel類(lèi)讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在,ViewModel類(lèi)旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù),感興趣可以來(lái)學(xué)習(xí)一下

前言

學(xué)習(xí)ViewModel之前首先我們得簡(jiǎn)單了解下MVP和MVVM,因?yàn)閂iewModel是MVVM中的一個(gè)元素

MVP MVVM

在MVP中View想要調(diào)用Model數(shù)據(jù)層,需要經(jīng)過(guò)中間層Presenter, 這樣就實(shí)現(xiàn)了View和Model的解耦,這也是MVP和MVC的差別; 但是如果一個(gè)Activity中有太多交互,那么我們的View接口數(shù)量就會(huì)很龐大達(dá)到十幾個(gè)也不足為奇,并且在View層調(diào)用了Presenter之后,會(huì)反過(guò)來(lái)調(diào)用View層,這樣顯得繁瑣;而MVVM的出現(xiàn)就解決了這個(gè)問(wèn)題

說(shuō)到MVVM的話,我們放上Google的架構(gòu)圖

MVVM中的VM指的就是ViewModel; 從上圖為沒(méi)看到,因?yàn)閂iewModel中持有了LiveData,而LiveData是一個(gè)可觀察的數(shù)據(jù)類(lèi)型,在LiveData原理篇中,我們做了詳細(xì)的分析;在View層中,將被觀察的數(shù)據(jù)LiveData訂閱,并提供了一個(gè)觀察者Observer,當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候,就會(huì)回調(diào)Observer中的onChanged()方法,從而更新UI, 這個(gè)過(guò)程是系統(tǒng)源碼幫我們處理的,所以就沒(méi)有上面Presenter中調(diào)用View的那一步了

ViewModel概述

應(yīng)用的某個(gè) Activity 中可能包含用戶(hù)列表,因配置更改而重新創(chuàng)建 Activity 后,新 Activity 必須重新提取用戶(hù)列表; 對(duì)于簡(jiǎn)單的數(shù)據(jù),Activity 可以使用onSaveInstanceState() 方法從 onCreate() 中的捆綁包恢復(fù)其數(shù)據(jù),但此方法僅適合可以序列化再反序列化的少量數(shù)據(jù),而不適合數(shù)量可能較大的數(shù)據(jù),如用戶(hù)列表或位圖,使用ViewModel可以解決這個(gè)問(wèn)題

另外,界面控制器經(jīng)常需要進(jìn)行異步調(diào)用,這些調(diào)用可能需要一些時(shí)間才能返回結(jié)果; 界面控制器需要管理這些調(diào)用,并確保系統(tǒng)在其銷(xiāo)毀后清理這些調(diào)用以避免潛在的內(nèi)存泄露;此項(xiàng)管理需要大量的維護(hù)工作,并且在因配置更改而重新創(chuàng)建對(duì)象的情況下,會(huì)造成資源的浪費(fèi),因?yàn)閷?duì)象可能需要重新發(fā)出已經(jīng)發(fā)出過(guò)的調(diào)用,使用ViewModel可以解決這個(gè)問(wèn)題

諸如 Activity 和 Fragment 之類(lèi)的界面控制器主要用于顯示界面數(shù)據(jù)、對(duì)用戶(hù)操作做出響應(yīng)或處理操作系統(tǒng)通信(如權(quán)限請(qǐng)求); 如果要求界面控制器也負(fù)責(zé)從數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)加載數(shù)據(jù),那么會(huì)使類(lèi)越發(fā)膨脹。為界面控制器分配過(guò)多的責(zé)任可能會(huì)導(dǎo)致單個(gè)類(lèi)嘗試自己處理應(yīng)用的所有工作,而不是將工作委托給其他類(lèi);以這種方式為界面控制器分配過(guò)多的責(zé)任也會(huì)大大增加測(cè)試的難度

ViewModel 類(lèi)旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。ViewModel 類(lèi)讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在

ViewModel使用

ViewModel的使用比較簡(jiǎn)單,我們想要使用使用的話直接繼承ViewModel或者繼承AndroidViewModel即可; AndroidViewModel源碼如下,他們倆的區(qū)別,是AndroidViewModel中多了一個(gè)Application的成員變量以及以Application為參數(shù)的構(gòu)造方法,如果你需要Application的話,就直接繼承AndroidViewModel即可

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;
    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }
    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

這里以我寫(xiě)的一個(gè)Demo為例,這個(gè)Demo可以在Github上找到,鏈接如下JetPack Demo,這個(gè)Demo用到了所有Jetpack的組件,是學(xué)習(xí)Jetpack的輔助資料,需要的小伙伴可以下載和star。我們自定義一個(gè)AppsViewModel如下:

class AppsViewModel(appsRepository: AppsRepository) : ViewModel() {
 val apps: LiveData<List<AppEntity>> = appsRepository.loadApps()
}

因?yàn)槲覀冞@里傳入了一個(gè)參數(shù),所以需要定義一個(gè)Factory,代碼如下:

	class AppsViewModelFactory(private val repository: AppsRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return AppsViewModel(repository) as T
    }
}

接下來(lái)就是使用了:

class AppsListFragment : Fragment() {
    private lateinit var viewModel: AppsViewModel
    //-----1-----
    private val viewModel: AppsViewModel by viewModels {
    	FactoryProvider.providerAppsFactory(requireContext())
	}
	override fun onActivityCreated(savedInstanceState: Bundle?) {
		  //-----2-----
  		  viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext()))
		        .get(AppsViewModel::class.java)
		  //-----3-----
		  viewModel.apps.observe(viewLifecycleOwner, Observer {
		  		//Update UI
		  })
	}
}

我們先聲明了一個(gè)變量viewModel,我們可以通過(guò)注釋1處的 by viewModels提供一個(gè)自定義的Factory,但是需要添加一個(gè)依賴(lài):implementation "androidx.fragment:fragment-ktx:1.2.2;或者采用注釋2的方式直接使用ViewModelProviders.of(fragment,factory).get(class)的形式獲取實(shí)例。 然后就直接使用了,在注釋3處使用viewmodel.apps.observe將其加入生命周期觀察中,如果數(shù)據(jù)發(fā)生變化就會(huì)調(diào)用Observer的回調(diào),從而更新UI

ViewModel源碼

上面我們采用ViewModelProviders.of(...).get(class)方法獲取ViewModel,我們就從這里開(kāi)始源碼開(kāi)始分析,我們先看下這個(gè)類(lèi)的源碼:

@Deprecated
public class ViewModelProviders {
    @Deprecated
    public ViewModelProviders() {
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {
        return new ViewModelProvider(fragment);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return new ViewModelProvider(activity);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        if (factory == null) {
            factory = fragment.getDefaultViewModelProviderFactory();
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        if (factory == null) {
            factory = activity.getDefaultViewModelProviderFactory();
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }
    @SuppressWarnings("WeakerAccess")
    @Deprecated
    public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
        @Deprecated
        public DefaultFactory(@NonNull Application application) {
            super(application);
        }
    }
}

我們看到此類(lèi)中提供了四個(gè)方法,其實(shí)著四個(gè)都以一樣,我們以第四個(gè)為例分析;第一個(gè)參數(shù)是Activity,第二個(gè)參數(shù)是一個(gè)Factory,默認(rèn)情況下我們是不需要傳入Factory這個(gè)參數(shù)的。如果不傳入的話,我們跟進(jìn)構(gòu)造方法就能看到默認(rèn)是用NewInstanceFactory來(lái)作為Factory的,看到內(nèi)部的create方法通過(guò)反射生成了一個(gè)ViewModel,實(shí)例源碼如下:

public static class NewInstanceFactory implements Factory {
    private static NewInstanceFactory sInstance;
    /**
     * Retrieve a singleton instance of NewInstanceFactory.
     *
     * @return A valid {@link NewInstanceFactory}
     */
    @NonNull
    static NewInstanceFactory getInstance() {
        if (sInstance == null) {
            sInstance = new NewInstanceFactory();
        }
        return sInstance;
    }
    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}
  • 如果傳入Factory參數(shù)的話,就會(huì)用我們自定義的Factory作來(lái)生成ViewModel。
  • of方法調(diào)用結(jié)束返回的是ViewModelProvider, 然后調(diào)用的是get方法,我們看下這個(gè)類(lèi)的部分源碼:
public class ViewModelProvider {
	private static final String DEFAULT_KEY ="androidx.lifecycle.ViewModelProvider.DefaultKey";
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    //-----1-----
	public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
	}
	public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    	this(owner.getViewModelStore(), factory);
	}
	public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
	 }
	 public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
}

上面截取的部分源碼其實(shí)是ViewModelProvider最重要的方法了,我們先看此類(lèi)中有兩個(gè)重要的成員變量,其中一個(gè)是mFactory, 注釋1處看到如果我們沒(méi)有傳入factory的話,默認(rèn)實(shí)現(xiàn)的是NewInstanceFactory, 印證了我們之前的說(shuō)法。還有一個(gè)是ViewModelStore, 它是怎么來(lái)的呢?因?yàn)镃omponentActivity中實(shí)現(xiàn)了接口ViewModelStoreOwner,在ViewModelProvider的構(gòu)造方法中調(diào)用owner.getViewModelStore(),這個(gè)owner就是ComponentActivity自身,然后獲取到了ViewModelStore這個(gè)變量,實(shí)際調(diào)用的源碼如下:

@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    //-----1-----
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

在注釋1處,判斷如果mViewModelStore == null的話,就會(huì)調(diào)取getLastNonConfigurationInstance嘗試獲取,如果獲取到了就將獲取到的賦值給mViewModelStore返回。這里就涉及到一個(gè)重要的知識(shí)點(diǎn)了,為什么說(shuō)ViewModel在橫豎屏切換的時(shí)候能夠持久的保存數(shù)據(jù),不需要像之前一樣調(diào)用onSaveInstanceState? 因?yàn)樵贏ctivity被銷(xiāo)毀的時(shí)候,還會(huì)調(diào)用另外一個(gè)方法onRetainNonConfigurationInstance, 我們看它在ComponentActivity中的源碼實(shí)現(xiàn):

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    if (viewModelStore == null && custom == null) {
        return null;
    }
	//----1-----
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

我們看到在注釋1的地方將我們之前存在的viewModelStore存儲(chǔ)到NonConfigurationInstances中了,然后在調(diào)用getViewModelStore的時(shí)候調(diào)用getLastNonConfigurationInstance這樣就保證了Activity銷(xiāo)毀之前和之后的viewModelStore是同一個(gè),那它里面存儲(chǔ)的ViewModel值也就是同樣的了。所以ViewModel的生命周期可以用下圖來(lái)概括:

接下來(lái)我們分析get方法:

private static final String DEFAULT_KEY =
        "androidx.lifecycle.ViewModelProvider.DefaultKey"
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

首先獲取類(lèi)的全稱(chēng)的字符串名字,和DEFAULT_KEY拼湊成一個(gè)Key,然后調(diào)用get的重載方法如下:

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
	//-----1-----
    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
    	//-----2-----
        viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

注釋1處判斷我們的modelClass是不是屬于ViewModel類(lèi)型的,并且判斷mFactory的類(lèi)型是否屬于OnRequeryFactory類(lèi)型,如果是的話,就返回值; 在注釋2處使用Factory通過(guò)反射創(chuàng)建一個(gè)viewModel, 然后將其存入mViewModelStore中。我們看下ViewModelStore的源碼:

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }
    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore的源碼很簡(jiǎn)單,內(nèi)部持有了一個(gè)HashMap對(duì)象,用來(lái)存放ViewModel。ViewModel的創(chuàng)建到此就結(jié)束了。然后就是使用的問(wèn)題, 使用如下

override fun onActivityCreated(savedInstanceState: Bundle?) {
	viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext()))
		        .get(AppsViewModel::class.java)
	viewModel.apps.observe(viewLifecycleOwner, Observer {
		  		//Update UI
    })	 
}

ViewModel的使用涉及到LiveData和Lifecycle部分,這里就不再多說(shuō)了

到此這篇關(guān)于Android開(kāi)發(fā)Jetpack組件ViewModel使用講解的文章就介紹到這了,更多相關(guān)Android Jetpack組件ViewModel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android實(shí)現(xiàn)輪播圖片效果

    Android實(shí)現(xiàn)輪播圖片效果

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)輪播圖片效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Android EditText默認(rèn)不彈出輸入法的實(shí)現(xiàn)方法

    Android EditText默認(rèn)不彈出輸入法的實(shí)現(xiàn)方法

    下面小編就為大家分享一篇Android EditText默認(rèn)不彈出輸入法的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Android微信圖片瀏覽框架設(shè)計(jì)

    Android微信圖片瀏覽框架設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了Android微信圖片瀏覽框架設(shè)計(jì),感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android自定義控件實(shí)現(xiàn)按鈕滾動(dòng)選擇效果

    Android自定義控件實(shí)現(xiàn)按鈕滾動(dòng)選擇效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)按鈕滾動(dòng)選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕詳解

    Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕詳解

    這篇文章主要介紹了Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕,PopupMenuButton是一個(gè)用于創(chuàng)建彈出菜單的小部件,當(dāng)用戶(hù)點(diǎn)擊觸發(fā)按鈕時(shí),PopupMenuButton會(huì)在屏幕上方或下方彈出一個(gè)菜單,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • android自定義Toast設(shè)定顯示時(shí)間

    android自定義Toast設(shè)定顯示時(shí)間

    這篇文章主要為大家詳細(xì)介紹了android自定義Toast設(shè)定顯示時(shí)間,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • android自定義手表效果

    android自定義手表效果

    這篇文章主要為大家詳細(xì)介紹了android自定義手表效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題

    解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題

    下面小編就為大家?guī)?lái)一篇解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Android微信右滑退出功能的實(shí)現(xiàn)代碼

    Android微信右滑退出功能的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android微信右滑退出功能的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Android中webview使用的一些坑

    Android中webview使用的一些坑

    這篇文章主要給大家介紹了關(guān)于Android中webview使用的一些坑,通過(guò)一下總結(jié)的這些內(nèi)容,對(duì)大家學(xué)習(xí)或者使用webview具有一定的參考學(xué)習(xí)價(jià)值,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-05-05

最新評(píng)論