我正在开发一个天气应用,目前正在开发一个搜索城市按钮。我的主要目标是启用按钮搜索并显示在编辑文本中键入的任何城市数据。
我创建了一个编辑文本和搜索按钮,并将它们与我的改造解析类连接起来。
我关注了这个 youtube 教程以获得一些帮助https://www.youtube.com/watch?v=SrVY2la7lCI 并且还从这篇文章 How to get "weather" object data from OpenWeatherMap API Using Retrofit 中得到了一些帮助。他们都能够显示任何搜索到的城市的天气数据。
但是如果我在我的 ApiInterface weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric
上使用这个地址(他们使用的),它会返回以下错误:
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$1.onResponse(FirstFragment.java:109)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCall$1$3wC8FyV4pyjrzrYL5U0mlYiviZw.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
,它没有返回错误,但仍然没有显示任何数据。我觉得问题出在其他地方,但我不知道具体在哪里。此外,这也不是解决方案,因为我需要启用该应用来搜索任何位置,而不仅仅是某个特定城市。
到目前为止,我已经:
检入和检出我所有解析的网络类是否有错误,但没有发现
检查了我的 ApiClient
和 ApiInterface
类是否有任何错误的网络地址,但没有发现
在我的 ApiInterface 地址上添加了 q=london
,但它不显示任何数据,也不搜索其他城市数据
查看本网站是否有任何与天气相关的问题,但未发现任何问题。
使用 https://stackoverflow.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);
}
示例.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;
}
}
答案 0 :(得分:2)
问题是您通过拨打 http://api.openweathermap.org/data/2.5/weather?appid=9c547bfc852923c3b30d0d62a5ae35e8&units=metric 收到 400 Bad Request
甚至
任何不属于 [200; 300[
改造范围的东西都认为是错误的,不会给你一个 body()
,因此是空指针,因为 body()
是空的。另一方面,errorBody()
将包含您想要的字符串。
要使用错误正文,您可以简单地执行 errorBody().string()
但要小心,因为它表现为一个流并且只能使用一次。
至于您的请求下降的原因,这似乎是因为您缺少一些查询参数来允许开放天气 api 返回给定坐标的天气。添加一个简单的 q=lisbon
似乎可以解决问题:
将返回 200 OK
并且改造 body()
方法将返回一些东西。也许你发送的是空的?
答案 1 :(得分:1)
在 discussion 之后以及我对问题的评论中,映射 JSON 响应的模型不正确,所需要做的就是将 response 正确映射到 Java 模型。