是否可以强制实施LiveData值的非空性?

时间:2017-09-29 12:03:24

标签: android android-architecture-components android-livedata

有没有办法强制实施LiveData值的不可为空性?默认的Observer实现似乎有@Nullable注释,它强制IDE建议该值可能为null并且应该手动检查:

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(@Nullable T t);
}

6 个答案:

答案 0 :(得分:3)

如果您使用Kotlin,您可以使用扩展创建更好的非空观察函数。有一篇关于它的文章。 https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd0333

答案 1 :(得分:1)

虽然您可以执行一些操作,但您有责任确保不将null传递给LiveData。除此之外,每个“解决方案”更多的是对警告的抑制,这可能是危险的(如果你确实得到一个空值,你可能无法处理它,Android Studio也不会警告你)。

断言

您可以添加assert t != null;。断言不会在Android上执行,但Android Studio会理解它。

class PrintObserver implements Observer<Integer> {

    @Override
    public void onChanged(@Nullable Integer integer) {
        assert integer != null;
        Log.d("Example", integer.toString());
    }
}

取消警告

添加注释以禁止警告。

class PrintObserver implements Observer<Integer> {

    @Override
    @SuppressWarnings("ConstantConditions")
    public void onChanged(@Nullable Integer integer) {
        Log.d("Example", integer.toString());
    }
}

删除注释

这也适用于我安装的Android Studio,但它可能不适合您,但您可以尝试从实现中删除@Nullable注释:

class PrintObserver implements Observer<Integer> {

    @Override
    public void onChanged(Integer integer) {
        Log.d("Example", integer.toString());
    }
}

默认方法

你不太可能在Android上使用它,但纯粹从Java的角度来看,你可以定义一个新的接口并在默认方法中添加一个空检查:

interface NonNullObserver<V> extends Observer<V> {

    @Override
    default void onChanged(@Nullable V v) {
        Objects.requireNonNull(v);
        onNonNullChanged(v);
        // Alternatively, you could add an if check here.
    }

    void onNonNullChanged(@NonNull V value);
}

答案 2 :(得分:1)

只有在您控制了包含数据的代码时才可以安全地执行此操作,因为您还必须包装LiveData类。这样,数据设置方法将受@NonNull保护,您可以确保在到达Observer之前已经检查了数据。

包裹LiveData课程:

public class NonNullMutableLiveData<T> extends MutableLiveData<T> implements NonNullLiveData<T> {

    private final @NonNull T initialValue;

    public NonNullMutableLiveData(@NonNull T initialValue) {
        this.initialValue = initialValue;
    }

    @Override
    public void postValue(@NonNull T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(@NonNull T value) {
        super.setValue(value);
    }

    @NonNull
    @Override
    public T getValue() {
        //the only way value can be null is if the value hasn't been set yet.
        //for the other cases the set and post methods perform nullability checks.
        T value = super.getValue();
        return value != null ? value : initialValue;
    }

    //convenience method
    //call this method if T is a collection and you modify it's content
    public void notifyContentChanged() {
        postValue(getValue());
    }

    public void observe(@NonNull LifecycleOwner owner, @NonNull NonNullObserver<T> observer) {
        super.observe(owner, observer.getObserver());
    }
}

创建一个用于公开为不可变的接口:

public interface NonNullLiveData<T> {

    @NonNull T getValue();

    void observe(@NonNull LifecycleOwner owner, @NonNull NonNullObserver<T> observer);
}

最后,打包Observer

//not implementing Observer<T> to make sure this class isn't passed to
//any class other than NonNullMutableLiveData.
public abstract class NonNullObserver<T> {

    public Observer<T> getObserver() {
        return new ActualObserver();
    }

    public abstract void onValueChanged(@NonNull T t);

    private class ActualObserver implements Observer<T> {

        @Override
        public void onChanged(@Nullable T t) {
            //only called through NonNullMutableLiveData so nullability check has already been performed.
            //noinspection ConstantConditions
            onValueChanged(t);
        }
    }
}

现在您可以像这样创建数据:

class DataSource {
    private NonNullMutableLiveData<Integer> data = new NonNullMutableLiveData<>(0);

    public NonNullLiveData<Integer> getData() {
        return data;
    }
}

并像这样使用它:

dataSource.getData().observe(this, new NonNullObserver<Integer>() {
            @Override
            public void onValueChanged(@NonNull Integer integer) {

            }
        });

完全null安全。

答案 3 :(得分:1)

如果使用Kotlin,则可以使用新选项。您可以将LiveData替换为StateFlow。它更适合Kotlin代码,并提供内置的null安全性。

代替使用:

class MyViewModel {
    val data: LiveData<String> = MutableLiveData(null) // the compiler will allow null here!
}

class MyFragment: Fragment() {
    model.data.observe(viewLifecycleOwner) {
        // ...
    }
}

您可以使用:

class MyViewModel {
    val data: StateFlow<String> = MutableStateFlow(null) // compilation error!
}

class MyFragment: Fragment() {
    lifecycleScope.launch {
        model.data.collect {
          // ...
        }
    }
}

StateFlowcoroutines的一部分,要使用lifecycleScope,您需要添加lifecycle-extensions依赖项:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

请注意,此API在coroutines 1.4.0之前是试验性的。

Here's关于将LiveData替换为StateFlow的其他内容。

正如Igor Bubelov所指出的那样,这种方法的另一个优点是它不是Android特有的,因此可以在多平台项目的共享代码中使用。

答案 4 :(得分:0)

您必须做一些额外的工作来处理来自库本身的空值。

例如,当你从房间的LiveData返回@Dao时,例如:

@Dao interface UserDao {

    @get:Query("SELECT * FROM users LIMIT 1")
    val user: LiveData<User>

}

观察user实时数据,如果没有用户,它会调用onChanged回调,并带有null值。

答案 5 :(得分:0)

fun <T> LiveData<T>.observeNonNull(owner: LifecycleOwner, observer: (t: T) -> Unit) {
    this.observe(owner, Observer {
        it?.let(observer)
    })
}