web-dev-qa-db-ja.com

LiveData setValueまたはPostValueがビューでonChangeを1回だけトリガーするのはなぜですか?

LiveData setValueは、アクティビティでonChangedメソッドをトリガーする必要がありますが、最初にのみ呼び出します。ページングを作成しようとすると、応答が成功し、次のように表示されますが、壊れてonChangedを呼び出さなくなります。ログ。 setValue/postValueの何が問題になっていますか?バグですか?オブザーバーパターンを自分で実装する必要がありますか?では、LiveDataを使用する意味は何ですか?私のページングは​​、この2〜3日間だけでは機能しません。

  1. MainActivityクラス

     public class MainActivity extends AppCompatActivity 
     private MutableLiveData<List<Photo>> mLivePhotos;
     // some code...
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
        mLivePhotos = loadData();
        mLivePhotos.observe(this, photos -> {
            Log.d(TAG, "onChanged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            mProgressBar.setVisibility(View.GONE);
            mPhotos = photos;
            if (mIsInitialCall) {
                initiateAdapter();
                mIsInitialCall = false;
            } else {
                mAdapter.updateList(mPhotos.subList(mPageNumber, mPageNumber + 10));
            }
        });
    
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                int lastPosition = 
    mLayoutManager.findLastCompletelyVisibleItemPosition();
                Log.d(TAG, "onScrolled - lastPosition: " + lastPosition);
                if (lastPosition == mLayoutManager.getItemCount() - 1) {
                    Log.d(TAG, "onScrolled - End of list?");
                    loadData();
                }
            }
        });
    }
    
    private MutableLiveData<List<Photo>> loadData() {
        Log.d(TAG, "loadData");
        if (mArticleViewModel == null) return null;
        mPageNumber += 10;
        mProgressBar.setVisibility(View.VISIBLE);
        return mArticleViewModel.loadPhotos();
    }
    
  2. ViewModel

    public class ArticleViewModel extends ViewModel {
        private MutableLiveData<List<Photo>> photos;
        private ArticleRepository articleRepository;
    
        public MutableLiveData<List<Photo>> loadPhotos() {
            Log.d(TAG, "getArticleList");
            //TODO; add Dagger 2
            articleRepository = new ArticleRepository();
            photos = articleRepository.getPhotos();
            return photos;
        }
    
  3. リポジトリ

    public class ArticleRepository {
    
        public MutableLiveData<List<Photo>> getPhotos() {
            final MutableLiveData<List<Photo>> result = new MutableLiveData<>();
            Log.d(TAG, "getResults");
            ApiService.getService().getPhotos().enqueue(new Callback<List<Photo>>() {
            @Override
            public void onResponse(Call<List<Photo>> call, Response<List<Photo>> response) {
                Log.d(TAG, "onResponse");
                if (response.isSuccessful()) {
                    Log.d(TAG, "isSuccessful");
                    result.postValue(response.body());
                }
            }
    
            @Override
            public void onFailure(Call<List<Photo>> call, Throwable t) {
                Log.d(TAG, "onFailure: " + t.getMessage() + "\n" +  t.getStackTrace());
            }
        });
        return result;
    }
    
4
sunflower20

アクティビティには、ViewModel内にあるMutablieLiveDataメンバー変数があってはなりません。

初めてしか機能しない理由は、最初に何かを観察したときに変更されたと通知するためですが、配置が正しくないため、二度と更新されません。つまり、ArticleRepositoryは新しいMutableLiveDataのセットを使用してViewModel内で再作成されるため、以前にサブスクライブしたものは関連しなくなり、onCreate()を1回だけサブスクライブします。

バインドをloadData()などの非同期タスクから分離する必要があります。これらは同じものではありません。バインディングは、MutableLiveData(loadDataで実行していること)を収集するために最初に行うことですが、一度実行した後は、再度実行しないでください。

また、実際にはモデル内にLiveDataがあることにも注意しました。パターンが壊れたり、他の問題が発生したりする可能性があるため、この方法で行うことはお勧めしません。プレゼンテーションを準備するのはViewModelであり、リポジトリではありません。現在設定しているので、リポジトリはViewModelと呼ばれることもあります。代わりに、オブザーバブルを使用して、発生した可能性のあるエラーを投稿または処理するための新しいバッチをViewModelに通知する必要があります。

この例を調べてください: https://developer.Android.com/topic/libraries/architecture/viewmodel

loadUsers()は、getUsers()が呼び出されたときに1回実行されることに注意してください。これは、アクティビティをViewModelにバインドするものです。ただし、loadUsers()は後で再度実行でき、ViewModel内のLiveDataへの変更を投稿する必要があります。

5
Tiago Redaelli

写真を追加するたびにnew MutableLiveData()を実行するのではなく、単一のpostValueオブジェクトでMutableLiveDataを呼び出します。以下のサンプルコード:

ViewModel:

public class ArticleViewModel extends ViewModel {
    private MutableLiveData<List<Photo>> photos;

    public MutableLiveData<List<Photo>> getPhotoList() {
        Log.d(TAG, "loadData");
        if (photos == null) {
            photos = new MutableLiveData<>();
            loadPhotos();
        }
        return photos;
    }

    public void loadPhotos() {
        Log.d(TAG, "getArticleList");

        ApiService.getService().getPhotos().enqueue(new Callback<List<Photo>>() {
            @Override
            public void onResponse(Call<List<Photo>> call, Response<List<Photo>> response) {
                Log.d(TAG, "onResponse");
                if (response.isSuccessful()) {
                    Log.d(TAG, "isSuccessful");
                    List<Photo> morePhotos = response.body();
                    List<Photo> allPhotos = new ArrayList<>();
                    allPhotos.addAll(photos.getValue());
                    allPhotos.addAll(morePhotos);
                    photos.postValue(allPhotos);   // <--- post change here
                }
            }

            @Override
            public void onFailure(Call<List<Photo>> call, Throwable t) {
                Log.d(TAG, "onFailure: " + t.getMessage() + "\n" +  t.getStackTrace());
            }
        });
    }
}

主な活動:

public class MainActivity extends AppCompatActivity 
    private ArticleViewModel mArticalViewModel;

    // some code...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mArticalViewModel = ViewModelProviders.of(this).get(ArticleViewModel.class);
        mArticleViewModel.getPhotoList().observe(this, photos -> {
            Log.d(TAG, "onChanged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        mProgressBar.setVisibility(View.GONE);
        mPhotos = photos;
        if (mIsInitialCall) {
            initiateAdapter();
            mIsInitialCall = false;
        } else {
            mAdapter.updateList(mPhotos.subList(mPageNumber, mPageNumber + 10));
        }
    });

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            int lastPosition = 
mLayoutManager.findLastCompletelyVisibleItemPosition();
            Log.d(TAG, "onScrolled - lastPosition: " + lastPosition);
            if (lastPosition == mLayoutManager.getItemCount() - 1) {
                Log.d(TAG, "onScrolled - End of list?");
                mArticleViewModel.loadPhotos(); // <--- changed here
            }
        }
    });
}
4
Hong Duan