MutableLiveData не возвращает объект HashMap после того, как Retrofit завершит загрузку моих данных и обновит HashMap

Итак, недавно я изучил Retrofit и пытался создать приложение, которое использует API для загрузки сведений о покемонах. Вот код для класса MainActivity.java и класса MainActivityViewModel.java. Там есть и другие классы, но в основном это классы моделей, поэтому я их не упомянул:

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.widget.Toast;

import com.arpansircar.java.pokemonapplication.R;
import com.arpansircar.java.pokemonapplication.viewmodel.MainActivityViewModel;

public class MainActivity extends AppCompatActivity {

    private MainActivityViewModel mainActivityViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mainActivityViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class);
    }

    @Override
    protected void onResume() {
        super.onResume();
        observeRetrofitFailures();

        mainActivityViewModel.downloadMainPokemonData();
        mainActivityViewModel.returnMainDownloadedData().observe(this, resultsPokemonServiceModels ->
                mainActivityViewModel.downloadSelectedPokemonData(resultsPokemonServiceModels));

        mainActivityViewModel.returnDownloadedPokemonData().observe(this, map -> {

        });
    }

    private void observeRetrofitFailures() {
        mainActivityViewModel.returnMainServiceErrorLiveData().observe(this, s ->
                Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show());

        mainActivityViewModel.returnSelectedPokemonServiceErrorLiveData().observe(this, s ->
                Toast.makeText(this, s, Toast.LENGTH_SHORT).show());
    }
}

MainActivityViewModel.java

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.arpansircar.java.pokemonapplication.model.GetPokemonId;
import com.arpansircar.java.pokemonapplication.model.MainPokemonServiceModel;
import com.arpansircar.java.pokemonapplication.model.ResultsPokemonServiceModel;
import com.arpansircar.java.pokemonapplication.model.SelectedPokemonModel;
import com.arpansircar.java.pokemonapplication.retrofit.MainPokemonRetrofitInstance;
import com.arpansircar.java.pokemonapplication.retrofit.PokemonServiceInterface;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.internal.EverythingIsNonNull;

public class MainActivityViewModel extends ViewModel {

    private MainPokemonServiceModel mainPokemonServiceModel;
    private final Map<String, String> pokemonHashMap = new HashMap<>();
    private final PokemonServiceInterface mainPokemonServiceInterface = MainPokemonRetrofitInstance.getInstance();

    private final MutableLiveData<String> mainServiceErrorLiveData = new MutableLiveData<>();
    private final MutableLiveData<String> selectedPokemonServiceErrorLiveData = new MutableLiveData<>();
    private final MutableLiveData<Map<String, String>> downloadMainPokemonData = new MutableLiveData<>();
    private final MutableLiveData<List<ResultsPokemonServiceModel>> resultsPokemonServiceModelListLiveData = new MutableLiveData<>();

    public void downloadMainPokemonData() {
        Call<MainPokemonServiceModel> mainPokemonServiceModelCall = mainPokemonServiceInterface.getResultsDataFromService();
        mainPokemonServiceModelCall.enqueue(new Callback<MainPokemonServiceModel>() {

            @Override
            @EverythingIsNonNull
            public void onResponse(Call<MainPokemonServiceModel> call, Response<MainPokemonServiceModel> response) {
                if (!response.isSuccessful()) {
                    mainServiceErrorLiveData.postValue(String.valueOf(response.code()));
                    return;
                }

                mainPokemonServiceModel = response.body();
                resultsPokemonServiceModelListLiveData.postValue(Objects.requireNonNull(mainPokemonServiceModel).resultsPokemonServiceModel);
            }

            @Override
            @EverythingIsNonNull
            public void onFailure(Call<MainPokemonServiceModel> call, Throwable t) {
                mainServiceErrorLiveData.postValue(t.getMessage());
            }
        });
    }

