如何使用ViewModel和LiveData进行改进API调用

Posted

tags:

篇首语:本文由小常识网(cha138.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调用,mGameList的初始值将为null,直到call.enqueue返回一个值。这将导致空指针异常

popViewModel.getGameList().observe(this, new Observer<List<Game>>() 
  1. 那么,在进行API调用时,处理LiveData观察的正确方法是什么?
  2. 我是否在正确的位置执行了Retrofit API调用?
答案

您的代码中有3个问题。

  1. 您必须创建一个MutableLiveData对象,因为在API调用之前您有一个空响应,然后您的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课程中调用改造是避免分离关注点。最好创建一个存储库模块并通过接口获得响应。请阅读此article了解详情。

存储库模块负责处理数据操作。它们为应用程序的其余部分提供了一个干净的API。他们知道从何处获取数据以及在更新数据时要进行的API调用。您可以将它们视为不同数据源(持久模型,Web服务,缓存等)之间的中介。

以上是关于如何使用ViewModel和LiveData进行改进API调用的主要内容,如果未能解决你的问题,请参考以下文章

如何将editText值传递给viewModel和Livedata(Kotlin)

使用 livedata 和 ViewModel 根据现有数据查找数据

Android ViewModel+LiveData实现Fragment间通信详解

JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )

JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )

使用 ViewModel 和 LiveData 多次改造执行 API