Как связать Retrofit и Repository / ViewModel для шаблона MVVM? - PullRequest
1 голос
/ 27 апреля 2019

У меня проблемы с подключением Retrofit к архитектуре MVVM.Действительно, после прочтения документации, в которой говорится только о Room, предназначенной для локальной базы данных SQLite, я искал то же самое, но не данные, полученные с сервера отдыха.Итак, я попытался сделать что-то похожее на это, и это не сработало: https://proandroiddev.com/mvvm-architecture-viewmodel-and-livedata-part-1-604f50cda1

У меня есть активность, которая наблюдает за ViewModel:

Код активности:

mFlightPlanViewModel = ViewModelProviders.of(this).get(FlightPlanViewModel.class);
        mFlightPlanViewModel.getFlightPlans().observe(this, (flightPlans) -> {
            Log.d(TAG, "ON_CHANGED");

            mFlightPlanAdapter.setFlightPlans(flightPlans);
        });

ViewModel:

public class FlightPlanViewModel extends AndroidViewModel {
    private static final String TAG = "FlightPlanViewModel";

    private LiveData<List<FlightPlan>> mFlightPlans;
    private FlightPlanRepository mFlightPlanRepository;

    public FlightPlanViewModel(@NonNull Application application) {
        super(application);
        Log.d(TAG, "CONSTRUCTOR");

        mFlightPlanRepository = FlightPlanRepository.getInstance();
        mFlightPlans = mFlightPlanRepository.getFlightPlans();
    }

    public LiveData<List<FlightPlan>> getFlightPlans() {
        Log.d(TAG, "GET_FLIGHT_PLANS");

        return mFlightPlans;
    }
}

ViewModel отвечает репозиторию, который использует шаблон синглтона:

public class FlightPlanRepository {
    private static final String TAG = "FlightPlanRepository";

    private static FlightPlanRepository instance;
    private RestApi mRestApi;

    private FlightPlanRepository() {
        Log.d(TAG, "CONSTRUCTOR");

        mRestApi = RestDao.getRestDao();
    }

    public static FlightPlanRepository getInstance() {
        Log.d(TAG, "GET_INSTANCE");

        if (instance == null) {
            instance = new FlightPlanRepository();
        }
        return instance;
    }

    public MutableLiveData<List<FlightPlan>> getFlightPlans() {
        Log.d(TAG, "GET_FLIGHT_PLANS");

        final MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();

        mRestApi.getFlightPlanList().enqueue(new Callback<List<FlightPlan>>() {
            @Override
            public void onResponse(Call<List<FlightPlan>> call, Response<List<FlightPlan>> response) {
                if (response.code() == 200) {
                    List<FlightPlan> temp = response.body();
                    for (FlightPlan flightPlan : temp) {
                        Log.d(TAG + "res", flightPlan.toString());
                    }
                    data.setValue(response.body());
                    Log.d(TAG + "res", response.toString());
                }
            }

            @Override
            public void onFailure(Call<List<FlightPlan>> call, Throwable t) {
                List<FlightPlan> flightPlans = new ArrayList<>();
                flightPlans.add(new FlightPlan(0, "Test", 3.551, 50.52, 3.55122, 50.52625));
                data.setValue(flightPlans);
                Log.d(TAG, t.getMessage());
            }
        });

        return data;
    }
}

В репозитории используется экземпляр Retrofit:

public class RestDao {
    private static final String BASE_URL = "http://192.168.1.78:8080";
    private static Retrofit instance;

