0

I want to show a list of information about devices. First, I call API to get a list of devices. After that, in onResponse function, I for each device and call another API to get data of each device. But this data is not updated in View. View just show a list of device, can not show data of each device. I think that observe don't catch event when updating data of devices

My Device class:

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

My repository:

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

This is my 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;
    }
}

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

And 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>
NAM
  • 107
  • 3
  • 10

1 Answers1

0

Your problem is with LiveData managing and about async operations

  1. All Retrofit "enqueue" are asynchronous, each result you can catch in callback - onResponse.
  2. Let's say your first response you will get after 1 second, then you send another 10 request for devices details, and you wait for response (first of them will come in 1.5 second, second - in 1.2, third - in 2 and so on). So to get whole data about devices you should wall until all request will be fulfilled. And only then to send with LiveData mark to UI, that data is ready and it could be updated.
  3. You observe LiveData in your Fragment. But when it's callback invoked (that place where you set data in your adapter)? Answer - just after you set your LiveData's value in your Repository. And you do it right after getting first request (not waiting for return rest of request about devices' data). That's why you don't get detailed info about devices' data in your fragment.
// 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. If you use Java, you can wait for all request chaining them - you can do it with RxJava. There are lot articles on StackOverflow about that. Here is one of them.
sergiy tikhonov
  • 4,961
  • 1
  • 10
  • 27