是否可以强制 LiveData 值的不可空性?
Is it possible to enforce non-nullability of LiveData values?
有什么方法可以强制 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);
}
虽然您可以做一些事情,但您有责任确保不将 null
传递给 LiveData
。除此之外,每个 'solution' 更多的是对警告的抑制,这可能是危险的(如果你确实得到一个空值,你可能不会处理它并且 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);
}
您将不得不做一些额外的工作来处理来自库本身的空值。
例如,当您 return 来自 @Dao
房间中的 LiveData
时,例如:
@Dao interface UserDao {
@get:Query("SELECT * FROM users LIMIT 1")
val user: LiveData<User>
}
并观察 user
实时数据,如果没有用户,它将调用 onChanged
回调并返回 null
值。
如果你使用 Kotlin,你可以创建更好的带有扩展的非空观察函数。有一篇关于它的文章。 https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd0333
只有当您控制设置数据的代码时才有可能安全地执行此操作,因为您还必须包装 LiveData
class。这样,数据设置方法将受到 @NonNull
的保护,并且您可以确保数据在到达 Observer
.
之前已经过检查。
换行 LiveData
class:
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
安全。
fun <T> LiveData<T>.observeNonNull(owner: LifecycleOwner, observer: (t: T) -> Unit) {
this.observe(owner, Observer {
it?.let(observer)
})
}
如果您使用 Kotlin
,可以使用一个新选项。您可以将 LiveData
替换为 StateFlow。它更适合 Kotlin
代码并提供 built-in 空安全。
而不是使用:
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 {
// ...
}
}
}
StateFlow
是 coroutines 的一部分,要使用 lifecycleScope
,您需要添加 lifecycle-extensions
依赖项:
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
请注意,此 API 在 coroutines 1.4.0 之前是实验性的。
Here's 一些关于用 StateFlow
替换 LiveData
的额外阅读。
正如 Igor Bubelov 指出的那样,这种方法的另一个优点是它不是 Android
特定的,因此可以在多平台项目的共享代码中使用。
有什么方法可以强制 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);
}
虽然您可以做一些事情,但您有责任确保不将 null
传递给 LiveData
。除此之外,每个 'solution' 更多的是对警告的抑制,这可能是危险的(如果你确实得到一个空值,你可能不会处理它并且 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);
}
您将不得不做一些额外的工作来处理来自库本身的空值。
例如,当您 return 来自 @Dao
房间中的 LiveData
时,例如:
@Dao interface UserDao {
@get:Query("SELECT * FROM users LIMIT 1")
val user: LiveData<User>
}
并观察 user
实时数据,如果没有用户,它将调用 onChanged
回调并返回 null
值。
如果你使用 Kotlin,你可以创建更好的带有扩展的非空观察函数。有一篇关于它的文章。 https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd0333
只有当您控制设置数据的代码时才有可能安全地执行此操作,因为您还必须包装 LiveData
class。这样,数据设置方法将受到 @NonNull
的保护,并且您可以确保数据在到达 Observer
.
换行 LiveData
class:
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
安全。
fun <T> LiveData<T>.observeNonNull(owner: LifecycleOwner, observer: (t: T) -> Unit) {
this.observe(owner, Observer {
it?.let(observer)
})
}
如果您使用 Kotlin
,可以使用一个新选项。您可以将 LiveData
替换为 StateFlow。它更适合 Kotlin
代码并提供 built-in 空安全。
而不是使用:
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 {
// ...
}
}
}
StateFlow
是 coroutines 的一部分,要使用 lifecycleScope
,您需要添加 lifecycle-extensions
依赖项:
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
请注意,此 API 在 coroutines 1.4.0 之前是实验性的。
Here's 一些关于用 StateFlow
替换 LiveData
的额外阅读。
正如 Igor Bubelov 指出的那样,这种方法的另一个优点是它不是 Android
特定的,因此可以在多平台项目的共享代码中使用。