ViewModel

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

場景一: 如果系統(tǒng)銷毀或重新創(chuàng)建界面控制器,則存儲在其中的任何瞬態(tài)界面相關數(shù)據(jù)都會丟失。例如,應用可能會在它的某個 Activity 中包含用戶列表。為配置更改重新創(chuàng)建 Activity 后,新 Activity 必須重新提取用戶列表。對于簡單的數(shù)據(jù),Activity 可以使用 onSaveInstanceState() 方法從 onCreate() 中的捆綁包恢復其數(shù)據(jù),但此方法僅適合可以序列化再反序列化的少量數(shù)據(jù),而不適合數(shù)量可能較大的數(shù)據(jù),如用戶列表或位圖。

場景二:界面控制器經常需要進行可能需要一些時間才能返回的異步調用。界面控制器需要管理這些調用,并確保系統(tǒng)在其銷毀后清理這些調用以避免潛在的內存泄漏。此項管理需要大量的維護工作,并且在為配置更改重新創(chuàng)建對象的情況下,會造成資源的浪費(重新網絡請求數(shù)據(jù)),因為對象可能需要重新發(fā)出已經發(fā)出過的調用。

場景三:諸如 Activity 和 Fragment 之類的界面控制器主要用于顯示界面數(shù)據(jù)、對用戶操作做出響應或處理操作系統(tǒng)通信(如權限請求)。如果要求界面控制器也負責從數(shù)據(jù)庫或網絡加載數(shù)據(jù),那么會使類越發(fā)膨脹。為界面控制器分配過多的責任可能會導致單個類嘗試自己處理應用的所有工作,而不是將工作委托給其他類。以這種方式為界面控制器分配過多的責任也會大大增加測試的難度。
從界面控制器邏輯中分離出視圖數(shù)據(jù)所有權的操作更容易且更高效。

場景四:Activity 中的兩個或更多 Fragment 需要相互通信是一種很常見的現(xiàn)象。想象一下拆分視圖 (master-detail) Fragment 的常見情況,假設您有一個 Fragment,在該 Fragment 中,用戶從列表中選擇一項,還有另一個 Fragment,用于顯示選定項的內容。這種情況不太容易處理,因為這兩個 Fragment 都需要定義某種接口描述,并且所有者 Activity 必須將兩者綁定在一起。此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未創(chuàng)建或不可見的情況。
可以使用 ViewModel 對象解決這一常見的難點。這兩個 Fragment 可以使用其 Activity 范圍共享 ViewModel 來處理此類通信,如以下示例代碼所示:

以上幾種種情況描述摘自官網;從中得出ViewModel 目標:

1、讓狀態(tài)管理獨立于 視圖層(activity或fragment) 之外,保存狀態(tài)數(shù)據(jù),避免數(shù)據(jù)丟失導致的數(shù)據(jù)重建;
2、通過作用域可控的 共享ViewModel ,進行跨界面通信
3、及時叫停異步任務,避免造成不必要的內存泄漏;

ViewModel 的生命周期

ViewModel 對象存在的時間范圍是獲取 ViewModel 時傳遞給 ViewModelProviderLifecycleViewModel 將一直留在內存中,直到限定其存在時間范圍的 Lifecycle 永久消失:對于 Activity,是在 Activity 完成時;而對于 Fragment,是在 Fragment 分離時。

圖 1 說明了 Activity 經歷屏幕旋轉而后結束時所處的各種生命周期狀態(tài)。該圖還在關聯(lián)的 Activity 生命周期的旁邊顯示了 ViewModel 的生命周期。此圖表說明了 Activity 的各種狀態(tài)。這些基本狀態(tài)同樣適用于 Fragment 的生命周期。

viewmodel-lifecycle.png

實例一:簡單實用ViewModel

/**
 * 視圖層MainFragment
 */
class MainFragment : Fragment() {
    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.main_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        //TODO:通過 ViewModelProvider 獲取作用域在 MainFragment 的 ViewModel
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        // TODO: Use the ViewModel
        viewModel.getUsers().observe(this) {
            //TODO:可執(zhí)行刷新recycleview adapter 的 操作
        }
    }
}


/**
 * 狀態(tài)管理MainViewModel
 */
class MainViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData<List<User>>().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }

    //TODO:需要單獨額外維護,這里只是示例
    data class User(val namt: String)
}

????示例是如何使用 ViewModel,如何理解 ViewModel 的作用域,下面以MainFragment為例,帶著問題看源碼

1、class MainFragment : Fragment() {//MainFragment 繼承了 Fragment}

2、public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, SavedStateRegistryOwner {
    //TODO: Fragment 實現(xiàn)了 ViewModelStoreOwner
}

3、public interface ViewModelStoreOwner {
     //TODO: 
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
   }

4、getViewModelStore() //用于獲取 ViewModelStore

5、ViewModelStore 類:
public class ViewModelStore {
    //TODO: 維護了一個mMap 用于存儲ViewModel
    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();
    }
}

6、通過上面5個步驟:
   MainFragment-->Fragment()-->ViewModelStoreOwner-->ViewModelStore{
   private final HashMap<String, ViewModel> mMap = new HashMap<>();
   }
  //TODO:可以理解為: MainFragment 維護了一個 mMap 用于存儲ViewModel

7、繼續(xù)看ViewModel的初始化:
  viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
  |
  public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
  |
  private final ViewModelStore mViewModelStore;//TODO:注意這里
  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;//賦值
    }
  
8、回過頭再看: viewModel = ViewModelProvider(this).get(MainViewModel::class.java) 里面的get(MainViewModel::class.java)
看 源碼:
 @NonNull
 @MainThread
 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);
 }
|
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
      //TODO: 這里mViewModelStore 既第7步中的 private final ViewModelStore mViewModelStore
        ViewModel viewModel = mViewModelStore.get(key);
      //……省略部分代碼
}
|
//TODO: 在此回到了ViewModelStore 里面
final ViewModel get(String key) {
        return mMap.get(key);
} 

通過源碼解讀,分析出ViewModel的作用域是受創(chuàng)建它的視圖層所控的也就是

//TODO: 當this 是 Fragment ,那么它的作用域是fragment ,
viewModel = ViewModelProvider(this@MainFragment).get(MainViewModel::class.java)
//TODO: 當this 是 Activity ,那么它的作用域是activity ,
viewModel = ViewModelProvider(this@MainActivity).get(MainViewModel::class.java)

//TODO: 當Activity內的多個Fragment要共享ViewModel 可以通過傳入Activity 進行初始化共享ViewModel
viewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

上面通過源碼分析了,ViewModel的作用域是可控,以及多Fragment共享VIewModel的實現(xiàn)和原理

下一個問題,ViewModel 何時調用 clear 移除操作呢?看源碼:

public final void clear() {
        for (ViewModel vm : mMap.values()) {
        //TODO: 遍歷移除所有ViewModel
            vm.clear();
        }
        mMap.clear();
 }
最簡單的方式,看誰調用了clear方法:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
      //TODO: 省略大部分代碼……

public ComponentActivity() {
        //……省略大部分代碼
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        //TODO:清理移除操作,避免內存泄漏
                        getViewModelStore().clear();
                    }
                }
            }
        });
      //……省略大部分代碼
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容