Android  JetPack~ ViewModel (一)   介紹與使用

TMusketeer發表於2023-02-20
 
ViewModel,MVVM框架中的一部分,他主要作用是處理業務邏輯、資料分配等,他是Mode和VIew連線的橋樑,和MVP相比,他類似P。
ViewModel類有如下優點:
  1. ViewModel類是具有生命感知的能力,與和他繫結的Activity一樣的生命週期,它可以解決請求網路時,Activity被突然銷燬造成一些不必要的麻煩。
  2. 裝置資訊發生變更資料不會丟失(切橫豎屏),其實它只有一個生命週期,檢測頁面銷燬時觸發
  3. ViewModel 的另一個特點就是同一個 Activity 的 Fragment 之間可以使用ViewModel實現共享資料。
  4. 每個Activity繫結的ViewModel都是獨立的(Activity之間)

沒用的小忠告

有些問題,可能沒有深度。
有一次一個面試官問我,Q:你知道ViewModel是如何和Acvitity繫結的嗎?我當時大腦在想1、他是不是問我內部實現?2、簡單用法?我猜是第一,不然第二種的話,有啥可問的。當時我說:我不知道。他詫異的看著我,你不是使用過MVVM框架嗎?不知道怎麼用ViewModel嗎。我不自信的說了句是透過ViewModelProvider。他來了一句是,是這個,後面問了我好多很簡單的問題,當時給我整無語了,怎麼全是這咋用,那咋用,一點深度都沒有,卻給我搞的很不自信,因為好多使用步驟平常不復習,就很容易忘,我當時就覺得這小子水平估計也不咋樣,離場後果斷刪除了他們人事的聯絡方式。
 
Android  JetPack~ ViewModel (一)   介紹與使用

Android  JetPack~ ViewModel (一)   介紹與使用1、開始使用

新增依賴

以前是需要新增依賴的,現在
方式一: implementation 'androidx.appcompat:appcompat:1.2.0',//這裡面也包含ViewModel,LiveData等,
    
方式二: 也可以單獨新增
implementation "androidx.fragment:fragment:1.1.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.1.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"

2、Activity

2.1、新建ViewModel

public class MyViewModel extends ViewModel {

    @Override
    protected void onCleared() {
        super.onCleared();
    }
}
onCleared方法是ViewModel獨有的,當Activity銷燬後,它會呼叫,所以我們可以在這取消網路請求等。平常開發中不做任何操作時,如果有網路請求中,Activity被銷燬,那麼極有可能請求成功返回結果到activity中造成洩漏等不必要的麻煩。

解決方式:

可以使用ViewModel,結合onCleared(),call.cancel()等去做。
網路請求時,突然銷燬activity,那麼與之繫結的viewmodel也會銷燬,同時我們在onCleared()方法中取消網路連線介面(call.cancel()),就算是有資料返回,activity也不會收到通知,因為資料是從livedata中監聽的,只有livedata觸發了更新,Activity才會收到通知。
(當然還有很多其他方式)
 

2.2、繫結Activity

這裡有如下方式:
  • ViewModelProviders.of(this).get(class)(過時)
  • new ViewModelProvider(this,factory).get(class)(常用)
  • new ViewModelProvider.NewInstanceFactory().create(class);
 

第一種方式:ViewModelProviders.of(this).get(class)

ViewModelProviders.of(this).get(MyViewModel.class);


implementation "androidx.fragment:fragment:1.1.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.1.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
這種方式是這些依賴中的,當然,如果你的和我的不一樣,也無所謂,這不是重點。這個方式比較老了,在原始碼中也能發現,ViewModelProviders的of方法其實也是呼叫了ViewModelProvider,自動建立了一個factory。
----------------ViewModelProviders.class----------------

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
 
 

第二種方式:new ViewModelProvider(this,factory).get(class)

ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
傳入一個系統的factory。create是建立一個新的例項,而get是先從HashMap中找,找不到就建立新的例項。也是為什麼重建的Viewmodel是同一個物件的原因,他會把物件放在ViewModelStore類中HashMap<String, ViewModel> mMap = new HashMap<>();中。這個方式比較新,一般也是這樣建立。
 
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //第一步:先查詢
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    //第二步:沒有則建立,並儲存在mViewModelStore中
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

第三種方式:new ViewModelProvider.NewInstanceFactory().create(class);

MyViewModel myViewModel1 = new ViewModelProvider.NewInstanceFactory().create(MyViewModel.class);
 
 
下面寫一個LiveData,如果對LiveData不懂的可以翻看之前的部落格,目錄在頂部
public class MyViewModel extends ViewModel {
    private MutableLiveData<String> mStr= new MutableLiveData<>();

    public MutableLiveData<String> getmStr() {
        return mStr;
    }
    public void setmStr(String s) {
        if(mStr==null){
            mStr = new MutableLiveData<>();
        }
        mStr.setValue(s);
    }
    @Override
    protected void onCleared() {
        super.onCleared();
    }
}
全部程式碼
public class JPackActivity extends AppCompatActivity {

    private ActyJpackLayoutBinding mBinding;
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //透過databinding 把activity和view繫結
        mBinding = DataBindingUtil.setContentView(this,R.layout.acty_jpack_layout);
        //透過ViewModeProvider 把activity和ViewModel繫結起來。
        myViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);

        myViewModel.getmStr().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                mBinding.textShow.setText(s);
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //移除觀察者
       
    }
 

3、Fragment

使用和Activity中一樣。不過需要注意的是傳入this和getActivity的區別
  • this:這個ViewModel是獨立的,只為這個Fragment單獨服務,其他Fragment無法獲取到相同記憶體地址的ViewModel
  • getActivity:使用getActivity()獲得的ViewModel 作用域在Activity裡和所有他建立碎片的裡,意思是你在其他Fragment也獲取相同記憶體地址的ViewModel
 
 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    MyViewModel myViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
    return inflater.inflate(R.layout.fragment_blank, container, false);
}
 
當然還有一個注意點get("key1",MyViewModel.class);,get方法可以傳key值,不同的key建立的ViewModel資料是獨立的
 

4、總結

使用起來非常簡單,還要多加練習,才不會被那些xx面試官問步驟咋實現難住。
 

相關文章