5

私の質問はViewModel second time returns nullに関連しています。私はサーバに繰り返し呼び出すとobserve関数でコールバックを取得していません。AndroidViewModel - 監視機能でデータを返さない

@Singleton 
public class NetworkInformationViewModel extends AndroidViewModel { 
    private LiveData<Resource<NetworkInformation>> networkInfoObservable; 
    private final APIClient apiClient; 

    @Inject 
    NetworkInformationViewModel(@NonNull APIClient apiClient, @NonNull Application application) { 
    super(application); 
    this.apiClient = apiClient; 
    getNetworkInformation(); 
    } 

    public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() { 
    return networkInfoObservable; 
    } 

    // making API calls and adding it to Observable 
    public void getNetworkInformation() { 
    networkInfoObservable = apiClient.getNetworkInformation(); 
    } 
} 

アクティビティでは、ViewModelには、以下のように定義されている - -

final NetworkInformationViewModel networkInformationViewModel = 
     ViewModelProviders.of(this, viewModelFactory).get(NetworkInformationViewModel.class); 
    observeViewModel(networkInformationViewModel); 

observeViewModel機能がViewModelに観察可能な追加するために使用される以下は、私が使用していたコードです。上記の機能に次の行をコメントアウト

public void observeViewModel(final NetworkInformationViewModel networkInformationViewModel) { 
    networkInformationViewModel.getNetworkInfoObservable() 
     .observe(this, networkInformationResource -> { 
     if (networkInformationResource != null) { 
      if (networkInformationResource.status == APIClientStatus.Status.SUCCESS) { 
      Timber.d("Got network information data"); 
      } else { 
      final Throwable throwable = networkInformationResource.throwable; 
      if (throwable instanceof SocketTimeoutException) { 
       final NetworkInformation networkInformation = networkInformationResource.data; 
       String error = null; 
       if (networkInformation != null) { 
       error = TextUtils.isEmpty(networkInformation.error) ? networkInformation.reply : networkInformation.error; 
       } 
       Timber.e("Timeout error occurred %s %s", networkInformationResource.message, error); 

      } else { 
       Timber.e("Error occurred %s", networkInformationResource.message); 
      } 
      if (count != 4) { 
       networkInformationViewModel.getNetworkInformation(); 
       count++; 
       // Uncommenting following line enables callback to be received every time 
       //observeViewModel(networkInformationViewModel); 
      } 
      } 
     } 
     }); 
    } 

は、コールバックが毎回来ることができますが、これを行うための適切な方法が存在しなければなりません。

//observeViewModel(networkInformationViewModel); 

ご注意: - 私はこれを実現するためにRxJavaの実装を必要としません。

+0

答えを確認しましたか:https://stackoverflow.com/questions/45889604/livedata-is-not-updating-its-value-after-first-call? – NiVeR

+0

@NiVeRそれを試してみて、助けにはならない。 –

+0

observableに追加する方法をコードに追加できますか? – Gautam

答えて

1

は今getNetworkInformation()にあなたがいる:setValue代わり

