【问题标题】:Android MVVM architecture and observing changes on data from an APIAndroid MVVM 架构和观察来自 API 的数据变化
【发布时间】:2019-11-30 19:21:51
【问题描述】:

我是 Android MVVM 架构的新手。我有一个在本地运行的 API,其中包含数据(“交易”)。我想简单地向 API 发出请求并在文本字段中显示该数据。目前,首次加载片段时数据不会显示,但如果我转到另一个活动然后返回它加载的片段。

这里有 3 类重要性。

DashboardViewModel.java:

package com.example.android_client.ui.dashboard;

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

import com.example.android_client.models.Deal;
import com.example.android_client.repository.Repository;

import java.util.List;

public class DashboardViewModel extends ViewModel {

    private MutableLiveData<String> mText;
    private Repository repository;
    private MutableLiveData<List<Deal>> deals = null;

    public void init() {
        if(this.deals == null) {
            this.repository = Repository.getInstance();
            this.deals = this.repository.getDeals();
        }
    }

    public DashboardViewModel() {
        this.mText = new MutableLiveData<>();
    }

    public LiveData<List<Deal>> getDeals() {
        return this.deals;
    }
}

DashboardFragment.java:

package com.example.android_client.ui.dashboard;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;

import com.example.android_client.R;
import com.example.android_client.models.Deal;

import java.util.List;

public class DashboardFragment extends Fragment {

    private DashboardViewModel dashboardViewModel;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
        final TextView textView = root.findViewById(R.id.text_dashboard);
        dashboardViewModel = ViewModelProviders.of(this).get(DashboardViewModel.class);
        dashboardViewModel.init();
        dashboardViewModel.getDeals().observe(this, new Observer<List<Deal>>() {
            @Override
            public void onChanged(List<Deal> deals) {
                if (deals != null && !deals.isEmpty()) {
                    System.out.println(deals.get(0).toString());
                    textView.setText(deals.get(0).toString());
                }
            }
        });
        return root;
    }
}

和 Repository.java:

package com.example.android_client.repository;

import androidx.lifecycle.MutableLiveData;

import com.example.android_client.models.Deal;
import com.google.gson.Gson;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class Repository {

    private static Repository instance;
    private ArrayList<Deal> dealsList = new ArrayList<>();
    private final OkHttpClient client = new OkHttpClient();

    public static Repository getInstance() {
        if(instance == null) {
            instance = new Repository();
        }
        return instance;
    }

    private Repository() {}

    public MutableLiveData<List<Deal>> getDeals() {
        setDeals();
        MutableLiveData<List<Deal>> deals = new MutableLiveData<>();
        deals.setValue(dealsList);
        return deals;
    }

    private void setDeals() {
        Request request = new Request.Builder()
                .url("http://10.0.2.2:8000/api/deals?<params here>")
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                try (ResponseBody responseBody = response.body()) {
                    if (!response.isSuccessful()) {
                        throw new IOException("Unexpected code " + response);
                    }
                    String jsonDeals = responseBody.string(); // can only call string() once or you'll get an IllegalStateException
                    Deal[] deals = new Gson().fromJson(jsonDeals, Deal[].class);
                    dealsList = new ArrayList<>(Arrays.asList(deals));
                }
            }
        });

    }
}

单步执行 Repository 类中的代码时,我可以看到在加载片段时调用了 setDeals(),并且回调中的请求已排队。 getDeals() 第一次返回时,它返回一个包含 0 个交易的列表(在 MutableLiveData 对象内)。

回调中的onResponse 直到片段已经加载后才会运行。调试时,我可以看到数据在对象中(所有 Gson 的东西都可以正常工作),但 onChanged 不会再次被调用(这会设置文本视图)。

我没有正确观察deals 的变化吗?

