Leonardo Dias
Leonardo Dias

Reputation: 345

Observer for MutableLiveData not being triggered

I am developing an activity using MutableLiveData to keep my fragments up-to-date with the results retrieved from a search, but the Observer is not being triggered after the list of results is updated.

I have, on the main activity, an EditText where I type what I want to search and, right below it, a ViewPager widget with three options, to where the results are subdivided into three categories (on three different fragments). When I first enter the activity, I run an initial search to retrieve data and populate the three fragments. This works perfectly.

SearchActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);

        mViewModel = ViewModelProviders.of(this).get(SearchViewModel.class);

        /* Init TabMenu */
        TabLayout tabSearchMenu = findViewById(R.id.tabSearchMenu);
        ViewPager vpSearch = findViewById(R.id.vpSearchResult);

        TabSearchPageAdapter tabSearchPageAdapter = new TabSearchPageAdapter(getSupportFragmentManager(), tabSearchMenu.getTabCount());
        vpSearch.setAdapter(tabSearchPageAdapter);
        vpSearch.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabSearchMenu));


        /* Init Search */
        etxtSearch = findViewById(R.id.etxtSearch);
        etxtSearch.setOnEditorActionListener((textView, actionId, keyEvent) -> {
            if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE
                    || keyEvent.getAction() == KeyEvent.ACTION_DOWN || keyEvent.getAction() == KeyEvent.KEYCODE_ENTER) {

                mViewModel.searchText(etxtSearch.getText().toString());
            }

            return false;
        });
    }

SearchViewModel.java

public class SearchViewModel extends ViewModel {
    private MutableLiveData<List<ProductItemModel>> listProducts = new MutableLiveData<>(new ArrayList<>());
    private List<ProductItemModel> mProducts = new ArrayList<>();

    [...]

    public void searchText(String text) {
        // make search... 
        // ...
        // ...

        // Process Json result.
        CompletionHandler completionHandler = (content, error) -> {
            if (content != null && content.length() > 0) {
                Log.d(TAG, content.toString());
                Log.d(TAG, "searchText: found result.");

                listProducts = new MutableLiveData<>(new ArrayList<>());

                mProducts = new ArrayList<>();

                try {
                    JSONArray products = content.getJSONArray("hits");

                    for (int i = 0; i < products.length(); i++) {
                        JSONObject prodObject = products.getJSONObject(i);
                        ProductItemModel prodItem = new ProductItemModel(prodObject);

                        mProducts.add(prodItem);

                        Log.d(TAG, "searchText: " + prodObject.toString());
                    }

                    listProducts.setValue(mProducts);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                Log.d(TAG, "searchText: no hits");
            }
        };
    }

    [...]

    MutableLiveData<List<ProductItemModel>> getProductList() {
        if (listProducts == null)
            return new MutableLiveData<>(new ArrayList<>());

        return listProducts;
    }
}

SearchProductFragment

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        InitFragment();
    }

    [...]

    private void InitFragment() {
        mViewModel = ViewModelProviders.of(Objects.requireNonNull(getActivity())).get(SearchViewModel.class);
        mViewModel.SearchProducts();

        final Observer<List<ProductItemModel>> listObserver = result -> {
            Log.d(TAG, "listObserver: observer modified.");
            if (result.size() > 0) {
                InitProductList(result);
            }
        };

        mViewModel.getProductList().observe(getActivity(), listObserver);
    }

Already tried to move the method InitFragment() inside the fragment to the OnActivityCreated, but it didn't worked either. I know for a fact that what is not working is the observer not being triggered. I've made tests with breakpoints.

As you guys can see, when the fragment is initialized the method SearchProducts() is called and the result is sent perfectly to the MutableLiveData and the observer is triggered. But whenever I run a search calling the function searchText(), retrieve the results from it, and update the value of my variable (works perfect till this point), the observer is not triggered.

Extra info:

Any ideas?

Upvotes: 0

Views: 488

Answers (1)

user12194200
user12194200

Reputation:

The problem is you're returning different instances of MutableLiveData, but your fragment is only observing the first instance that it gets in your InitFragment() method.

You're also setting listProducts to multiple instances of MutableLiveData (first at its declaration site and again in the searchText() method) rather than just updating the value that it holds.

To elaborate, you don't need the null check in the getProductsList() method because listProducts is initialized to a new instance of MutableLiveData when the view model is created.

Now, the value held by that instance of MutableLiveData might be null.. and it is only that value which you should be updating via setValue(). Any null checks on the value of the MutableLiveData could be done in the Observer you created in your fragment.

Upvotes: 2

Related Questions