を使用してLiveDataを更新する新しいLiveData

  • を作成

    1. 、あなたはメンバーとしてAPIClientのための単一のLiveDataを作成しておく必要があり変数、次にgetNetworkInformation()ちょうどそのメンバーを更新しますLiveData

      さらに一般的には、APIClientはデータソースです。データソースには、データが変更されたときに更新されるメンバーLiveDataオブジェクトが含まれるようにすることができます。これらのLiveDataオブジェクトにゲッターを提供して、それらをViewModelでアクセス可能にし、最終的にそれらをアクティビティ/フラグメントで聴くことができます。これは、Roomなどの別のデータソースを使用して、Roomから返されたLiveDataをリッスンする方法と似ています。

      だから、この場合のコードは次のようになります。あなたは、もともとそれをコード化されたとして、その後、あなたのViewModelに...

      // I don't think this should be a Singleton; ViewModelProviders will keep more than one from being instantiate for the same Activity/Fragment lifecycle 
      public class SplashScreenViewModel extends AndroidViewModel { 
      
      private LiveData<Resource<NetworkInformation>> networkInformationLiveData; 
      
          @Inject 
          SplashScreenViewModel(@NonNull APIClient apiClient, @NonNull Application application) { 
          super(application); 
          this.apiClient = apiClient; 
      
          // Initializing the observable with empty data 
          networkInfoObservable = apiClient.getNetworkData() 
      
          } 
      
          public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() { 
          return networkInformationLiveData; 
          } 
      
      } 
      

      あなたの活動を同じにすることができ

      @Singleton 
      public class APIClient { 
          private final MutableLiveData<Resource<NetworkInformation>> mNetworkData = new MutableLiveData<>(); // Note this needs to be MutableLiveData so that you can call setValue 
      
          // This is basically the same code as the original getNetworkInformation, instead this returns nothing and just updates the LiveData 
          public void fetchNetworkInformation() { 
           apiInterface.getNetworkInformation().enqueue(new Callback<NetworkInformation>() { 
            @Override 
            public void onResponse(
            @NonNull Call<NetworkInformation> call, @NonNull Response<NetworkInformation> response 
           ) { 
            if (response.body() != null && response.isSuccessful()) { 
             mNetworkData.setValue(new Resource<>(APIClientStatus.Status.SUCCESS, response.body(), null)); 
            } else { 
             mNetworkData.setValue(new Resource<>(APIClientStatus.Status.ERROR, null, response.message())); 
            } 
            } 
      
            @Override 
            public void onFailure(@NonNull Call<NetworkInformation> call, @NonNull Throwable throwable) { 
            mNetworkData.setValue(
             new Resource<>(APIClientStatus.Status.ERROR, null, throwable.getMessage(), throwable)); 
            } 
           }); 
          } 
      
          // Use a getter method so that you can return immutable LiveData since nothing outside of this class will change the value in mNetworkData 
          public LiveData<Resource<NetworkInformation>> getNetworkData(){ 
           return mNetworkData; 
          } 
      
      } 
      

      。 ViewModelからLiveDataを取得して監視するだけです。

      Transformations.switchMapとは何ですか?

      switchMapあなたはAPIClientで基礎となるLiveDataインスタンスを変更する必要はありませんので、ここでは必要ありません。これは、本当に変化するデータが1つしかないからです。

      public class APIClient { 
          private MutableLiveData<Resource<NetworkInformation>> mNetData1, mNetData2, mNetData3, mNetData4; 
      
          ... 
      } 
      

      その後のあなたのfetchNetworkInformationは、状況に応じて観察するために異なるLiveDataを参照してくださいだろうと言ってみましょう:のではなく、あなたのAPIClientが何らかの理由で4つの異なるLiveDataを必要とし、あなたが観測されLiveData変更したいとしましょう。 getNetworkInformationから来る実際LiveDataが変更され、そしてあなたも、あなたが望むどのLiveData決定するためのパラメータのいくつかの並べ替えを使用しているこの場合

      public LiveData<Resource<NetworkInformation>> getNetworkInformation(int keyRepresentingWhichLiveDataToObserve) { 
          LiveData<Resource<NetworkInformation>> currentLiveData = null; 
          switch (keyRepresentingWhichLiveDataToObserve) { 
           case 1: 
            currentLiveData = mNetData1; 
            break; 
           case 2: 
            currentLiveData = mNetData2; 
            break; 
           //.. so on 
          } 
      
          // Code that actually changes the LiveData value if needed here 
      
          return currentLiveData; 
      } 
      

      :それはこのようになります。この場合、switchMapを使用します。これは、基になるLiveDataインスタンスを変更した場合でも、アクティビティ/フラグメントで呼び出されたobserveステートメントがAPIClientから返されたLiveDataを監視するようにするためです。そしてあなたは再び観察を呼びたくはありません。

      は今、これは抽象例のビットですが、それは基本的にだRoom Daoへのあなたの呼び出しが何をすべきか - あなたはIDに基づいて、あなたのRoomDatabaseを照会し、LiveData返すDao方法を持っている場合、それは異なるが返されますLiveDataインスタンスはIDに基づいています。

  • +0

    最後の質問では、 'mNetworkData'を' APIInterfaceクラス 'のプライベートグローバル変数にしました。これは私が他の場所でweillとして使うので意味があります。しかし、アプリは異なる画面で異なるAPIに対して30以上のオブザーバブルを持つかもしれません。そのような場合には、それらをすべてグローバルにすることは理にかなっていません。 –

    +1

    [ここのGithubサンプル](https://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample/app/src/)に示すように、アプリケーションのさまざまなデータタイプに異なるリポジトリを作成することができますデータを分離し、特定のViewModelに必要なデータを表すリポジトリのみを使用するのに役立ちます。 – Lyla

    +1

    もし、githubサンプルがLiveDataを返すように思われるのであれば、それは[NetworkResource]というMediatorLiveDataオブジェクトを返すからです。(https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample /app/src/main/java/com/android/example/github/repository/NetworkBoundResource.java)。この方法でのMediatorLiveDataの使用方法については、こちらをご覧ください(https://developer.android.com/topic/libraries/architecture/guide.html#addendum)。 – Lyla

    0

    私はすでにlinked question's answerを更新しました。再掲載、私は疑問に報奨金を置いてきたし、うまくいけば、誰かがこの問題を処理するための適切な方法であることが確認されますので、ここに。

    更新作業溶液を以下に示します - 彼女はdroidCon NYC video LiveDataの詳細については、見る

    final SplashScreenViewModel splashScreenViewModel = 
        ViewModelProviders.of(this, viewModelFactory).get(SplashScreenViewModel.class); 
    observeViewModel(splashScreenViewModel); 
    // This function will ensure that Transformation.switchMap() function is called 
    splashScreenViewModel.setNetworkInformation(); 
    

    -

    @Singleton 
    public class SplashScreenViewModel extends AndroidViewModel { 
        private final APIClient apiClient; 
        // This is the observable which listens for the changes 
        // Using 'Void' since the get method doesn't need any parameters. If you need to pass any String, or class 
        // you can add that here 
        private MutableLiveData<Void> networkInfoObservable; 
        // This LiveData contains the information required to populate the UI 
        private LiveData<Resource<NetworkInformation>> networkInformationLiveData; 
    
        @Inject 
        SplashScreenViewModel(@NonNull APIClient apiClient, @NonNull Application application) { 
        super(application); 
        this.apiClient = apiClient; 
    
        // Initializing the observable with empty data 
        networkInfoObservable = new MutableLiveData<Void>(); 
        // Using the Transformation switchMap to listen when the data changes happen, whenever data 
        // changes happen, we update the LiveData object which we are observing in the MainActivity. 
        networkInformationLiveData = Transformations.switchMap(networkInfoObservable, input -> apiClient.getNetworkInformation()); 
        } 
    
        /** 
        * Function to get LiveData Observable for NetworkInformation class 
        * @return LiveData<Resource<NetworkInformation>> 
        */ 
        public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() { 
        return networkInformationLiveData; 
        } 
    
        /** 
        * Whenever we want to reload the networkInformationLiveData, we update the mutable LiveData's value 
        * which in turn calls the `Transformations.switchMap()` function and updates the data and we get 
        * call back 
        */ 
        public void setNetworkInformation() { 
        networkInfoObservable.setValue(null); 
        } 
    } 
    

    活動のコードは次のように更新されます。 LiveDataのためのGoogleの公式リポジトリはGithubBrowserSampleプロジェクトのhttps://github.com/googlesamples/android-architecture-components/外観です。

    apiClient.getNetworkInformation()コールは、追加情報を取得する任意のパラメータ、それを必要としません。したがって、「ボイド」はMutableLiveDataに添加しました。

    public LiveData<Resource<NetworkInformation>> getNetworkInformation() { 
        final MutableLiveData<Resource<NetworkInformation>> data = new MutableLiveData<>(); 
    
        apiInterface.getNetworkInformation().enqueue(new Callback<NetworkInformation>() { 
         @Override 
         public void onResponse(
         @NonNull Call<NetworkInformation> call, @NonNull Response<NetworkInformation> response 
        ) { 
         if (response.body() != null && response.isSuccessful()) { 
          data.setValue(new Resource<>(APIClientStatus.Status.SUCCESS, response.body(), null)); 
         } else { 
          data.setValue(new Resource<>(APIClientStatus.Status.ERROR, null, response.message())); 
         } 
         } 
    
         @Override 
         public void onFailure(@NonNull Call<NetworkInformation> call, @NonNull Throwable throwable) { 
         data.setValue(
          new Resource<>(APIClientStatus.Status.ERROR, null, throwable.getMessage(), throwable)); 
         } 
        }); 
        return data; 
        } 
    
    +0

    通常、 'input'を' getNetworkInformation'関数のidや何かに使うと、そのIDに結びついた別のLiveDataを返す必要があります。 'getNetworkInformation'機能が何をしているのかを共有することは可能ですか?それはあなたの根本的な問題が存在する場所になります。私はそれが新しいLiveDataを構築していると思っています。新しいLiveDataを構築する必要があるのか​​、 'setValue' /' postValue'を使って既存のLiveDataを単純に更新できるのかを判断すると役に立ちます。 – Lyla

    +0

    ネットワークデータを取得するLiveDataを更新する方法は、(新しいLiveDataを作成するのではなく)次のようになります:[this class](https://github.com/googlecodelabs/android-build-an-app-architecture) /コンポーネント/ BLOB/arch-training-steps/app/src/main/java/com/example/android/sunshine/data/network/WeatherNetworkDataSource.java#L180)。 – Lyla

    +0

    ここで[LiveDataが作成されました](https://github.com/googlecodelabs/android-build-an-app-architecture-components/blob/arch-training-steps/app/src/main/java/com/example) /android/sunshine/data/network/WeatherNetworkDataSource.java#L64)、[ネットワークリクエストが完了すると更新されました](https://github.com/googlecodelabs/android-build-an-app-architecture-components/blob/ arch-training-steps/app/src/main/java/com/example/android/sunshine/data/network/WeatherNetworkDataSource.java#L180)を実行します。 – Lyla

    0

    私は同じ問題を満たしていなかったが、私はオブザーバーの数iがDBにデータを保存するたびに増加し、同じようなことに出くわしました。私はデバッグ方法が呼び出され、私はあなたがビューモデルからのライブデータをフェッチしているとき、それは非nullをチェックする必要があるか、あなただけの1インスタンスが返されていると言うことができることを知っているようになったなっていたどのように多くのインスタンスまたはオブザーバーの異なるインスタンスでした -

    private LiveData<T> data; 
        public LiveData<T> getLiveData(){ 
         if(data ==null){ 
          data = //api call or fetch from db 
         } 
         return data; 
        } 
    

    私は単にdataオブジェクトを返していましたし、ソースを確認した後、私は自動的にせずに、オブジェクトと毎回の更新をlivedataという結論に来る前はnullチェックの新しいインスタンスを作成なっていたと新しいオブザーバーは、添付なりました。私は、livedataに関する私の理解が間違っている場合、誰かが私を正すことができます。

    +0

    droidConのビデオを見ましたか?それが助けになりました。 –

    +0

    まだ、私は確かに見ています。それはあなたを助けてうれしい – Gautam

    +0

    彼女の助けを借りて会議!彼女はそれを勧めました。 –

    関連する問題