Android ViewModel无法解析getEarthquakes方法及UI动态更新问题求助
报错原因与修复
你在EarthquakeActivity中声明的mViewModel是父类androidx.lifecycle.ViewModel类型,父类本身没有定义getEarthquakes方法,所以编译无法识别。同时你当前的异步请求逻辑存在缺陷:网络请求是子线程异步执行,getEarthquakes直接返回时请求还未完成,拿到的永远是空值,也无法实现数据更新后自动刷新UI。
完整修改代码
1. 修改MyModel.java
用MutableLiveData包裹地震列表,添加请求标记避免屏幕旋转重复发起请求,数据变化时自动通知观察者:
package com.example.android.earthquake; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyModel extends ViewModel { // 用LiveData包裹数据,支持观察变化 private MutableLiveData<ArrayList<Earthquake>> mEarthquakesLiveData; // 标记是否已经发起过请求,避免屏幕旋转重复调用 private boolean isFetched = false; public MutableLiveData<ArrayList<Earthquake>> getEarthquakesLiveData() { if (mEarthquakesLiveData == null) { mEarthquakesLiveData = new MutableLiveData<>(); } // 已经请求过就不再重复发请求 if (!isFetched) { fetchEarthquakeData(); isFetched = true; } return mEarthquakesLiveData; } private void fetchEarthquakeData() { ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(() -> { // 子线程请求数据 ArrayList<Earthquake> result = QueryUtils.fetchEarthquakeData("https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10"); // 子线程用postValue更新LiveData mEarthquakesLiveData.postValue(result); }); executorService.shutdown(); } // 对外提供刷新数据的方法,需要拉取最新数据时调用即可 public void refreshData() { fetchEarthquakeData(); } }
2. 修改EarthquakeActivity.java
把ViewModel声明为自定义的MyModel类型,观察LiveData数据变化自动更新UI:
package com.example.android.earthquake; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.app.SearchManager; import android.content.Intent; import android.os.Bundle; import java.util.ArrayList; public class EarthquakeActivity extends AppCompatActivity { // 声明为自定义的MyModel类型,不是父类ViewModel private MyModel mViewModel; private EarthquakeAdapter earthquakeAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.earthquake_activity); // 初始化ViewModel mViewModel = new ViewModelProvider(this).get(MyModel.class); // 初始化RecyclerView和适配器 RecyclerView recyclerView = findViewById(R.id.earthquakes); recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 初始传入空列表,后续数据返回后更新 earthquakeAdapter = new EarthquakeAdapter(this, new ArrayList<>()); recyclerView.setAdapter(earthquakeAdapter); // 观察LiveData数据变化,数据更新时自动回调 mViewModel.getEarthquakesLiveData().observe(this, earthquakes -> { // 更新适配器的数据 earthquakeAdapter.updateData(earthquakes); // 刷新列表 earthquakeAdapter.notifyDataSetChanged(); // 设置点击监听 earthquakeAdapter.setClickListener((view, position) -> searchWeb(earthquakes.get(position).getUrl())); }); } private void searchWeb (String url) { Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.putExtra(SearchManager.QUERY,url); if(intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } } }
3. 给EarthquakeAdapter添加更新数据的方法
在你的EarthquakeAdapter类中添加如下方法,用来更新内部存储的地震列表:
public void updateData(ArrayList<Earthquake> newData) { // 把你适配器里原来存储地震数据的成员变量替换成新数据即可 this.mEarthquakeList = newData; }
API新数据自动更新UI实现
当前代码已经支持数据变化自动刷新UI,如果你需要实时拉取API最新数据,仅需要在合适的时机调用mViewModel.refreshData()即可,常见触发场景:
- 加入下拉刷新控件,用户手动下拉时调用刷新
- 添加定时任务,每隔固定间隔自动调用
refreshData()拉取最新数据,拉取到新数据后LiveData会自动通知UI更新,不需要重启APP
内容的提问来源于stack exchange,提问作者nmnsharma007
相关产品推荐
相关产品推荐

