Метод наблюдателя ViewModel возвращает ноль - PullRequest
0 голосов
/ 07 ноября 2018

Я получаю следующую ошибку

Attempt to invoke virtual method 'void android.arch.lifecycle.LiveData.observe on a null object reference

Из этой следующей части кода в моем основном фрагменте

    mReleasesViewModel = ViewModelProviders.of(this).get(ReleasesViewModel.class);
    mReleasesViewModel.getUpcomingReleases(filter).observe(this, new Observer<List<_Release>>() {
        @Override
        public void onChanged(@Nullable List<_Release> releases) {
            // whenever the list is changed
            if (releases != null) {
                mUpcomingGamesAdapter.setData(releases);
                mUpcomingGamesAdapter.notifyDataSetChanged();
            }
            mDatabaseLoading.setVisibility(View.GONE);
        }
    }); 

Ошибка специально выдается в этой строке (когда я прикрепляю наблюдателя)

mReleasesViewModel.getUpcomingReleases(filter) ...

Мой класс ViewModel:

public class ReleasesViewModel extends ViewModel {
    // fragment name and list
    private HashMap<String, MutableLiveData<List<_Release>>> upcomingReleasesListMap = new HashMap<>();

    private ReleasesRepository releasesRepository;
    private ArrayList<Integer> platforms;
    private String region;

    public ReleasesViewModel() {
        // Shared to all fragments : User settings region & platforms
        region = SharedPrefManager.read(SharedPrefManager.KEY_PREF_REGION, "North America");
        Set<String> defaultPlatformsSet = new HashSet<>();
        platforms = SharedPrefManager.read(SharedPrefManager.PLATFORM_IDS, defaultPlatformsSet);
    }

    public MutableLiveData<List<_Release>> getUpcomingReleases(String filter) {
        // ReleasesRepository takes a different monthly filter
        releasesRepository = new ReleasesRepository(region, filter, platforms);

        if (upcomingReleasesListMap.containsKey(filter)) {
            // Double check if it isn't null, just in case
            if (upcomingReleasesListMap.get(filter) == null) {
                // if null; try again to send a new request
                loadReleases(filter);
            } // else just don't do anything, the list is already in the Map
        } else {
            // Load it in if this filter was never added to the map [New filter and new list]
            loadReleases(filter);
        }
        return upcomingReleasesListMap.get(filter);
    }


    private void loadReleases(final String filter) {
        releasesRepository.addListener(new FirebaseDatabaseRepository.FirebaseDatabaseRepositoryCallback<_Release>() {
            @Override
            public void onSuccess(List<_Release> result) {
                // sort by release date
                if (platforms.size() > 1) {
                    // Will only sort for multiple platforms filter
                    Collections.sort(result);
                }
                MutableLiveData<List<_Release>> releases = new MutableLiveData<>();
                releases.setValue(result);
                upcomingReleasesListMap.put(filter, releases);
            }

            @Override
            public void onError(Exception e) {
                // Log.e(TAG, e.getMessage());
                MutableLiveData<List<_Release>> releases = new MutableLiveData<>();
                releases.setValue(null);
                upcomingReleasesListMap.put(filter, releases);
            }
        });
    }
}

Hashmap является неотъемлемой частью того, как я буду передавать данные своим фрагментам. Из-за того, что я теперь поддерживаю 6 фрагментов в своем приложении, мне нужно использовать одну ViewModel для всех шести фрагментов. Каждый из фрагментов имеет вид повторного использования с данными одного и того же типа (объекты и представления), но они применяют различные фильтры к этим данным. Обратите внимание на параметр String filter во ViewModel, этот параметр применяет фильтр к данным. И, конечно, я поддерживаю все 6 фрагментов в ViewModel. потому что все они нуждаются в обновлении одновременно (когда пользователь выполняет действие, такое как изменение списка platforms)

1 Ответ

0 голосов
/ 07 ноября 2018

У вас есть асинхронный код, который вы не учитывали при реализации этих методов, и в результате вы получаете нулевые LiveData, для которых вы вызываете наблюдаем (), вызывая исключение. Поток, который вы используете в данный момент:

  • вызов getUpcomingReleases () с неизвестным фильтром
  • этого фильтра нет на карте, поэтому вы вызываете loadReleases ()
  • loadReleases () просто устанавливает прослушиватель для фоновой операции (firebase) и возвращает немедленно
  • вы попадаете на строку return upcomingReleasesListMap.get (filter); , которая будет возвращать значение null, поскольку прослушиватель firebase, скорее всего, не завершен, и вы ничего не поместите в карту для этого значения фильтра
  • использовать нулевые LiveData и потерпеть неудачу

Вам понадобится что-то вроде приведенного ниже кода, чтобы оно заработало:

public MutableLiveData<List<_Release>> getUpcomingReleases(String filter) {
   ...
   if (upcomingReleasesListMap.get(filter) == null) {
       // we don't have a mapping for this filter so create one in the map
       MutableLiveData<List<_Release>> releases = new MutableLiveData<>();
       upcomingReleasesListMap.put(filter, releases);  
       // also call this method to update the LiveData
       loadReleases(filter);
    }
    // for now just return the empty LiveData so our ui can use it
    // when the firebase listener returns we will update it
    return upcomingReleasesListMap.get(filter);
}

private void loadReleases(final String filter) {
    releasesRepository.addListener(new FirebaseDatabaseRepository.FirebaseDatabaseRepositoryCallback<_Release>() {
        @Override
        public void onSuccess(List<_Release> result) {
            // sort by release date
            if (platforms.size() > 1) {
                // Will only sort for multiple platforms filter
                Collections.sort(result);
            }
            // just use the previous created LiveData, this time with the data we got 
            MutableLiveData<List<_Release>> releases = upcomingReleasesListMap.get(filter);
            releases.setValue(result);
        }

        @Override
        public void onError(Exception e) {
            // Log.e(TAG, e.getMessage());
            MutableLiveData<List<_Release>> releases = upcomingReleasesListMap.get(filter);
            releases.setValue(null);                
        }
    });
}
...