    private static Retrofit getInstance() {
        if (instance == null) {
            instance = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return instance;
    }

    public static RestApi getRestDao() {
        return getInstance().create(RestApi.class);
    }
}

Он использует этот интерфейс:

public interface RestApi {
    @GET("/plan/list")
    Call<List<FlightPlan>> getFlightPlanList();
}

Часть кода, которая не работает:

public MutableLiveData<List<FlightPlan>> getFlightPlans() {
    Log.d(TAG, "GET_FLIGHT_PLANS");

    final MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();

    mRestApi.getFlightPlanList().enqueue(new Callback<List<FlightPlan>>() {
        @Override
        public void onResponse(Call<List<FlightPlan>> call, Response<List<FlightPlan>> response) {
            if (response.code() == 200) {
                List<FlightPlan> temp = response.body();
                for (FlightPlan flightPlan : temp) {
                    Log.d(TAG + "res", flightPlan.toString());
                }
                data.setValue(response.body());
                Log.d(TAG + "res", response.toString());
            }
        }

        @Override
        public void onFailure(Call<List<FlightPlan>> call, Throwable t) {
            List<FlightPlan> flightPlans = new ArrayList<>();
            flightPlans.add(new FlightPlan(0, "Test", 3.551, 50.52, 3.55122, 50.52625));
            data.setValue(flightPlans);
            Log.d(TAG, t.getMessage());
        }
    });

    return data;
}

Возвращает пустой список.Я думаю, что понимаю, почему: вызов метода enqueue () сделал запрос, который находится в другом потоке, поэтому здесь мы возвращаем данные, не ожидая результата.

Итак, мой вопрос, как связать Retrofit и мою ViewModel?

Ответы [ 3 ]

0 голосов
/ 28 апреля 2019

Если я это сделаю:

public LiveData<List<FlightPlan>> getFlightPlans() {
    Log.d(TAG, "GET_FLIGHT_PLANS");

    final MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();


    List<FlightPlan> flightPlans = new ArrayList<>();
    flightPlans.add(new FlightPlan(5, "Test5", 3.551, 50.52, 3.55122, 50.52625));
    data.setValue(flightPlans);
    Log.d(TAG, data.getValue().toString());


    mRestApi.getFlightPlanList().enqueue(new Callback<List<FlightPlan>>() {
        @Override
        public void onResponse(Call<List<FlightPlan>> call, Response<List<FlightPlan>> response) {
            if (response.code() == 200) {
                List<FlightPlan> temp = response.body();
                for (FlightPlan flightPlan : temp) {
                    Log.d(TAG + "res", flightPlan.toString());
                }
                data.setValue(response.body());
                Log.d(TAG + "res", response.toString());
            }
        }

        @Override
        public void onFailure(Call<List<FlightPlan>> call, Throwable t) {
            List<FlightPlan> flightPlans = new ArrayList<>();
            flightPlans.add(new FlightPlan(0, "Test", 3.551, 50.52, 3.55122, 50.52625));
            data.setValue(flightPlans);
            Log.d(TAG, t.getMessage());
        }
    });

    return data;
}

Я вижу в Logcat значение по умолчанию и два значения, которые приходят с моего сервера покоя, но строки имеют нулевые значения, а int / doubles равны 0, и этоне добавляет две остальные записи в представление переработчика.

Вот мой код активности:

public class FlightPlanActivity extends AppCompatActivity implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener, LifecycleOwner {
    private static final String TAG = "FlightPlanActivity";

    private FlightPlanAdapter mFlightPlanAdapter;
    private FlightPlanViewModel mFlightPlanViewModel;

    /**
     * UI
     */
    private FloatingActionButton mAddPlanButton;
    private RecyclerView mRecyclerView;
    private SwipeRefreshLayout mSwipeRefreshLayout;

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

        Log.d(TAG, "onCreate called");

        initUI();
    }

    private void initUI() {
        this.setTitle("Flight Plans");

        mRecyclerView = findViewById(R.id.flight_plan_list);
        mAddPlanButton = findViewById(R.id.add_flight_plan);
        mSwipeRefreshLayout = findViewById(R.id.refresh_flight_plan_list);

        mAddPlanButton.setOnClickListener(this);
        mSwipeRefreshLayout.setOnRefreshListener(this);

        mFlightPlanAdapter = new FlightPlanAdapter();

        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mFlightPlanAdapter);

        mFlightPlanViewModel = ViewModelProviders.of(this).get(FlightPlanViewModel.class);
        mFlightPlanViewModel.getFlightPlans().observe(this, (flightPlans) -> {
            Log.d(TAG, "ON_CHANGED");

            mFlightPlanAdapter.setFlightPlans(flightPlans);
        });
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == mAddPlanButton.getId()) {
            // TODO: A implémenter
        }
    }

    @Override
    public void onRefresh() {
        mFlightPlanAdapter.setFlightPlans(mFlightPlanViewModel.getFlightPlans().getValue());

        mSwipeRefreshLayout.setRefreshing(false);
    }
}
0 голосов
/ 28 апреля 2019

