如何阻止异常搜索 Unavailable/no 个城市?

How to stop exception from searching Unavailable/no cities?

我有一个天气应用程序,可以搜索在 EditText 上输入的任何城市,而且效果很好。 但问题是,每当我搜索不可用的城市或将 EditText 留空并单击搜索按钮时,应用程序崩溃并在我的 phone 上报告 "(my app) has stopped"(这仅发生在我search an unavailable city/no city is searched), 它可以正确搜索任何有效的城市。

这是我的 Logcat 显示的内容(仅当搜索到不可用的城市时):

java.lang.NullPointerException: Attempt to invoke virtual method 'com.viz.lightweatherforecast.Retrofit.Example$Main com.viz.lightweatherforecast.Retrofit.Example.getMain()' on a null object reference
        at com.viz.lightweatherforecast.FirstFragment.onResponse(FirstFragment.java:104)
        at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.lambda$onResponse[=11=]$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) 

java.lang.NullPointerException: Attempt to invoke virtual method 'com.viz.lightweatherforecast.first.PrettyTime com.viz.lightweatherforecast.Retrofit.Example.getDt()' on a null object reference
        at com.viz.lightweatherforecast.Activity.HomeActivity.onResponse(HomeActivity.java:100)
        at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.lambda$onResponse[=12=]$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)

没有搜索到城市时(EditText为空)。

所以我试图停止对此负责的异常,并在搜索 unavailable/no 城市时写一个 toast/message 告诉用户“no city found”。 请问我该怎么做?到目前为止,我已经尝试添加:

Log.d(TAG, "No City found"); 

在我的

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

HomeActivity中的方法,还是崩溃了

这是我的具体代码:

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());
                FirstFragment firstFragment = (FirstFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
                firstFragment.getWeatherData(textfield.getText().toString().trim());

                            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("Last Updated:" + " " + response.body().getDt());

                    }

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

                });
            }

        });
    }
}

FirstFragment.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);

        return rootView;
    }

    public 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().getWeather().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();
            }
        });
    }
}

编辑

Example.java:

public class Example {

    @SerializedName("coord")
    private Coord coord;
    @SerializedName("weather")
    private List<Weather> weather = null;
    @SerializedName("base")
    private String base;
    @SerializedName("main")
    private Main main;
    @SerializedName("visibility")
    private Integer visibility;
    @SerializedName("wind")
    private Wind wind;
    @SerializedName("clouds")
    private Clouds clouds;
    @SerializedName("dt")
    @Expose
    private PrettyTime dt;
    @SerializedName("sys")
    private Sys sys;
    @SerializedName("timezone")
    private Integer timezone;
    @SerializedName("id")
    private Integer id;
    @SerializedName("name")
    private String name;
    @SerializedName("cod")
    private Integer cod;

    public Coord getCoord() {
        return coord;
    }

    public void setCoord(Coord coord) {
        this.coord = coord;
    }

    public List<Weather> getWeather() {
        return weather;
    }

    public void setWeather(List<Weather> weather) {
        this.weather = weather;
    }

    public String getBase() {
        return base;
    }

    public void setBase(String base) {
        this.base = base;
    }

    public Main getMain() {
        return main;
    }

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

    public Integer getVisibility() {
        return visibility;
    }

