web-dev-qa-db-ja.com

LiveDataの値をnullにできないようにすることは可能ですか?

LiveData値のnull可能性を強制する方法はありますか?デフォルトのObserver実装には@Nullableアノテーションがあり、IDEが値がnullの可能性があるため手動で確認する必要があることを示唆しています。

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

できることはいくつかありますが、nullLiveDataに渡さないようにするのはあなたの責任です。それに加えて、すべての「解決策」は警告の抑制であり、危険である可能性があります(null値を取得した場合、それを処理できず、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の観点から見ると、新しいインターフェイスを定義して、デフォルトのメソッドにnullチェックを追加できます。

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);
}
1
niknetniko

Kotlinを使用する場合、拡張機能を使用して、より優れた非null監視関数を作成できます。それに関する記事があります。 https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd03

4
Henry Tao

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
Sir Codesalot
fun <T> LiveData<T>.observeNonNull(owner: LifecycleOwner, observer: (t: T) -> Unit) {
    this.observe(owner, Observer {
        it?.let(observer)
    })
}
1
metis

ライブラリ自体からのnull値を処理するには、追加の作業を行う必要があります。

たとえば、Roomの@DaoからLiveDataを返すと、次のようになります。

@Dao interface UserDao {

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

}

そして、userライブデータを観察します。ユーザーがいない場合は、onChangedコールバックをnull値で呼び出します。

0
arekolek