我的天气应用程序收到响应但不显示

My Weather app gets response but doesn't display it

我正在开发一个天气应用程序,目前正在开发一个搜索城市按钮。我的主要目标是启用按钮搜索并显示在 edittext 上键入的任何城市数据。

我创建了一个编辑文本和搜索按钮,我还将它们与我解析的改进 classes 连接起来。

我按照这个 youtube 教程寻求帮​​助 https://www.youtube.com/watch?v=SrVY2la7lCI 也从这个 post 得到了一点帮助。他们都能够显示搜索到的任何城市的天气数据。

但是如果我在我的 ApiInterface weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric 上使用这个地址(他们使用的),它 returns 会出现以下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'com.viz.realtimeweather.Retrofit.Main com.com.viz.realtimeweather.Retrofit.Example.getMain()' on a null object reference
at com.viz.realtimeweather.FirstFragment.onResponse(FirstFragment.java:109)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.lambda$onResponse[=10=]$DefaultCallAdapterFactory$ExecutorCallbackCall(DefaultCallAdapterFactory.java:89)
at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCallwC8FyV4pyjrzrYL5U0mlYiviZw.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6819)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:497)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:912)

不显示任何数据。

然后当我向它添加查询时。即 q=london,它没有返回任何错误,但仍然没有显示任何数据。我觉得问题出在其他地方,但我不知道确切的位置。此外,这也不是解决方案,因为我需要启用该应用程序来搜索任何位置,而不仅仅是特定的城市。

到目前为止,我有:

使用 https://whosebug.com/help/minimal-reproducible-example,我将分享我的代码以寻求帮助。

我正在使用 OpenWeatherMap API 格式:

{
   "coord":{
      "lon":-122.08,
      "lat":37.39
   },
   "weather":[
      {
         "id":800,
         "main":"Clear",
         "description":"clear sky",
         "icon":"01d"
      }
   ],
   "base":"stations",
   "main":{
      "temp":282.55,
      "feels_like":281.86,
      "temp_min":280.37,
      "temp_max":284.26,
      "pressure":1023,
      "humidity":100
   },
   "visibility":16093,
   "wind":{
      "speed":1.5,
      "deg":350
   },
   "clouds":{
      "all":1
   },
   "dt":1560350645,
   "sys":{
      "type":1,
      "id":5122,
      "message":0.0139,
      "country":"US",
      "sunrise":1560343627,
      "sunset":1560396563
   },
   "timezone":-25200,
   "id":420006353,
   "name":"Mountain View",
   "cod":200
}

我的 JSON 回复(当我不添加查询时):

    {
   "cod":"400",
   "message":"Nothing to geocode"
}

HomeActivity.java:

public class HomeActivity extends AppCompatActivity {
    // User current time
    TextView time_field;
    ImageView Search;
    EditText textfield;
    ConstraintLayout constraintLayout;
    // For scheduling background image change
    public static int count=0;
    int[] drawable =new int[]{R.drawable.dubai,R.drawable.central_bank_of_nigeria,R.drawable.eiffel_tower,R.drawable.hong_kong,R.drawable.statue_of_liberty};
    Timer _t;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        time_field = findViewById(R.id.textView9);
        Search = findViewById(R.id.imageView4);
        textfield = findViewById(R.id.textfield);

        BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
        final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
        assert navHostFragment != null;
        final NavController navController = navHostFragment.getNavController();
        NavigationUI.setupWithNavController(bottomNavigationView, navController);

        Search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                getWeatherData(textfield.getText().toString().trim());

                int id = Objects.requireNonNull(navController.getCurrentDestination()).getId();
                navController.popBackStack();
                navController.navigate(id);


                            constraintLayout = findViewById(R.id.layout);
                            constraintLayout.setBackgroundResource(R.drawable.dubai);
                            _t = new Timer();
                            _t.scheduleAtFixedRate(new TimerTask() {
                                @Override
                                public void run() {
                                    // run on ui thread
                                    runOnUiThread(() -> {
                                        if (count < drawable.length) {

                                            constraintLayout.setBackgroundResource(drawable[count]);
                                            count = (count + 1) % drawable.length;
                                        }
                                    });
                                }
                            }, 5000, 5000);
                        }

            private void getWeatherData(String name) {

                ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);

                Call<Example> call = apiInterface.getWeatherData(name);

                call.enqueue(new Callback<Example>() {
                    @Override
                    public void onResponse(@NotNull Call<Example> call, @NotNull Response<Example> response) {

                        assert response.body() != null;
                        time_field.setText(String.valueOf(response.body().getDt()));



                    }

                    @Override
                    public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
                        t.printStackTrace();
                    }


                });
            }




        });
    }
}

第一个 Fragment.java:

