简体   繁体   English

Android MVVM 架构和观察来自 API 的数据变化

[英]Android MVVM architecture and observing changes on data from an API

I'm new to the Android MVVM architecture.我是 Android MVVM 架构的新手。 I have an API running locally with data ("deals") in it.我有一个本地运行的 API,其中包含数据(“交易”)。 I'd like to simply make a request to the API and display that data in a text field.我只想向 API 发出请求并在文本字段中显示该数据。 Currently the data does not show up when the fragment is first loaded, but if I go to another activity and then back to the fragment it loads.目前,首次加载片段时数据不会显示,但如果我转到另一个活动,然后返回到它加载的片段。

There are 3 classes of importance here.这里有 3 类重要性。

DashboardViewModel.java:仪表板视图模型.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:仪表板片段.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;
    }
}

and Repository.java:和 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));
                }
            }
        });

    }
}

When stepping through the code in the Repository class I can see that setDeals() is called when I load the fragment, and the request in the callback is queued.在单步执行 Repository 类中的代码时,我可以看到在加载片段时调用了setDeals() ,并且回调中的请求已排队。 The first time getDeals() returns, it returns a list of 0 deals (within the MutableLiveData object). getDeals()第一次返回时,它返回一个包含 0 个交易的列表(在MutableLiveData对象中)。

onResponse in the callback doesn't run until the fragment is already loaded.在片段已经加载之前,回调中的onResponse不会运行。 When debugging I can see that the data is in the objects (all the Gson stuff works fine), but onChanged doesn't get called again (which sets the text view).调试时我可以看到数据在对象中(所有 Gson 的东西都工作正常),但不会再次调用onChanged (设置文本视图)。

Am I not observing changes on the deals properly?我没有正确观察deals的变化吗?

I think this would help.我认为这会有所帮助。 Try postValue on MutableLiveData in onResponse of network call.在网络调用的 onResponse 中尝试 MutableLiveData 上的 postValue。 Please change your repository class like below:请像下面这样更改您的存储库类:

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;
}
}

Your code is not working due to a new live data instance be created whenever getDeals() is called and the api response value be informed to other live data instance.由于每当调用 getDeals() 时都会创建一个新的实时数据实例,并且将 api 响应值通知给其他实时数据实例,因此您的代码无法正常工作。 You must set api response value to same instance of MutableLiveData returned by getDeals()您必须将 api 响应值设置为 getDeals() 返回的相同 MutableLiveData 实例

I'm not saying that it is the best architectural solution, but if you create a mutable live data as a class attribute and return it whenever getDeals() is called.我并不是说它是最好的架构解决方案,但是如果您创建一个可变的实时数据作为类属性并在调用 getDeals() 时返回它。 Probably, it's going to work.可能,它会起作用。

Also, a good practice is return a LiveData and not a MutableLiveData to not allowing a external component modify the internal value.此外,一个好的做法是返回 LiveData 而不是 MutableLiveData 以不允许外部组件修改内部值。

Please, take a look at the piece of code below.请看一下下面的代码。

OBS: Maybe, there is some syntax error, because I have not compiled it 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 

in your repository class in function get deals.在函数中的存储库类中获取交易。 you are initializing live data.您正在初始化实时数据。 requesting url in background thread and posting value on live data which is not received from server yet.在后台线程中请求 url 并在尚未从服务器接收到的实时数据上发布值。

to solve this create livedata instance in constructor of repository and postvalue on livedata in onResponse callback.要解决此问题,请在存储库的构造函数中创建实时数据实例,并在 onResponse 回调中在实时数据上创建后值。

//sorry for bad writting, posted from mobile. //抱歉写得不好,从手机上发布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用MVVM体系结构从FireStore检索数据 - Retrieving Data from FireStore using MVVM Architecture 观察多个 LiveData 和 MVVM 结构 Android Java - Observing Multiple LiveData and MVVM Structure Android Java android 中的 MVVM 架构使用 Volley - MVVM architecture in android Using Volley 从MVVM体系结构中的存储库中插入数据后,无法切换到MainActivity - Unable to switch to MainActivity after data insertion from repository in MVVM architecture Android mvvm架构麻烦了解流程 - Android mvvm architecture trouble understanding process flow Android 上的简单 MVVM 架构与 Java 上的 AndroidX - Simple MVVM architecture on Android with AndroidX on Java 对我当前的 MVVM android 应用架构的建议 - Advice for my current MVVM android app architecture Android 应用 MVVM 架构,使用 MVVM 从 Service 写入本地 Room 数据库,Service 应该如何写入数据库? - Android application with MVVM architecture, writing to local Room database from a Service using MVVM, how should the Service write to the database? 从房间中选择数据而不观察 - Selecting Data from Room without observing StartActivityForResult 从 MVVM 架构中的图库中获取图像? - StartActivityForResult to get images from gallery in MVVM architecture?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM