Google Architecture ViewModel

關於ViewModel 介紹,文章不過多闡述。官方文檔html

本文將從三個方面作必定闡述java

  1. 爲何會推出ViewModel,它能帶來什麼好處?
  2. 一些例子
  3. 相關注意事項

爲何須要ViewModel

  • 你須要處理配置變化android

    • 筆者認爲,用戶能夠隨時改變配置(i.e 旋轉屏幕,切換語言,切換系統文字大小 ...),這可能會致使當前Activity重建,這些都不受開發者的控制,可是你又不得不處理它
    • 可能不少APP在配置清單文件中申明瞭每個Activityorientation = portrait ,可是你沒法禁止用戶去改變語言、文字大小。這樣就可能會致使Activity被移除或者從新建立
  • 爲何onSaveInstanceState依舊不夠git

    傳統的作法都是在配置發生變化即 onSaveInstanceState 方法去save data, 在onCreate去restore datagithub

    可是這裏有兩個限制api

    • onSaveInstanceState方法不可以緩存較大的數據,筆者以前嘗試緩存上百兆數據發現拋出了TransactionTooLargeException
    • 保存的數據必定須要實現serializable 或者 Parceable, 可是有時候這些數據來自第三方庫,咱們不能修改它,對於某些場景,很難在onSaveonSaveInstanceState中保存數據

基於上述兩點,ViewModel應運而生緩存

  • 配置改變先後數據存儲與恢復

一些例子

基礎功能

public class ZeroViewModel extends ViewModel {
    public User user;
}
複製代碼
public class ZeroDemo extends AppCompatActivity {
    private TextView tv;
    private ZeroViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tv_btn);
        tv = findViewById(R.id.tv_simple);
        
        vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
        System.out.println("szw vm.user = " + vm.user);
    }
    
    // android:onClick="onClickSimpleButton"
    public void onClickSimpleButton(View v) {
        vm.user = new User(23, "jorden");
    }
}
複製代碼

旋轉屏幕,vm.user 依舊 != null網絡

同一個Activity不一樣實例

  1. 同時存在兩個實例
public class SameClass01 extends AppCompatActivity {
    private TextView tv;
    private ZeroViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tv_btn);
        tv = findViewById(R.id.tv_simple);

        vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
        System.out.println("szw SameClass01 : " + vm.user);

    }
    // launch the second instance
    // android:onClick="onClickSimpleButton"
    public void onClickSimpleButton(View v) {
        vm.user = new User(100, "SuperMario");
        startActivity(new Intent(this, SameClass01.class));
    }
}
複製代碼

即便有兩個同類的Activity實例,第一個vm.user 持有的依舊是Mario,第二個vm.user 持有null . 這符合筆者的預期oracle

  1. finish再從新建立
public class SameClass02 extends AppCompatActivity {
    private TextView tv;
    private ZeroViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tv_btn);
        tv = findViewById(R.id.tv_simple);
        vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
        System.out.println("szw SameClass02 onCreate() : " + vm.user);
    }
    // android:onClick="onClickSimpleButton"
    public void onClickSimpleButton(View v) {
        vm.user = new User(22, "test");
    }

    // android:onClick="onClickSimpleButton2"
    public void onClickSimpleButton2(View v) {
        System.out.println("szw SameClass02 : saved = "+vm.user);
    }
}
複製代碼

先啓動SameClass02 ,執行onClickSimpleButton,finish從新打開,日誌輸出nullapp

上述兩個例子表現正常

和Static申明的變量比較

  1. 基礎比較
public class SameVm {
    public static User user;
}
複製代碼
public class ZeroDemo extends AppCompatActivity {
    private TextView tv;
    private ZeroViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tv_btn);
        tv = findViewById(R.id.tv_simple);

        String value = savedInstanceState == null ? "emptyBundle" : savedInstanceState.getString("key");
        System.out.println("szw onCreate() " + value);

        vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
        System.out.println("szw vm.user = " + vm.user);

        System.out.println("szw static = "+SameVm.user);
    }

    // android:onClick="onClickSimpleButton"
    public void onClickSimpleButton(View v) {
        vm.user = new User(23, "jorden");
        SameVm.user = new User(21, "king");
    }
}
複製代碼

旋轉屏幕後,日誌輸出

szw vm.user = User{id=23, name='jorden'}
szw static = User{id=21, name='king’}
複製代碼
  1. 終止應用Terminate Application

和1一樣的操做

szw vm.user = null
szw static = null
複製代碼

從上面兩個例子,感受沒什麼不一樣。他們都能緩存數據,終止應用程序都會被銷燬

它們的不一樣之處:

  • ViewModel 主要是爲了解耦,有點相似於MVP中的P,ViewModel 是MvvM中VM。你能在ViewModel中作異步操做(i.e訪問網絡),你能夠改變data而且讓View接受到通知LiveData
  • static value 能被任何類修改,可是ViewModel 是Activity的私有變量,有點相似ThreadLocal
  • ViewModel 能夠判斷Activity是正常銷燬或者配置改變,進而作出不一樣的響應,finish->removedata ,configurationchange->savedata,靜態變量卻不能

相關注意事項

  1. ViewModel不要應用Activity等相關實例,容易形成內存泄漏
  2. 若是你須要在ViewMolde中獲取Resource LocationManager等系統服務,能夠繼承AndroidViewModel
  3. ViewModel自己不支持事件模型(EventBus),你能夠使用LiveData,固然爲了解決旋轉屏幕後,再次註冊Observer,重複提示,能夠使用SingleLiveEvent
  4. 當系統回收咱們應用時,ViewModel 並不能保存數據,咱們依舊須要複寫onSaveInstanceState方法

源碼以下

public class DupliViewModel extends ViewModel {
    private SingleLiveEvent<String> message = new SingleLiveEvent<>();

    public void fetchMessage(){
        message.setValue("A New Value");
    }

    public LiveData<String> getMessage() {
        return message;
    }
}
複製代碼
public class DupliObserverDemo extends AppCompatActivity {
    private TextView tv;
    private DupliObserverDemo self;
    private DupliViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tv_btn);
        self = this;
        tv = findViewById(R.id.tv_simple);

        vm = ViewModelProviders.of(this).get(DupliViewModel.class);
        vm.getMessage().observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                System.out.println("szw updated ~");
                Toast.makeText(self, "updated "+s, Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    // android:onClick="onClickSimpleButton"
    public void onClickSimpleButton(View v) {
        vm.fetchMessage();
    }

}
複製代碼
相關文章
相關標籤/搜索