public class FirstFragment extends Fragment {
    // User current time, current temperature, current condition, sunrise, sunset, temperature, pressure, humidity, wind_speed, visibility, clouds
    TextView current_temp, current_output, rise_time, set_time, temp_out, Press_out, Humid_out, Ws_out, Visi_out, Cloud_out;
    // TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    public FirstFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment SecondFragment.
     */
// TODO: Rename and change types and number of parameters
    public static FirstFragment newInstance(String param1, String param2) {
        FirstFragment fragment = new FirstFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);

        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_first, container, false);
        // For displaying weather data
        current_temp = rootView.findViewById(R.id.textView10);
        current_output = rootView.findViewById(R.id.textView11);
        rise_time = rootView.findViewById(R.id.textView25);
        set_time = rootView.findViewById(R.id.textView26);
        temp_out = rootView.findViewById(R.id.textView28);
        Press_out = rootView.findViewById(R.id.textView29);
        Humid_out = rootView.findViewById(R.id.textView30);
        Ws_out = rootView.findViewById(R.id.textView33);
        Visi_out = rootView.findViewById(R.id.textView34);
        Cloud_out = rootView.findViewById(R.id.textView35);

        // Use activity data
        FragmentActivity fa = getActivity();
        assert fa != null;
        EditText textfield = fa.findViewById(R.id.textfield);
        getWeatherData(textfield.getText().toString().trim());
        return rootView;
    }

    private void getWeatherData(String name) {

        ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);

        Call<Example> call = apiInterface.getWeatherData(name);

        call.enqueue(new Callback<Example>() {
            @Override
            public void onResponse(@NotNull Call<Example> call, @NotNull Response<Example> response) {

                assert response.body() !=null;
                current_temp.setText(response.body().getMain().getTemp() + " ℃");
                current_output.setText(response.body().getWeatherList().get(0).getDescription());
                rise_time.setText(response.body().getSys().getSunrise() + " ");
                set_time.setText(response.body().getSys().getSunset() + " ");
                temp_out.setText(response.body().getMain().getTemp() + " ℃");
                Press_out.setText(response.body().getMain().getPressure() + " hpa");
                Humid_out.setText(response.body().getMain().getHumidity() + " %");
                Ws_out.setText(response.body().getWind().getSpeed() + " Km/h");
                Visi_out.setText(response.body().getVisibility() + " m");
                Cloud_out.setText(response.body().getClouds().getAll()+ " %");
            }

            @Override
            public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
                t.printStackTrace();
            }
        });
    }
}

ApiClient.java:

public class ApiClient {

    private static Retrofit retrofit = null;

    public static  Retrofit getClient(){ //creating object

        if (retrofit == null) {

            retrofit = new Retrofit.Builder()
                    .baseUrl("http://api.openweathermap.org/data/2.5/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }

        return retrofit;

    }
}

ApiInterface.java:

public interface ApiInterface {

    @GET("weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric")
    Call<Example> getWeatherData(@Query("q") String name);
}

Example.java:

public class Example {
    @SerializedName("main")
    private Main main;
    @SerializedName("weather")
    private List<Weather> weatherList;
    @SerializedName("visibility")
    private Visibility visibility;
    @SerializedName("wind")
    private Wind wind;
    @SerializedName("clouds")
    private Clouds clouds;
    @SerializedName("dt")
    private Dt dt;
    @SerializedName("sys")
    private Sys sys;
    @SerializedName("name")
    private Name name;

    public Main getMain() {
        return main;
    }

    public void setMain(Main main) {
        this.main = main;
    }

    public List<Weather> getWeatherList() {
        return weatherList;
    }

    public void setWeatherList(List<Weather> weatherList) {
        this.weatherList = weatherList;
    }

    public Visibility getVisibility() {
        return visibility;
    }

    public void setVisibility(Visibility visibility) {
        this.visibility = visibility;
    }

    public Wind getWind() {
        return wind;
    }

    public void setWind(Wind wind) {
        this.wind = wind;
    }

    public Clouds getClouds() {
        return clouds;
    }

    public void setClouds(Clouds clouds) {
        this.clouds = clouds;
    }

    public Dt getDt() {
        return dt;
    }

    public void setDt(Dt dt) {
        this.dt = dt;
    }

    public Sys getSys() {
        return sys;
    }

    public void setSys(Sys sys) {
        this.sys = sys;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }
}

Main.java:

public class Main {

    @SerializedName("temp")
    String temp;

    @SerializedName("pressure")
    String pressure;

    @SerializedName("humidity")
    String humidity;

    public String getTemp() {
        return temp;
    }

    public void setTemp(String temp) {
        this.temp = temp;
    }
    public String getPressure() {
        return pressure;
    }

    public void setPressure(String pressure) {
        this.pressure = pressure;
    }

    public String getHumidity() {
        return humidity;
    }

    public void setHumidity(String humidity) {
        this.humidity = humidity;
    }
}

编辑

dt.java:

public class Dt {

    @SerializedName("dt")
    @Expose
    private PrettyTime dt;

    public PrettyTime getDt() {
        return dt;
    }

    public void setDt(PrettyTime dt) {
        this.dt = dt;
    }
}

问题是您通过调用 http://api.openweathermap.org/data/2.5/weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric

收到 400 Bad Request

甚至

http://api.openweathermap.org/data/2.5/weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric&q=

任何不属于 [200; 300[ 范围的改造都会被认为是错误,不会给你一个 body(),因此是空指针,因为 body() 是空的。另一方面,errorBody() 将有您想要的字符串。

要使用错误主体,您可以简单地执行 errorBody().string() 但要小心,因为它表现为一个流并且只能使用一次。

至于您的请求为何下降,这似乎是因为您缺少一些查询参数以允许给定坐标的开放天气 api 至 return 天气。添加一个简单的 q=lisbon 似乎可以解决问题:

http://api.openweathermap.org/data/2.5/weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric&q=lisbon

将 return 200 OK 和改造 body() 方法将 return 一些东西。也许你发送的是空的?

经过discussion and also from my comments to the question the model mapping the JSON response was not correct and all that was required was to map the response正确到Java模型。