android Jetpack应用实战(二)-ViewModel

ViewModel是一个Android架构组件,用于保持Activity或Fragment的数据,即使设备旋转等配置更改也不会丢失。它通过LiveData进行数据绑定,并在主线程中安全地更新UI。当在Fragment中使用ViewModel时,需确保使用`newViewModelProvider(getActivity()).get(MyViewModel.class)`来获取相同的ViewModel实例。AndroidViewModel则提供Application级别的context,避免内存泄露。此外,`postValue()`方法用于在非主线程中更新LiveData。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于View Model 

官网都有介绍了哈,

https://developer.android.com/topic/libraries/architecture/viewmodel?hl=zh-cn

官网里面的生命周期图片,一目了然

 总的来说是,就是保证数据在整个Activity 生命周期都不会丢失,包括旋转屏.

使用方式

使用也很简单

Activity中,套用官网的代码参考就可以了,这个很简单

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}
public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}

照抄就可以运行了

对于fragment ,这个稍微有点需要注意的地方.

Activity 中数据有变化,在Fragment 中更新不了?或者一个Activity 中两个Fragment 互相交互数据没法更新到对方的ViewModel 中.有点绕,举个例子.

我写了一个简单的页面

一个页面里的两个Fragment,  通过button1 改变 Fragment2 中的值

button2 改变frament1 中的文本值,用view model 实现

 放有问题的代码,逻辑比较简单

ViewModel

public class MyViewModel extends ViewModel {

    protected MutableLiveData<String> test1 = new MutableLiveData<>();
    protected MutableLiveData<String> test2 = new MutableLiveData<>();

    public  void setTest1(String name){
        test1.setValue(name);
    }

    public  void setTest2(String name){
        test2.setValue(name);
    }
}

BlankFragment1
   button1.setOnClickListener(v -> {
            model.setTest2("BlankFragment1");
        });
        model = new ViewModelProvider(this).get(MyViewModel.class);
        model.test1.observe(this, tset -> {

            Log.e("xss",tset);
            // update UI
            blank1.setText(tset);
        });

BlankFragment2

button2.setOnClickListener(v -> {
            model.setTest1("BlankFragment2");
        });
        model = new ViewModelProvider(this).get(MyViewModel.class);

        model.test2.observe(this, tset -> {
            // update UI
            Log.e("xss",tset);
            textView.setText(tset);
        });

通过点击发现页面文本没有任何改变....然后我把model 对象打印出来,发现两个Frament 中压根不是一个对象,如何保证和Activity 中同样的对象呢.

model = new ViewModelProvider(this).get(MyViewModel.class); 错误

model = new ViewModelProvider(getActivity()).get(MyViewModel.class); 正确

修改后验证ok ,Fragment 之间,或者Fragment 和Actvity 之前数据都可以共享了

AndroidViewModel

我们使用ViewModel 的时候难免会需要一些context 对象,当然我们也可以通过参数的方式传递当前的context,但是这样可能会导致内存泄露,于是为了解决这个问题,google 为我们提供了AndroidViewModel.

可以看到接收的是Application 作为context,应用的生命周期一致,解决内存泄露的问题

public class MyAndroidViewModel extends AndroidViewModel {

    public MyAndroidViewModel(@NonNull @NotNull Application application) {
        super(application);
    }


}

其他使用方式同ViewModel 一致

其他用法

postValue()

  /**
     * Posts a task to a main thread to set the given value. So if you have a following code
     * executed in the main thread:
     * <pre class="prettyprint">
     * liveData.postValue("a");
     * liveData.setValue("b");
     * </pre>
     * The value "b" would be set at first and later the main thread would override it with
     * the value "a".
     * <p>
     * If you called this method multiple times before a main thread executed a posted task, only
     * the last value would be dispatched.
     *
     * @param value The new value
     */
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

看接口描述就知道在其他线程中调用,和handler 中更新MainThread 中UI 效果一样.当然了如果是主线程的话,直接调用setValue() 即可

参考

https://developer.android.com/topic/libraries/architecture/viewmodel?hl=zh-cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值