簡體   English   中英

Android MVVM 架構和觀察來自 API 的數據變化

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

我是 Android MVVM 架構的新手。 我有一個本地運行的 API,其中包含數據(“交易”)。 我只想向 API 發出請求並在文本字段中顯示該數據。 目前,首次加載片段時數據不會顯示,但如果我轉到另一個活動,然后返回到它加載的片段。

這里有 3 類重要性。

儀表板視圖模型.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;
    }
}

儀表板片段.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的變化嗎?

我認為這會有所幫助。 在網絡調用的 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;
}
}

由於每當調用 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 

在函數中的存儲庫類中獲取交易。 您正在初始化實時數據。 在后台線程中請求 url 並在尚未從服務器接收到的實時數據上發布值。

要解決此問題,請在存儲庫的構造函數中創建實時數據實例,並在 onResponse 回調中在實時數據上創建后值。

//抱歉寫得不好,從手機上發布。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM