web-dev-qa-db-ja.com

ViewModelとLiveDataを使用してレトロフィットAPI呼び出しを行う方法

mVVMアーキテクチャを実装しようとするのはこれが初めてであり、API呼び出しを行う正しい方法について少し混乱しています。

現在、IGDB APIから簡単なクエリを作成し、ログの最初のアイテムの名前を出力しようとしています。

私の活動は次のように設定されています:

public class PopularGamesActivity extends AppCompatActivity {

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


        PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
        popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
            @Override
            public void onChanged(@Nullable List<Game> gameList) {
                String firstName = gameList.get(0).getName();
                Timber.d(firstName);
            }
        });
    }
}

私のビューモデルは次のように設定されています。

public class PopularGamesViewModel extends AndroidViewModel {

    private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
    private static final String FIELDS = "id,name,genres,cover,popularity";
    private static final String ORDER = "popularity:desc";
    private static final int LIMIT = 30;

    private LiveData<List<Game>> mGameList;

    public PopularGamesViewModel(@NonNull Application application) {
        super(application);


        // Create the retrofit builder
        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(igdbBaseUrl)
                .addConverterFactory(GsonConverterFactory.create());

        // Build retrofit
        Retrofit retrofit = builder.build();

        // Create the retrofit client
        RetrofitClient client = retrofit.create(RetrofitClient.class);
        Call<LiveData<List<Game>>> call = client.getGame(FIELDS,
                ORDER,
                LIMIT);

        call.enqueue(new Callback<LiveData<List<Game>>>() {
            @Override
            public void onResponse(Call<LiveData<List<Game>>> call, Response<LiveData<List<Game>>> response) {
                if (response.body() != null) {
                    Timber.d("Call response body not null");
                    mGameList = response.body();

                } else {
                    Timber.d("Call response body is null");
                }
            }

            @Override
            public void onFailure(Call<LiveData<List<Game>>> call, Throwable t) {
                Timber.d("Retrofit call failed");
            }
        });

    }

    public LiveData<List<Game>> getGameList() {
        return mGameList;
    }

問題は、これがAPI呼び出しであるためです。call.enqueueが値を返すまで、mGameListの初期値はnullになります。これにより、nullポインタ例外が発生します。

popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
  1. API呼び出しが行われている間にLiveDataの観測を処理する正しい方法は何ですか?
  2. Retrofit API呼び出しを適切な場所で実行しましたか?
16
Chao Li

コードには3つの問題があります。

  1. API呼び出しの前に空の応答があるため、MutableLiveDataオブジェクトを作成する必要があります。その後、LiveDataオブジェクトは、何らかの形でIGDB応答を介して入力されます。
private MutableLiveData<List<Game>> mGameList = new MutableLiveData();
//...
public LiveData<List<Game>> getGameList() {
    return mGameList;
}
  1. 別の間違いは、値を設定する代わりにmGameListの参照を変更することです。そのため、変更を試みてください。
Timber.d("Call response body not null");
mGameList = response.body();

mGameList.setValue(response.body());
  1. ViewModelクラス内でレトロフィットを呼び出すことにより、懸念の分離を回避できます。リポジトリモジュールを作成し、インターフェイスを介して応答を取得することをお勧めします。詳細については、こちらをお読みください 記事 .

リポジトリモジュールは、データ操作の処理を担当します。アプリの残りの部分にクリーンなAPIを提供します。どこからデータを取得するか、データが更新されたときにどのAPI呼び出しを行うかを知っています。異なるデータソース(永続モデル、Webサービス、キャッシュなど)間のメディエーターと見なすことができます。

15
Saeed Masoumi

Googleのデモに触発され、RetrofitのLiveDataサポートを追加できるライブラリを作成しました。使い方は簡単です:

Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(LiveDataCallAdapterFactory())
            .build()
            .create(GithubService::class.Java)
            .getUser("shawnlinboy").observe(this,
                Observer { response ->
                    when (response) {
                        is ApiSuccessResponse -> {
                          //success response
                        }
                        else -> {
                            //failed response
                        }
                    }
                })

ライブラリサイト: https://github.com/shawnlinboy/retrofit-livedata-adapter

0
Shawn Lin