调出每小时天气预报数据时出错:java.lang.IllegalStateException:无法执行 android:onClick 的方法

Error bringing up hourly weather forecast datar: java.lang.IllegalStateException: Could not execute method for android:onClick

我的 Android 工作室项目存在运行时问题。这是一个天气应用程序,试图从 API 中调出每小时的预报数据。提出主要 activity 的意图不断提出 IllegalStateException。看起来代码本身没有错误

这是主要的 activity 文件。

package com.teamtreehouse.stormy.ui;

import android.content.Context;
import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.teamtreehouse.stormy.R;
import com.teamtreehouse.stormy.Weather.Current;
import com.teamtreehouse.stormy.Weather.Forecast;
import com.teamtreehouse.stormy.Weather.Hour;
import com.teamtreehouse.stormy.databinding.ActivityMainBinding;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.Serializable;
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;

public class MainActivity extends AppCompatActivity {

  public static final String TAG = MainActivity.class.getSimpleName();
  private Forecast forecast;
  private ImageView iconImageView;

  double latitude = 37.8267;
  double longitude = -122.4233;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getForecast(latitude, longitude);
    Log.d(TAG, "Main UI code is running, hooray!");
  }

  private void getForecast(double latitude, double longitude) {
    final ActivityMainBinding binding = DataBindingUtil
        .setContentView(MainActivity.this, R.layout.activity_main);

    iconImageView = findViewById(R.id.iconImageView);

    // Setup Dark Sky Link
    TextView darkSky = findViewById(R.id.darkSkyAttribution);
    darkSky.setMovementMethod(LinkMovementMethod.getInstance());

    String apiKey = "57eaf3aa961968bf65b0619680588073";


    String forecastURL = "https://api.darksky.net/forecast/" + apiKey +
        "/" + latitude + "," + longitude;

    if (isNetworkAvailable()) {
      OkHttpClient client = new OkHttpClient();

      Request request = new Request.Builder()
          .url(forecastURL)
          .build();

      Call call = client.newCall(request);
      call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
          try {
            String jsonData = response.body().string();
            Log.v(TAG, jsonData);
            if (response.isSuccessful()) {

                forecast = parseForecastData(jsonData);

                Current current = forecast.getCurrent();

                final Current displayWeather = new Current(
                  current.getLocationLabel(),
                  current.getIcon(),
                  current.getTime(),
                  current.getTemperature(),
                  current.getHumidity(),
                  current.getPrecipChance(),
                  current.getSummary(),
                  current.getTimeZone()
              );

              binding.setWeather(displayWeather);

              runOnUiThread(new Runnable() {
                @Override
                public void run() {
                  Drawable drawable = getResources().getDrawable(displayWeather.getIconId());
                  iconImageView.setImageDrawable(drawable);
                }
              });

            } else {
              alertUserAboutError();
            }
          } catch (IOException e) {
            Log.e(TAG, "IO Exception caught: ", e);
          } catch (JSONException e) {
            Log.e(TAG, "JSON Exception caught: ", e);
          }

        }
      });
    }
    else {
      Toast.makeText(this, R.string.network_unavailable_message,
          Toast.LENGTH_LONG).show();
    }
  }

  private Forecast parseForecastData(String jsonData) throws JSONException {
    Forecast forecast = new Forecast();

    forecast.setCurrent(getCurrentDetails(jsonData));

    return forecast;
  }

  private Hour[] getHourlyForecast(String jsonData) throws JSONException {
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");

       //get an array of JSON objects
        JSONObject hourly = forecast.getJSONObject("hourly");
        JSONArray data = hourly.getJSONArray("data");
        Hour[] hours = new Hour[data.length()];

        for (int i=0; i<data.length(); i++){
            JSONObject jsonHour = data.getJSONObject(i);

            Hour hour = new Hour();

            hour.setSummary(jsonHour.getString("summary"));
            hour.setIcon(jsonHour.getString("icon"));
            hour.setTemperature(jsonHour.getDouble("temperature"));
            hour.setTime(jsonHour.getLong("time"));
            hour.setTimeZone(timezone);

            hours[i] = hour;

        }
        return hours;

    }

  private Current getCurrentDetails(String jsonData) throws JSONException {
    JSONObject forecast = new JSONObject(jsonData);

    String timezone = forecast.getString("timezone");
    Log.i(TAG, "From JSON: " + timezone);

    JSONObject currently = forecast.getJSONObject("currently");

    Current current = new Current();

    // Parse weather data from currently object
    current.setHumidity(currently.getDouble("humidity"));
    current.setTime(currently.getLong("time"));
    current.setIcon(currently.getString("icon"));
    current.setLocationLabel("Alcatraz Island, CA");
    current.setPrecipChance(currently.getDouble("precipProbability"));
    current.setSummary(currently.getString("summary"));
    current.setTemperature(currently.getDouble("temperature"));
    current.setTimeZone(timezone);

    Log.d(TAG, current.getFormattedTime());

    return current;
  }

  private boolean isNetworkAvailable() {
    ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = manager.getActiveNetworkInfo();

    boolean isAvailable = false;

    if (networkInfo != null && networkInfo.isConnected()) {
      isAvailable = true;
    }

    return isAvailable;
  }

  private void alertUserAboutError() {
    AlertDialogFragment dialog = new AlertDialogFragment();
    dialog.show(getFragmentManager(), "error_dialog");
  }

  public void refreshOnClick(View view) {
    getForecast(latitude, longitude);
    Toast.makeText(this, "Refreshing data", Toast.LENGTH_LONG).show();
  }

  public void hourlyOnClick(View view) {

      List<Hour> hours = Arrays.asList(forecast.getHourlyForecast());
      Intent intent = new Intent(this, HourlyForecastActivity.class);

      intent.putExtra("HourlyList", (Serializable) hours);
      startActivity(intent);
  }

}

这是我的堆栈跟踪

07-19 09:53:53.662 12590-12590/uk.co.jonniegrieve.stormy E/AndroidRuntime: FATAL EXCEPTION: main
    Process: uk.co.jonniegrieve.stormy, PID: 12590
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:389)
        at android.view.View.performClick(View.java:5198)
        at android.view.View$PerformClick.run(View.java:21147)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384)
        at android.view.View.performClick(View.java:5198) 
        at android.view.View$PerformClick.run(View.java:21147) 
        at android.os.Handler.handleCallback(Handler.java:739) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:148) 
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
     Caused by: java.lang.NullPointerException: storage == null
        at java.util.Arrays$ArrayList.<init>(Arrays.java:38)
        at java.util.Arrays.asList(Arrays.java:155)
        at com.teamtreehouse.stormy.ui.MainActivity.hourlyOnClick(MainActivity.java:222)
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384) 
        at android.view.View.performClick(View.java:5198) 
        at android.view.View$PerformClick.run(View.java:21147) 
        at android.os.Handler.handleCallback(Handler.java:739) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:148) 
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
07-19 09:53:53.682 1228-1228/? E/EGL_emulation: tid 1228: eglCreateSyncKHR(1881): error 0x3004 (EGL_BAD_ATTRIBUTE

)

看起来 hourlyOnClick returns 中的方法 forecast.getHourlyForecast() 为 null 而不是数组。