    public void setVisibility(Integer 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 PrettyTime getDt() {
        return dt;
    }

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

    public Sys getSys() {
        return sys;
    }

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

    public Integer getTimezone() {
        return timezone;
    }

    public void setTimezone(Integer timezone) {
        this.timezone = timezone;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getCod() {
        return cod;
    }

    public void setCod(Integer cod) {
        this.cod = cod;
    }

    public class Clouds {

        @SerializedName("all")
        private Integer all;

        public Integer getAll() {
            return all;
        }

        public void setAll(Integer all) {
            this.all = all;
        }

    }

    public class Coord {

        @SerializedName("lon")
        private Double lon;
        @SerializedName("lat")
        private Double lat;

        public Double getLon() {
            return lon;
        }

        public void setLon(Double lon) {
            this.lon = lon;
        }

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

    }

    public class Main {

        @SerializedName("temp")
        private Double temp;
        @SerializedName("feels_like")
        private Double feelsLike;
        @SerializedName("temp_min")
        private Double tempMin;
        @SerializedName("temp_max")
        private Double tempMax;
        @SerializedName("pressure")
        private Integer pressure;
        @SerializedName("humidity")
        private Integer humidity;

        public Double getTemp() {
            return temp;
        }

        public void setTemp(Double temp) {
            this.temp = temp;
        }

        public Double getFeelsLike() {
            return feelsLike;
        }

        public void setFeelsLike(Double feelsLike) {
            this.feelsLike = feelsLike;
        }

        public Double getTempMin() {
            return tempMin;
        }

        public void setTempMin(Double tempMin) {
            this.tempMin = tempMin;
        }

        public Double getTempMax() {
            return tempMax;
        }

        public void setTempMax(Double tempMax) {
            this.tempMax = tempMax;
        }

        public Integer getPressure() {
            return pressure;
        }

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

        public Integer getHumidity() {
            return humidity;
        }

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

    }

    public class Sys {

        @SerializedName("type")
        private Integer type;
        @SerializedName("id")
        private Integer id;
        @SerializedName("country")
        private String country;
        @SerializedName("sunrise")
        @Expose
        private PrettyTime sunrise;
        @SerializedName("sunset")
        @Expose
        private PrettyTime sunset;

        public Integer getType() {
            return type;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getCountry() {
            return country;
        }

        public void setCountry(String country) {
            this.country = country;
        }

        public PrettyTime getSunrise() {
            return sunrise;
        }

        public void setSunrise(PrettyTime sunrise) {
            this.sunrise = sunrise;
        }

        public PrettyTime getSunset() {
            return sunset;
        }

        public void setSunset(PrettyTime sunset) {
            this.sunset = sunset;
        }

    }

    public class Weather {

        @SerializedName("id")
        private Integer id;
        @SerializedName("main")
        private String main;
        @SerializedName("description")
        private String description;
        @SerializedName("icon")
        private String icon;

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getMain() {
            return main;
        }

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

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getIcon() {
            return icon;
        }

        public void setIcon(String icon) {
            this.icon = icon;
        }

    }

    public class Wind {

        @SerializedName("speed")
        private Double speed;
        @SerializedName("deg")
        private Integer deg;
        @SerializedName("gust")
        private Double gust;

        public Double getSpeed() {
            return speed;
        }

        public void setSpeed(Double speed) {
            this.speed = speed;
        }

        public Integer getDeg() {
            return deg;
        }

        public void setDeg(Integer deg) {
            this.deg = deg;
        }

        public Double getGust() {
            return gust;
        }

        public void setGust(Double gust) {
            this.gust = gust;
        }
    }

}

根据日志,您得到了带有正文的成功响应,但是 getMain returns null.

您可以通过在 getMain 周围添加一些空检查来防止这种情况。请确保在 api 文档中检查 null 的可能性。

   public void onResponse(@NotNull Call<Example> call, @NotNull Response<Example> response) {
    
           assert response.body() !=null;
    
    if(response.body().getMain()==null && response.body().getWeather()!=null && all other field!=null){
    yourErrorTextview.setText("No City found");
    allOtherField.setText();
    }
    else if(response.body().getMain()!=null && response.body().getWeather()!=null && all other field!=null){
             current_temp.setText(response.body().getMain().getTemp() + " ℃");
             allOtherField.setText();
    }}

您可以在捕获异常时使用 try catch 块,您可以吐司消息 city not found 这样您的应用就不会崩溃。

正如OP所说I just need the best method to handle the exceptions

让我为您的情况举两个例子,以避免崩溃并为 Activity 和 Fragment 显示 toast。

  • try/catch

更新您的 Activity onResponse,如下所示。

                public void onResponse(Call<Example> call, Response<Example> response) {

                    try {
                        time_field.setText("Last Updated:" + " " + response.body().getDt());
                    } catch (Exception e) {
                        time_field.setText("Last Updated: Unknown");
                        Log.e("TAG", "No City found");
                        Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
                    }
                }

像下面这样更新你的片段`onResponse

public void onResponse(Call<Example> call, Response<Example> response) {

            try {
                current_temp.setText(response.body().getMain().getTemp() + " ℃");
                current_output.setText(response.body().getWeather().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() + " %");
            } catch (Exception e) {
                Log.e("TAG", "No City found");
                Toast.makeText(getActivity(), "No City found", Toast.LENGTH_SHORT).show();
            }

        }
  • 空检查

更新您的 Activity onResponse,如下所示。

                    public void onResponse(Call<Example> call, Response<Example> response) {

                    if (response.isSuccessful() && response.body() != null) {
                        time_field.setText("Last Updated:" + " " + response.body().getDt());
                    } else {
                        time_field.setText("Last Updated: Unknown");
                        Log.e("TAG", "No City found");
                        Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
                    }

                }

更新您的片段 onResponse 如下所示

public void onResponse(Call<Example> call, Response<Example> response) {

            if (response.isSuccessful() && response.body() != null) {
                current_temp.setText(response.body().getMain().getTemp() + " ℃");
                current_output.setText(response.body().getWeather().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() + " %");
            } else {
                Log.e("TAG", "No City found");
                Toast.makeText(getActivity(), "No City found", Toast.LENGTH_SHORT).show();
            }


        }

注意: 有一些空间可以改进您的代码并设计得更好,比如避免在搜索点击时额外调用 API,一个来自Activity 和片段中的另一个。但是所有这些建议都超出了这个问题的范围,所以我会坚持 I just need the best method to handle the exceptions.

的 OP 请求