    public void downloadSelectedPokemonData(List<ResultsPokemonServiceModel> resultsPokemonServiceModelList) {
        for (ResultsPokemonServiceModel resultsPokemonServiceModel : resultsPokemonServiceModelList) {
            String pokemonId = GetPokemonId.getId(resultsPokemonServiceModel.url);
            Call<SelectedPokemonModel> selectedPokemonModelCall = MainPokemonRetrofitInstance.getInstance().getSelectedPokemonSprite(pokemonId);

            selectedPokemonModelCall.enqueue(new Callback<SelectedPokemonModel>() {
                @Override
                @EverythingIsNonNull
                public void onResponse(Call<SelectedPokemonModel> call, Response<SelectedPokemonModel> response) {
                    if (!response.isSuccessful()) {
                        selectedPokemonServiceErrorLiveData.postValue(String.valueOf(response.code()));
                        return;
                    }

                    SelectedPokemonModel selectedPokemonModel = response.body();
                    String pokemonName = Objects.requireNonNull(selectedPokemonModel).pokemonName;
                    String pokemonSpriteUrl = selectedPokemonModel.pokemonSpriteModel.spriteUrl;
                    pokemonHashMap.put(pokemonName, pokemonSpriteUrl);
                }

                @Override
                @EverythingIsNonNull
                public void onFailure(Call<SelectedPokemonModel> call, Throwable t) {
                    selectedPokemonServiceErrorLiveData.postValue(t.getMessage());
                }
            });
        }

        downloadMainPokemonData.postValue(pokemonHashMap);
    }

    public LiveData<Map<String, String>> returnDownloadedPokemonData() {
        return downloadMainPokemonData;
    }

    public LiveData<List<ResultsPokemonServiceModel>> returnMainDownloadedData() {
        return resultsPokemonServiceModelListLiveData;
    }

    public LiveData<String> returnMainServiceErrorLiveData() {
        return mainServiceErrorLiveData;
    }

    public LiveData<String> returnSelectedPokemonServiceErrorLiveData() {
        return selectedPokemonServiceErrorLiveData;
    }

}

Моя главная проблема на линии downloadMainPokemonData.postValue(pokemonHashMap);. Когда загрузка заканчивается, объект MutableLiveData (по какой-то причине) не уведомляется об этом, т. е. HashMap не возвращается обратно в MainActivity.

Я проверил несколько журналов и обнаружил, что метод действительно запускается. Однако система выходит из метода еще до завершения загрузки. Загрузка завершается, да. Но по какой-то причине он не возвращает загруженный HashMap обратно в MainActivity. Я думаю, что это может быть связано с тем, что метод постановки в очередь, предоставляемый Retrofit, является асинхронным и выполняет все задачи в фоновом потоке.

Может ли кто-нибудь дать какие-либо намеки на то, как я могу позаботиться об этом? Спасибо!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
582
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

selectedPokemonModelCall.enqueue — асинхронный запрос. downloadMainPokemonData.postValue(pokemonHashMap) вызывается перед обработкой Response<SelectedPokemonModel> данных

Где вы рекомендуете разместить код загрузкиMainPokemonData.postValue(...), чтобы данные обрабатывались в нужное время?

Arpan Sircar 16.12.2020 08:45

@ArpanSircar, вы можете использовать его внутри public void onResponse, но это внутри цикла, поэтому вы получите много бесполезных значений. Я бы рекомендовал использовать синхронный вызов selectedPokemonModelCall.execute(), и когда цикл достигает конца, вы получаете данные одним куском. И не забудьте использовать Thread. Для получения дополнительной информации посетите howtodoinjava.com/retrofit2/retrofit-sync-async-calls

Stanislav Bondar 16.12.2020 08:52

Это может потребовать многого, но есть ли способ, которым я могу использовать метод enqueue и получить данные одним куском в конце? Я действительно не научился делать асинхронные вызовы. И я попробовал метод execute без многопоточности, но он возвращает NetworkOnMainThreadException.

Arpan Sircar 16.12.2020 09:14

@ArpanSircar, вот почему я сказал - не забудьте использовать Thread. Оберните foreach с помощью Thread thread = new Thread(){ public void run(){ for(...){....} downloadMainPokemonData.postValue(pokemonHashMap) } } thread.start();

Stanislav Bondar 16.12.2020 09:28

Другие вопросы по теме