Android 中 View Model class 中的错误解决方法

Error resolving method in View Model class in Android

我是 Android Devlopement 的新手,我试图使用 API 调用从网站获取地震列表,并使用 Recycler 视图将它们显示在屏幕上。我现在正在尝试将 ViewModel 合并到我的应用程序中,以便在屏幕旋转时不会进行多次 API 调用,但我不明白为什么会出现错误:Cannot resolve method 'getEarthquakes' in ViewModel 这是我的 MainActivity (EarthquakeActivity) 和 ViewModel(MyModel) class 在 Java 中的实现。

EarthquakeActivity.java

package com.example.android.earthquake;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.app.SearchManager;
import android.content.ClipData;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EarthquakeActivity extends AppCompatActivity {

    private ViewModel mViewModel;
    private ViewModelProvider mViewModelProvider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.earthquake_activity);
        mViewModelProvider = new ViewModelProvider(this);
        mViewModel = mViewModelProvider.get(MyModel.class);
        // find a reference to the {@link RecyclerView} in the layout
        RecyclerView recyclerView = findViewById(R.id.earthquakes);
        // fetch the list of earthquakes
        ArrayList<Earthquake> earthquakes = mViewModel.getEarthquakes();// error on this line
        // create adapter passing in the earthquake data
        EarthquakeAdapter earthquakeAdapter = new EarthquakeAdapter(EarthquakeActivity.this,earthquakes);

        // attach the adapter to the recyclerView to populate the items
        recyclerView.setAdapter(earthquakeAdapter);

        // set the layout manager to position the items
        recyclerView.setLayoutManager(new LinearLayoutManager(EarthquakeActivity.this));
        // click listener for when an item is clicked
        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);
        }
    }

}

和MyModel.java

package com.example.android.earthquake;

import android.os.Handler;
import android.os.Looper;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyModel extends ViewModel {
    private ArrayList<Earthquake> mEarthquakes;

    public ArrayList<Earthquake> getEarthquakes() {
        // perform the network request on separate thread
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // create array list of earthquakes
                mEarthquakes = QueryUtils.fetchEarthquakeData("https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10");
            }
        });
        executorService.shutdown();
        return mEarthquakes;
    }

}

即使编写这么多的 ViewModel 对我来说也很难,因为几乎所有教程都使用 Kotlin,而且我发现 Loaders 已被弃用。另外,如果可能的话,如果 API 在新地震发生时发生变化而无需重新启动应用程序,有人能告诉我如何用最新的地震数据更新 UI 吗?我将不胜感激。

我可以看到这里有很多空白。

让我们从您的 EarthquakeActivity 开始,这里您的 mViewModel 的类型是 ViewModel,它应该是 MyModel,并且由于 ViewModel class 中没有 getEarthquakes() 方法,您会收到该错误。

在行中,ArrayList earthquakes = mViewModel.getEarthquakes(); 您要求您的视图模型为您提供列表,网络调用需要一些时间,而且它也是异步的,因此您将收到一个空列表。

要解决此问题,您需要在视图模型中使用 MutableLiveData 并且需要在 activity 中观察它,以便在从网络调用,我也会建议你使用retrofit进行网络调用。

因此您的最终代码将如下所示,

EarthquakeActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.app.SearchManager;
import android.content.ClipData;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EarthquakeActivity extends AppCompatActivity {

    private MyModel mViewModel;
    private ViewModelProvider mViewModelProvider;
    
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.earthquake_activity);
        
        //init viewmodel
        mViewModelProvider = new ViewModelProvider(this);
        mViewModel = mViewModelProvider.get(MyModel.class);
        
        // find a reference to the {@link RecyclerView} in the layout
        recyclerView = findViewById(R.id.earthquakes);
        
        // fetch and observer the list of earthquakes
        mViewModel.getEarthquakes().observe(this, new Observer<ArrayList<Earthquake>>() {
            @Override
            public void onChanged(ArrayList<Earthquake> earthquakes) {
                //setup recycler view with this data, this will work even if you rotate the device
                setUpRecyclerView(earthquakes);
            }
        });
    }

    private void setUpRecyclerView(ArrayList<Earthquake> earthquakes) {
        // create adapter passing in the earthquake data
        EarthquakeAdapter earthquakeAdapter = new EarthquakeAdapter(EarthquakeActivity.this,earthquakes);
        // attach the adapter to the recyclerView to populate the items
        recyclerView.setAdapter(earthquakeAdapter);
        // set the layout manager to position the items
        recyclerView.setLayoutManager(new LinearLayoutManager(EarthquakeActivity.this));
        // click listener for when an item is clicked
        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);
        }
    }

}

还有你的MyModel.java

import android.os.Handler;
import android.os.Looper;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyModel extends ViewModel {
    private MutableLiveData<ArrayList<Earthquake>> mEarthquakesLiveData;

    public MutableLiveData<ArrayList<Earthquake>> getEarthquakes() {
        // perform the network request on separate thread
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // create array list of earthquakes
                mEarthquakesLiveData.setValue(QueryUtils.fetchEarthquakeData("https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10"));
            }
        });
        executorService.shutdown();
        return mEarthquakesLiveData;
    }

}

希望对您有所帮助,欢迎点个赞。美好的一天,伙计!!