Итак, что произойдет, если ответ поступит, если я это сделаю?

public LiveData<List<FlightPlan>> getFlightPlans() {
    Log.d(TAG, "GET_FLIGHT_PLANS");

    MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();

    mRestApi.getFlightPlanList().enqueue(new Callback<List<FlightPlan>>() {
        @Override
        public void onResponse(Call<List<FlightPlan>> call, Response<List<FlightPlan>> response) {
            if (response.code() == 200) {
                data.setValue(response.body());
            }
        }

        @Override
        public void onFailure(Call<List<FlightPlan>> call, Throwable t) {
            // Do something
        }
    });

    return data;
}

Если вы запустите это, вначале будет возвращен нулевой объект, но если будет ответ, он что-нибудь вернет?

Я пробовал это:

public LiveData<List<FlightPlan>> getFlightPlans() {
    Log.d(TAG, "GET_FLIGHT_PLANS");

    MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();
    List<FlightPlan> flightPlans;

    try {
        Response<List<FlightPlan>> response = mRestApi.getFlightPlanList().execute();
        if (response.isSuccessful()) {
            flightPlans = response.body();
        } else {
            Log.d(TAG, "Can't get data !");
            throw new Exception("Can't get data !");
        }
    } catch (Exception e) {
        e.printStackTrace();

        flightPlans = new ArrayList<>();
        flightPlans.add(new FlightPlan(5, "Test5", 3.551, 50.52, 3.55122, 50.52625));
    }

    data.setValue(flightPlans);
    Log.d(TAG, data.getValue().toString());

    return data;
}

Теперь происходит сбой в строке выполнения, потому что я запускаю это в основном потоке .. Поэтому я должен выполнить асинхронную задачу, но где?

Другой вариант: создайте асинхронную задачу, как сначала, поместите установщик в объект представления модели, и когда есть ответ, я вызываю установщик. Это хорошо?

Спасибо за ответы, которые вы мне дали! Это было очень полезно.

РЕДАКТИРОВАТЬ: Почему этот код в документации Google:

public class UserRepository {
    private Webservice webservice;
    // ...
    public LiveData<User> getUser(int userId) {
        // This isn't an optimal implementation. We'll fix it later.
        final MutableLiveData<User> data = new MutableLiveData<>();
        webservice.getUser(userId).enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                data.setValue(response.body());
            }

            // Error case is left out for brevity.
        });
        return data;
    }
}

Это значит, что это нормально делать?

РЕШЕНИЕ!

РЕДАКТИРОВАТЬ:

Как связать ViewModel с репозиторием, чтобы данные распространялись в View (MVVM, Livedata)

Это мне очень помогает !!!

Я нашел решение! Я тупой: я вставляю старые зависимости в файл gradle!

0 голосов
/ 27 апреля 2019

Привет, Кевин, небольшое изменение в вашем коде. Вместо MutableLiveData возвращают LiveData из вашего хранилища:

public LiveData<List<FlightPlan>> getFlightPlans() {
    Log.d(TAG, "GET_FLIGHT_PLANS");

    final MutableLiveData<List<FlightPlan>> data = new MutableLiveData<>();

    mRestApi.getFlightPlanList().enqueue(new Callback<List<FlightPlan>>() {
        @Override
        public void onResponse(Call<List<FlightPlan>> call, Response<List<FlightPlan>> response) {
            if (response.code() == 200) {
                List<FlightPlan> temp = response.body();
                for (FlightPlan flightPlan : temp) {
                    Log.d(TAG + "res", flightPlan.toString());
                }
                data.postValue(response.body());
                Log.d(TAG + "res", response.toString());
            }
        }

        @Override
        public void onFailure(Call<List<FlightPlan>> call, Throwable t) {
            List<FlightPlan> flightPlans = new ArrayList<>();
            flightPlans.add(new FlightPlan(0, "Test", 3.551, 50.52, 3.55122, 50.52625));
            data.postValue(flightPlans);
            Log.d(TAG, t.getMessage());
        }
    });

    return data;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...