在android中使用DataBinding调用API后,当仓库中的MutableLiveData发生变化时,如何更新View?

问题描述 投票:0回答:1

我想显示一个设备的信息列表。首先,我调用API来获取设备列表,然后在onResponse函数中获取每个设备的信息。然后,在onResponse函数中,我为每个设备调用另一个API来获取每个设备的数据。但是这些数据并没有在View中更新。视图只是显示一个设备列表,不能显示每个设备的数据。我想是observisor在更新设备数据的时候没有捕捉到事件。

我的设备类。

public class Device {

    @SerializedName("Altitude")
    private Double altitude;
    @SerializedName("Latitude")
    private Double latitude;
    @SerializedName("Longtitude")
    private Double longitude;
    @SerializedName("NodeId")
    private String nodeId;
    @SerializedName("ReverseGeocode")
    private String reverseGeocode;
    @SerializedName("Title")
    private Integer title;

    @Expose
    private List<Data> data = new ArrayList<>();
}

我的仓库

public class DeviceRepository {

    DeviceApi deviceApi;
    MutableLiveData<List<Device>> mutableLiveData = new MutableLiveData<>();

    //some code to init...

    //get devices
    public MutableLiveData<List<Device>> getDevices() {
        final List<Device> devices = new ArrayList<>();
        Call<List<Device>> call = deviceApi.getDevices();
        call.enqueue(new Callback<List<Device>>() {
            @Override
            public void onResponse(Call<List<Device>> call, Response<List<Device>> response) {
                if(response.body() != null) {
                    for (Device device: response.body()) {
                        devices.add(device);
                    }
                    mutableLiveData.setValue(devices);
                    //set data for device by call API
                    for(int i = 0; i<mutableLiveData.getValue().size(); i++){
                        DeviceRepository.this.getDataOfDevice(mutableLiveData.getValue().get(i));
                    }
                }
            }

            @Override
            public void onFailure(Call<List<Device>> call, Throwable t) {
            }
        });
        return mutableLiveData;
    }
    //call API to get Data
    public void getDataOfDevice(Device device) {
        final List<Data> data = new ArrayList<>();
        Call<List<Data>> call = deviceApi.getDataByNodeId(device.getNodeId());
        call.enqueue(new Callback<List<Data>>() {
            @Override
            public void onResponse(Call<List<Data>> call, Response<List<Data>> response) {
                if(response.body() != null && response.body().size()>0) {
                    data.add(response.body().get(0));
                    device.setData(data);
                }
            }

            @Override
            public void onFailure(Call<List<Data>> call, Throwable t) {
                System.out.println("fail");
            }
        });
    }
}

这是我的ViewModel

public class DeviceViewModel extends ViewModel {
    private MutableLiveData<List<Device>> devices;
    private DeviceRepository deviceRepository = new DeviceRepository();

    public MutableLiveData<List<Device>> getDevices() {
        devices = deviceRepository.getDevices();
        return devices;
    }
}

这是Fragment:

public class DeviceFragment extends Fragment{

    private DeviceViewModel deviceViewModel;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        DeviceFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.device_fragment, container, false);
        binding.setLifecycleOwner(this);
        View root = inflater.inflate(R.layout.device_fragment, container, false);
        deviceViewModel = new ViewModelProvider(this).get(DeviceViewModel.class);
        final ListView listView = root.findViewById(R.id.list);
        deviceViewModel.getDevices().observe(getViewLifecycleOwner(), devices -> {
            final List<String> list = new ArrayList<>();
            for (Device device: devices) {
                String info = device.getData().size() > 0 ? device.getData().get(0).getTime().toString() : "no data";
                list.add(device.getNodeId() + "-"+info);
            }
            ArrayAdapter<String> itemsAdapter =
                    new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, list);
            listView.setAdapter(itemsAdapter);
        });

        return root;
    }
}

还有device_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.Device.DeviceFragment">

        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/list">
        </ListView>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
android android-databinding
1个回答
0
投票

你的问题出在LiveData的管理和异步操作上。

  1. 所有Retrofit的 "enqueue "都是异步的,每个结果你都可以在回调中得到--onResponse。
  2. 比方说,你的第一个响应将在1秒后得到,然后你再发送10个设备详细信息的请求,并等待响应(其中第一个将在1.5秒内得到,第二个--在1.2秒内得到,第三个--在2秒内得到,以此类推)。因此,要想获得整个设备的数据,你必须要等到所有的请求都被满足后,才可以用LiveData发送。只有在这时将LiveData标记发送至UI,数据才会被准备好,并且可以更新。
  3. 你在你的Fragment中观察LiveData。但是当它的回调被调用时(你在适配器中设置数据的地方)?答案--就在你在Repository中设置LiveData的值之后。而且你要在收到第一个请求后立即调用它(而不是等待返回关于设备数据的其余请求)。这就是为什么你不能在你的片段中得到设备数据的详细信息。
// 1. your first request done (let's say after 1 second)
// 2. you set you LiveData value, and update your UI, but other request are not ready
mutableLiveData.setValue(devices);
// 3. rest of your requests done (let's say after 3 seconds)
// <- there you should set your LiveData
  1. 如果你使用Java,你可以等待所有的请求链--你可以用RxJava来做。在StackOverflow上有很多关于这方面的文章。这里是 是其中之一。
© www.soinside.com 2019 - 2024. All rights reserved.