【问题讨论】:

    标签: java android mvvm okhttp android-mvvm


    【解决方案1】:

    您的代码不起作用,因为每当调用 getDeals() 时都会创建一个新的实时数据实例,并将 api 响应值通知给其他实时数据实例。您必须将 api 响应值设置为 getDeals() 返回的同一 MutableLiveData 实例

    我并不是说它是最好的架构解决方案,但如果您创建一个可变的实时数据作为类属性并在调用 getDeals() 时返回它。可能,它会起作用。

    此外,一个好的做法是返回 LiveData 而不是 MutableLiveData 以不允许外部组件修改内部值。

    请看下面的代码。

    OBS: 可能有语法错误,因为我没有编译过

        import com.example.android_client.models.Deal;
        import com.google.gson.Gson;
        import org.jetbrains.annotations.NotNull;
        import java.io.IOException;
        import java.util.ArrayList;
        import java.util.Arrays;
        import java.util.List;
    
        import okhttp3.Call;
        import okhttp3.Callback;
        import okhttp3.OkHttpClient;
        import okhttp3.Request;
        import okhttp3.Response;
        import okhttp3.ResponseBody;
    
            public class Repository {
    
            private static Repository instance;
            private ArrayList<Deal> dealsList = new ArrayList<>();
            private final OkHttpClient client = new OkHttpClient();
            private MutableLiveData<List<Deal>> _deals = new MutableLiveData<>();                         
            private LiveData<List<Deal>> deals = _deals
    
    
            public static Repository getInstance() {
                if(instance == null) {
                    instance = new Repository();
                }
                return instance;
            }
    
            private Repository() {}
    
            public LiveData<List<Deal>> getDeals() {
                setDeals();
                return deals;
            }
    
            private void setDeals() {
                Request request = new Request.Builder()
                        .url("http://10.0.2.2:8000/api/deals?<params here>")
                        .build();
    
                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        e.printStackTrace();
                    }
    
                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        try (ResponseBody responseBody = response.body()) {
                            if (!response.isSuccessful()) {
                                throw new IOException("Unexpected code " + response);
                            }
                            String jsonDeals = responseBody.string(); // can only call string() once or you'll get an IllegalStateException
                            Deal[] deals = new Gson().fromJson(jsonDeals, Deal[].class);
                            dealsList = new ArrayList<>(Arrays.asList(deals));
                            _deals.setValue(dealsList);
    
                        }
                    }
                });
    
            }
    
    }
    When 
    

    【讨论】:

    • 糟糕!是的你是对的。如果我只是在getDeals 中取出MutableLiveData&lt;List&lt;Deal&gt;&gt; deals = new MutableLiveData&lt;&gt;();,它就可以工作。谢谢你。还要感谢有关何时使用 LiveData 的提示
    【解决方案2】:

    我认为这会有所帮助。在网络调用的 onResponse 中尝试 MutableLiveData 上的 postValue。请更改您的存储库类,如下所示:

    package com.example.android_client.repository;
    import androidx.lifecycle.MutableLiveData;
    
    import com.example.android_client.models.Deal;
    import com.google.gson.Gson;
    
    import org.jetbrains.annotations.NotNull;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import okhttp3.ResponseBody;
    
    public class Repository {
    
    private static Repository instance;
    private ArrayList<Deal> dealsList = new ArrayList<>();
    private final OkHttpClient client = new OkHttpClient();
    MutableLiveData<List<Deal>> deals = new MutableLiveData<>();
    
    public static Repository getInstance() {
        if(instance == null) {
            instance = new Repository();
        }
        return instance;
    }
    
    private Repository() {}
    
    private MutableLiveData<List<Deal>> getDeals() {
        Request request = new Request.Builder()
                .url("http://10.0.2.2:8000/api/deals?<params here>")
                .build();
    
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                e.printStackTrace();
            }
    
            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                try (ResponseBody responseBody = response.body()) {
                    if (!response.isSuccessful()) {
                        throw new IOException("Unexpected code " + response);
                    }
                    String jsonDeals = responseBody.string(); // can only call string() once or you'll get an IllegalStateException
                    Deal[] deals = new Gson().fromJson(jsonDeals, Deal[].class);
                    dealsList = new ArrayList<>(Arrays.asList(deals));
                    deals.postValue(dealsList);
                }
            }
        });
     return deals;
    }
    }
    

    【讨论】:

      【解决方案3】:

      在您的存储库类中获取交易功能。您正在初始化实时数据。在后台线程中请求 url 并在尚未从服务器接收到的实时数据上发布值。

      为了解决这个问题,在存储库的构造函数中创建 livedata 实例,并在 onResponse 回调中在 livedata 上创建 postvalue。

      //抱歉文笔不好,从手机发帖。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-01
        • 2020-07-20
        • 1970-01-01
        • 2012-02-22
        相关资源
        最近更新 更多