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

Posted

技术标签:

【中文标题】如何使用 ViewModel 和 LiveData 进行改造 API 调用【英文标题】:How to make retrofit API call using ViewModel and LiveData 【发布时间】:2019-01-17 17:25:57 【问题描述】:

这是我第一次尝试实现 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>>() 
    那么处理 LiveData 观察的正确方法是什么, 在进行 API 调用时? 我是否执行了 Retrofit API 调用 在正确的地方?

【问题讨论】:

【参考方案1】:

您的代码中有 3 个问题。

    您必须创建一个 MutableLiveData 对象,因为在 API 调用之前您有一个空响应,然后您的 LiveData 对象将通过 IGDB 响应以某种方式填充。
private MutableLiveData<List<Game>> mGameList = new MutableLiveData();
//...
public LiveData<List<Game>> getGameList() 
    return mGameList;

    另一个错误是更改mGameList的引用而不是设置它的值,所以尝试更改:
Timber.d("Call response body not null");
mGameList = response.body();

mGameList.setValue(response.body());
    ViewModel 类中调用改造可以避免关注点分离。最好创建一个存储库模块并通过界面获取您的响应。阅读此article 了解详情。

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

【讨论】:

感谢您的回复。在意识到您的回复之前,我实际上已经注意到了我的错误 1 ​​和 2,所以我解决了这个问题。谢谢指出我的错误3,我去加个仓库。 我想很多人都有同样的问题,你救了我的命,谢谢。 我如何从View 知道存储库数据请求何时完成。【参考方案2】:

我刚刚受到 Google Demo 的启发,创建了一个库,可以为 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

【讨论】:

【参考方案3】:

// 改造示例界面

public interface GitHubService 
    @GET("users/user/repos")
    Call<List<String>> listRepos(@Path("user") String user);

// ViewModel 类

public static class MainActivityViewModel extends AndroidViewModel 
    final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    final GitHubService gitHubService = retrofit.create(GitHubService.class);

    // binding to EditText in DataBinding layout
    public final MutableLiveData<String> userName = new MutableLiveData<>();

    // binding to ListView in DataBinding layout
    public final LiveData<List<String>> listLiveData = Transformations.switchMap(userName, (s) -> new MutableLiveData<List<String>>() 
        
            if (!TextUtils.isEmpty(s)) 
                gitHubService.listRepos(s).enqueue(new Callback<List<String>>() 
                    @Override
                    public void onResponse(Call<List<String>> call, Response<List<String>> response) 
                        setValue(response.body());
                    

                    @Override
                    public void onFailure(Call<List<String>> call, Throwable t) 

                    
                );
            
        
    );

    public MainActivityViewModel(@NonNull Application application, SavedStateHandle savedStateHandle) 
        super(application);
    

【讨论】:

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

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

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

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

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

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

MVVM 架构,ViewModel和LiveData