android 中的 ViewModel 和可变实时数据无法正常工作

ViewModel and Mutable Live Data in android not working properly

我只是想获取地震列表并从中制作一个数组列表。为了避免在屏幕旋转时多次 API 调用,我使用 ViewModel 实现了可变实时数据 class,但是通过登录我发出网络请求的 QueryUtils class,我发现即使我旋转设备,网络请求仍在进行中。我不明白为什么。这是我的相关 classes:

public class EarthquakeActivity extends AppCompatActivity {

    private static final String LOG_TAG = "MainActivity";
    private MyModel mMyModel;
    private ViewModelProvider mViewModelProvider;
    private RecyclerView mRecyclerView;
    private TextView mEmptyView;
    private EarthquakeAdapter mEarthquakeAdapter;
    private ProgressBar mProgressBar;
    private ConnectivityManager mConnectivityManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.earthquake_activity);
        mEmptyView = findViewById(R.id.empty_view);
        // get the progress bar to indicate loading
        mProgressBar = findViewById(R.id.loading_bar);
        // set a view model provider for the current activity
        mViewModelProvider = new ViewModelProvider(this);
        // get the view model for the class
        mMyModel = mViewModelProvider.get(MyModel.class);
        // find a reference to the {@link RecyclerView} in the layout
        mRecyclerView = findViewById(R.id.earthquakes);
        mConnectivityManager = getSystemService(ConnectivityManager.class);
        mEmptyView.setText("No internet available");
        mEmptyView.setVisibility(View.VISIBLE);
        Handler handler = new Handler(Looper.getMainLooper());
        mConnectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){
            @Override
            public void onAvailable(Network network) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        mEmptyView.setVisibility(View.GONE);
                        mProgressBar.setVisibility(View.VISIBLE);
                        fetchData();
                    }
                });
            }
        });
        Log.e(LOG_TAG,"Visibility changed");
    }

    private void fetchData(){
        // fetch the list of earthquakes
        mMyModel.getEarthquakes().observe(EarthquakeActivity.this, new Observer<ArrayList<Earthquake>>() {
            @Override
            public void onChanged(ArrayList<Earthquake> earthquakes) {
                Log.v(LOG_TAG,"fetching the data");
                // set up recycler view with this data, this will work even if you rotate the device
                setUpRecyclerView(earthquakes);
            }
        });
    }

    private void setUpRecyclerView(ArrayList<Earthquake> earthquakes) {
        if(earthquakes == null || earthquakes.size() == 0) {
            mRecyclerView.setVisibility(View.GONE);
            mProgressBar.setVisibility(View.GONE);
            mEmptyView.setText(R.string.no_data_available);
            mEmptyView.setVisibility(View.VISIBLE);
        }
        else {
            mRecyclerView.setVisibility(View.VISIBLE);
            mEmptyView.setVisibility(View.GONE);
            mProgressBar.setVisibility(View.GONE);
            // create adapter passing in the earthquake data
            mEarthquakeAdapter = new EarthquakeAdapter(EarthquakeActivity.this, earthquakes);
            // attach the adapter to the recyclerView to populate the items
            mRecyclerView.setAdapter(mEarthquakeAdapter);
            // set the layout manager to position the items
            mRecyclerView.setLayoutManager(new LinearLayoutManager(EarthquakeActivity.this));
            Log.e(LOG_TAG,"Recycler view about to be setup");
            // click listener for when an item is clicked
            mEarthquakeAdapter.setClickListener((view, position) -> searchWeb(earthquakes.get(position).getUrl()));
        }
    }
}

我的视图模型class

public class MyModel extends ViewModel {

    private static final String LOG_TAG = "MyModel";
    private MutableLiveData<ArrayList<Earthquake>> mMutableLiveData;

    public MutableLiveData<ArrayList<Earthquake>> getEarthquakes() {
        mMutableLiveData = new MutableLiveData<>();
        // call the API
        init();
        return mMutableLiveData;
    }

    public void init() {
        // perform the network request on separate thread
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // create array list of earthquakes
                mMutableLiveData.postValue(QueryUtils.fetchEarthquakeData("https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2021-06-01&limit=300"));
            }
        });
        executorService.shutdown();

    }


}

和我的 Query Utils class 相关方法只是因为 class 很大

private static String makeHttpRequest(URL url) throws IOException {
        String jsonResponse = "";

        // if url null, return
        if(url == null) {
            return jsonResponse;
        }
        HttpURLConnection urlConnection = null;
        InputStream inputStream = null;
        try {
            urlConnection= (HttpURLConnection)url.openConnection();
            urlConnection.setReadTimeout(10000/*milliseconds*/);
            urlConnection.setConnectTimeout(15000/*milliseconds*/);
            urlConnection.setRequestMethod("GET");
            urlConnection.connect();
            // this is being logged again even after screen rotation
            Log.v(LOG_TAG,"Network request made");

            // if the request was successful(response code 200)
            //then read the input stream and parse the output
            if(urlConnection.getResponseCode() == 200) {
                inputStream = urlConnection.getInputStream();
                jsonResponse = readFromStream(inputStream);
            }
            else{
                Log.e(LOG_TAG,"Error Response code: " + urlConnection.getResponseCode());
            }
        }
        catch (IOException e) {
            Log.e(LOG_TAG,"Problem retrieving the earthquake JSON results",e);
        }
        finally {
            if(urlConnection != null) {
                urlConnection.disconnect();
            }
            if(inputStream != null) {
                inputStream.close();
            }
        }
        return jsonResponse;

请帮我看看哪里出错了。这让我现在很困惑。

看看这里,也许能帮到你 Activity restart on rotation Android

可能在屏幕旋转时您再次调用 onCreate,ViewModel 也是如此

编辑:尝试以这种方式调用 API

 public MutableLiveData<ArrayList<Earthquake>> getEarthquakes() {
        mMutableLiveData = new MutableLiveData<>();
        //call the API
        mMutableLiveData.init();

配置更改时,您的 activity 会重新创建并调用 fetchData() 调用 getEarthquakes() 和 init()。

步骤应该是:

  1. 在 viewModel 中创建一个私有的 MutableLiveData
  2. 为该 MutableLiveData
  3. 创建一个 getter 方法
  4. 在 onCreate() 方法中,您应该在不进行任何调用的情况下注册观察者
  5. 然后您应该调用 public 方法来获取数据并将值发布到 MutableLiveData。
  6. 这样,您在 UI 中的观察器应该被触发。

您应该通过调用 getEarthquakes() 而不是调用 init() 来注册观察者。如果数据最初为空,则可以在观察者方法中进行空检查。

创建 activity 之后,您应该调用在 viewModel 中创建的 public 方法来获取数据,在您的例子中是 init()。