Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Mohammed Saif
Mohammed Saif
13,358 Points

NullPointerException, in Stormy app

Day.class 1 package com.stormy.weather;
2
3 import android.os.Parcel;
4i mport android.os.Parcelable;
5
6 import java.text.SimpleDateFormat;
7 import java.util.Date;
8 import java.util.Formatter;
9 import java.util.TimeZone;
10
11 public class Day implements Parcelable{
12 private String mIcon;
13 private long mTime;
14 private double mTempMax;
15 private String mSummary;
16 private String mTimezone;
17
18
19
20
21 public String getIcon() {
22 return mIcon;
23 }
24
25 public void setIcon(String icon) {
26 mIcon = icon;
27 }
28
29 public long getTime() {
30 return mTime;
31 }
32
33 public void setTime(long timee) {
34 mTime = timee;
35 }
36
37 public double getTempMax() {
38 return mTempMax;
39 }
40
41 public void setTempMax(double tempMax) {
42 mTempMax = tempMax;
43 }
44
45 public String getSummary() {
46 return mSummary;
47 }
48
49 public void setSummary(String summary) {
50 mSummary = summary;
51 }
52
53 public String getTimezone() {
54 return mTimezone;
55 }
56
57 public void setTimezone(String timezone) {
58 mTimezone = timezone;
59 }
60
61 public int getIconid(){
62 return Forecast.getIconId(getIcon());
63 }
64 public Day(){
65 }
66 public String getDaysOfWeek(){
67 SimpleDateFormat format = new SimpleDateFormat("EEEE");
68 format.setTimeZone(TimeZone.getTimeZone(mTimezone));
69 Date date = new Date(mTime*1000);
70 String day = format.format(date);
71 return day;
72
73 }
74
75 @Override
76 public int describeContents() {
77 return 0;
78 }
79
80 @Override
81 public void writeToParcel(Parcel dest, int flags) {
82 dest.writeString(mIcon);
83 dest.writeString(mSummary);
84 dest.writeLong(mTime);
85 dest.writeDouble(mTempMax);
86 dest.writeString(mTimezone);
87 }
88 protected Day(Parcel in) {
89 mIcon = in.readString();
90 mTime = in.readLong();
91 mTempMax = in.readDouble();
92 mSummary = in.readString();
93 mTimezone = in.readString();
94 }
95 public static final Creator<Day> CREATOR = new Creator<Day>() {
96 @Override
97 public Day createFromParcel(Parcel source) {
98 return new Day(source);
99 }
100
101 @Override
102 public Day[] newArray(int size) {
103 return new Day[size];
104 }
105 };
106}
107

DayAdapter.class
1 package com.stormy.adpaters;
2
3 import android.content.Context;
4 import android.view.LayoutInflater;
5 import android.view.View;
6 import android.view.ViewGroup;
7 import android.widget.BaseAdapter;
8 import android.widget.ImageView;
9 import android.widget.TextView;
10
11 import com.stormy.R;
12 import com.stormy.weather.Day;
13
14
15
16 public class DayAdapter extends BaseAdapter {
17 private Context mContext;
18 private Day[] mDays;
19 public DayAdapter(Context context , Day[]days){
20 mContext=context;
21 mDays = new Day[days.length];
22 mDays=days;
23 }
24 @Override
25 public int getCount() {
26 return mDays.length;
27 }
28
29 @Override
30 public Object getItem(int position) {
31 return mDays[position];
32 }
33
34 @Override
35 public long getItemId(int position) {
36 return 0;
37 }
38
39 @Override
40 public View getView(int position, View convertView, ViewGroup parent) {
41 ViewHolder holder;
42 if(convertView == null)
43 {
44 convertView = LayoutInflater.from(mContext).inflate(R.layout.daily_list_item,null);
45 holder = new ViewHolder();
46 holder.dayLabel = (TextView) convertView.findViewById(R.id.Days);
47 holder.iconImageView = (ImageView) convertView.findViewById(R.id.iconimage);
48 holder.temperaturelabel = (TextView) convertView.findViewById(R.id.templabel);
49
50 convertView.setTag(holder);
51 }
52 else{
53
54 holder = (ViewHolder) convertView.getTag();
55
56 }
57 Day day = mDays[position];
58 holder.iconImageView.setImageResource(day.getIconid());
59 holder.temperaturelabel.setText(day.getTempMax()+"");
60 holder.dayLabel.setText(day.getDaysOfWeek());
61
62 return convertView;
63 }
64 private static class ViewHolder {
65
66 ImageView iconImageView;
67 TextView temperaturelabel;
68 TextView dayLabel;
69
70 }
71 }
72

Error :
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.stormy, PID: 5138
java.lang.NullPointerException: id == null
at java.util.TimeZone.getTimeZone(TimeZone.java:393)
at com.stormy.weather.Day.getDaysOfWeek(Day.java:68)
at com.stormy.adpaters.DayAdapter.getView(DayAdapter.java:60)
at android.widget.AbsListView.obtainView(AbsListView.java:2346)
at android.widget.ListView.makeAndAddView(ListView.java:1875)
at android.widget.ListView.fillDown(ListView.java:702)
at android.widget.ListView.fillFromTop(ListView.java:763)
at android.widget.ListView.layoutChildren(ListView.java:1684)
at android.widget.AbsListView.onLayout(AbsListView.java:2148)
at android.view.View.layout(View.java:16630)
at android.view.ViewGroup.layout(ViewGroup.java:5437)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
at android.view.View.layout(View.java:16630)
at android.view.ViewGroup.layout(ViewGroup.java:5437)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
at android.view.View.layout(View.java:16630)
at android.view.ViewGroup.layout(ViewGroup.java:5437)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)
at android.view.View.layout(View.java:16630)
at android.view.ViewGroup.layout(ViewGroup.java:5437)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
at com.android.internal.policy.PhoneWindow$DecorView.onLayout(PhoneWindow.java:2678)
at android.view.View.layout(View.java:16630)
at android.view.ViewGroup.layout(ViewGroup.java:5437)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2171)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1931)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
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)

Can you turn on your line numbers and let us know what is at line 273 of MainActivity please?

Thanks,

Steve.

Mohammed Saif
Mohammed Saif
13,358 Points

i updated the error message.

at com.stormy.weather.Day.getDaysOfWeek(Day.java:68) format.setTimeZone(TimeZone.getTimeZone(mTimezone));

at com.stormy.adpaters.DayAdapter.getView(DayAdapter.java:60) holder.dayLabel.setText(day.getDaysOfWeek());

7 Answers

Hi there,

Try changing this line:

format.setTimeZone(TimeZone.getTimeZone(getTimezone()));

To:

format.setTimeZone(TimeZone.getTimeZone(mTimezone));

That's what my version looks like, although that code is pretty old now. The member variable mTimezone is set in the Day constructor.

Let me know if that helps.

Steve.

Failing that, update the error message and highlight the line of code which that refers to. Counting to line 68 in the code listing is a chore, so help me out!! :smile:

Mohammed Saif
Mohammed Saif
13,358 Points

I did it. Think it should be easier to discern now. Thank you!

It looks like mTimezone is containing nothing. Can you add a breakpoint to see what mTimezone contains as the code enters the GetDaysOfWeek() method? We than need to track that backwards to the point in the code where mTimezone gets its data from the Parcel. Let's start by checking that my assumption is correct first, though! So, let's check mTimezone is null, as above.

Steve.

OK - so we have the cause of the exception. Now, identify where the parcel gets opened and check what came out. I'm trying to get my app started so I can see how it should work.

Give me a minute, the emulator is taking its usual age to open and my Genymotion installation isn't working.

I'll be right back ...

Mohammed Saif
Mohammed Saif
13,358 Points

The mDays array passed to the DayAdapter through parameter has all the data except for the mTimezone. How do i populate mTimezone?

Can you post your MainActivity code; it's in there under getDailyForecast(). Maybe add a breakpoint in there to see if timezone has a value. [EDIT] I have checked that timezone is in the API feed. It is a string decribing the location of the lat/long pair.

MainActivity.java
    private Day[] getDailyForecast(String jsonData) throws JSONException {

        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        Day[] days = new Day[data.length()];
  .
  .
        for (int i = 0; i < data.length(); i++) {
            JSONObject jsonDay = data.getJSONObject(i);
            Day day = new Day();
    .
            day.setTimezone(timezone);
    .
    .
            days[i] = day;
        }

        return days;
    }

Steve.

Answer, above, edited ...

Mohammed Saif
Mohammed Saif
13,358 Points

I debugged and found out that the getDailyForecast() passed through intent.putextra has ALL the values. So does the local timezone variable, as you asked me to check. The thing is when I debug in the Day class I find that the data is missing, of both mSummary and mTimezone. Later I debugged the method where the parcel is wrapped, and other data were intact but mSummary and mTimezone were null.

MainActivity code :

1 package com.stormy.ui;
2
3 import android.content.Context;
4 import android.content.Intent;
5 import android.graphics.drawable.Drawable;
6 import android.net.ConnectivityManager;
7 import android.net.NetworkInfo;
8 import android.support.v7.app.AppCompatActivity;
9 import android.os.Bundle;
10 import android.util.Log;
11 import android.view.View;
12 import android.widget.CompoundButton;
13 import android.widget.ImageView;
14 import android.widget.ProgressBar;
15 import android.widget.TextView;
16 import android.widget.Toast;
17 import android.widget.ToggleButton;
18
19 import com.stormy.R;
20 import com.stormy.weather.Current;
21 import com.stormy.weather.Day;
22 import com.stormy.weather.Forecast;
23 import com.stormy.weather.Hour;
24
25 import org.json.JSONArray;
26 import org.json.JSONException;
27 import org.json.JSONObject;
28
29 import java.io.IOException;
30
31 import butterknife.BindView;
32 import butterknife.ButterKnife;
33 import butterknife.OnClick;
34 import okhttp3.Call;
35 import okhttp3.Callback;
36 import okhttp3.OkHttpClient;
37 import okhttp3.Request;
38 import okhttp3.Response;
39
40 public class MainActivity extends AppCompatActivity {
41 String TAG = MainActivity.class.getSimpleName();
42 public static String Daily_Forecast = "Daily_Forecast";
43
44
45 @BindView(R.id.timeLabel)
46 TextView mTimeLabel;
47 @BindView(R.id.temperatureLabel)
48 TextView mTemperatureLabel;
49 @BindView(R.id.humidityValue)
50 TextView mHumidityValue;
51 @BindView(R.id.precipValue)
52 TextView mPrecipValue;
53 @BindView(R.id.summaryLabel)
54 TextView mSummaryLabel;
55 @BindView(R.id.iconImageView)
56 ImageView mIconImageView;
57 @BindView(R.id.refreshImageView)
58 ImageView mRefresh;
59 @BindView(R.id.progressBar)
60 ProgressBar mProgressBar;
61 @BindView(R.id.toggleButton)
62 ToggleButton mToggleButton;
63
64
65 Current mCurrent;
66 Forecast mForecast;
67 Day mDay;
68
69 @Override
70 protected void onCreate(Bundle savedInstanceState) {
71 super.onCreate(savedInstanceState);
72 setContentView(R.layout.activity_main);
73 ButterKnife.bind(this);
74 mProgressBar.setVisibility(View.INVISIBLE);
75
76
77 mRefresh.setOnClickListener(new View.OnClickListener() {
78 @Override
79 public void onClick(View v) {
80
81 getForecast();
82
83 }
84 });
85
86 getForecast();
87 }
88
89 private void getForecast() {
90 String api = "b496d09c6039cc0868c683bcc9dccc7b";
91 double latitude = 12.9141;
92 double longitude = 74.8560;
93 String forecastUrl = "https://api.darksky.net/forecast/" + api + "/" + latitude + "," + longitude;
94 if (NetworkAvailable()) {
95 Refresh();
96
97 OkHttpClient client = new OkHttpClient();
98 Request request = new Request.Builder()
99 .url(forecastUrl)
100 .build();
101 Call call = client.newCall(request);
102 call.enqueue(new Callback() {
103 @Override
104 public void onFailure(Call call, IOException e) {
105 runOnUiThread(new Runnable() {
106 @Override
107 public void run() {
108 Refresh();
109 }
110 });
111 alertUser();
112 }
113
114 @Override
115 public void onResponse(Call call, Response response) throws IOException {
116 runOnUiThread(new Runnable() {
117 @Override
118 public void run() {
119 Refresh();
120 }
121 });
122 try {
123 String jsonData = response.body().string();
124 Log.v(TAG, jsonData);
125 if (response.isSuccessful()) {
126 mForecast = parseForecastDetails(jsonData);
127 runOnUiThread(new Runnable() {
128 @Override
129 public void run() {
130 updateDisplay();
131 }
132 });
133 } else {
134 alertUser();
135 }
136 } catch (IOException | JSONException e) {
137 Log.e(TAG, "Exception caught : ", e);
138 }
139 }
140
141
142 });
143
144 Log.d(TAG, "Main UI code is running");
145 Log.d(TAG, forecastUrl);
146
147
148 } else
149 Toast.makeText(this, "Network connection unavailable!", Toast.LENGTH_LONG).show();
150
151 }
152
153 private void Refresh() {
154 if (mProgressBar.getVisibility() == View.INVISIBLE) {
155 mProgressBar.setVisibility(View.VISIBLE);
156 mRefresh.setVisibility(View.INVISIBLE);
157 } else {
158 mProgressBar.setVisibility(View.INVISIBLE);
159 mRefresh.setVisibility(View.VISIBLE);
160 }
161 }
162
163 private void updateDisplay() {
164 Current current = mForecast.getCurrent();
165
166 mTemperatureLabel.setText((int) current.getTemp() + "");
167 mTimeLabel.setText("At " + current.getFormattedDate() + " it will be");
168 mHumidityValue.setText(current.getHumidity() + "");
169 mPrecipValue.setText(current.getPrecipChance() + "%");
170 mSummaryLabel.setText(current.getSummary());
171
172 Drawable drawable = getResources().getDrawable(current.getIconId());
173 mIconImageView.setImageDrawable(drawable);
174 }
175
176 private Forecast parseForecastDetails(String jsonData) throws JSONException {
177 Forecast forecast = new Forecast();
178
179 forecast.setCurrent(getCurrentDetails(jsonData));
180 forecast.setHourlyForecast(getHourlyForecast(jsonData));
181 forecast.setDailyForecast(getDailyForecast(jsonData));
182
183 return forecast;
184 }
185
186 private static Day[] getDailyForecast(String jsonData) throws JSONException {
187 JSONObject forecast = new JSONObject(jsonData);
188 String timezone = forecast.getString("timezone");
189
190 JSONObject daily = forecast.getJSONObject("daily");
191 JSONArray data = daily.getJSONArray("data");
192
193 Day[] days = new Day[data.length()];
194
195 for (int i = 0; i < data.length(); i++) {
196 JSONObject jsonDay = data.getJSONObject(i);
197 Day day = new Day();
198
199 day.setSummary(jsonDay.getString("summary"));
200 day.setIcon(jsonDay.getString("icon"));
201 day.setTempMax(jsonDay.getDouble("temperatureMax"));
202 day.setTime(jsonDay.getLong("time"));
203 day.setTimezone(timezone);
204
205 days[i] = day;
206 }
207
208 return days;
209 }
210
211 private Hour[] getHourlyForecast(String jsonData) throws JSONException {
212 JSONObject forecast = new JSONObject(jsonData);
213 String timezone = forecast.getString("timezone");
214 JSONObject hourly = forecast.getJSONObject("hourly");
215 JSONArray data = hourly.getJSONArray("data");
216
217 Hour[] hours = new Hour[data.length()];
218
219 for (int i = 0; i < data.length(); i++) {
220 JSONObject jsonHour = data.getJSONObject(i);
221 Hour hour = new Hour();
222
223 hour.setSummary(jsonHour.getString("summary"));
224 hour.setIcon(jsonHour.getString("icon"));
225 hour.setTemp(jsonHour.getDouble("temperature"));
226 hour.setTime(jsonHour.getLong("time"));
227 hour.setTimezone(timezone);
228
229 hours[i] = hour;
230 }
231
232 return hours;
233 }
234
235
236 private Current getCurrentDetails(String jsonData) throws JSONException {
237 JSONObject forecast = new JSONObject(jsonData);
238 String timezone = forecast.getString("timezone");
239 Log.i(TAG, "From JSON: " + timezone);
240
241 JSONObject currently = forecast.getJSONObject("currently");
242
243 Current current = new Current();
244 current.setHumidity(currently.getDouble("humidity"));
245 current.setTime(currently.getLong("time"));
246 current.setIcon(currently.getString("icon"));
247 current.setPrecipChance(currently.getDouble("precipProbability"));
248 current.setSummary(currently.getString("summary"));
249 current.setTemp(currently.getDouble("temperature"));
250 current.setTimeZone(timezone);
251
252 Log.d(TAG, current.getFormattedDate());
253
254 return current;
255 }
256
257
258 private boolean NetworkAvailable() {
259 ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
260 NetworkInfo networkInfo = manager.getActiveNetworkInfo();
261 boolean isAvailable = false;
262 if (networkInfo != null && networkInfo.isConnected())
263 isAvailable = true;
264 return isAvailable;
265
266
267 }
268
269 private void alertUser() {
270 AlertDialogFragment dialog = new AlertDialogFragment();
271 dialog.show(getFragmentManager(), "Error!");
272 }
273 @OnClick(R.id.dailyButton)
274 public void startDailyActivity(View view){
275 Intent intent = new Intent(this, DailyForecastActivity.class);
276 intent.putExtra(Daily_Forecast, mForecast.getDailyForecast());
277 startActivity(intent);
278
279 }
280
281
282 }
283
284
285

I'm off out for a while - I will get back to this later today. We're making good progress; you've clearly understood how to debug! I guess you need to follow the data and work out exactly where something isn't transferring over as you expect. Is it in the parcel or is a getter/setter failing for some reason? If we can work out where the vriables change from holding a value to holding null, we know precisely where to look for a code issue. You can compare to my code by looking at my Github repo here. This code works and has a few extras, like geo-location. It uses the old API, though, so don't change that in your code!!

Change one thing at once and test. Understand what you expect to see and define what you actually see. This helps narrow the problem and define it in words. Being able to expres a problem in language, rather than code/error messages can help it become clearer, I find.

Anyway; good luck - I'll be back later. Do let me know if you make any progress.

Mohammed Saif
Mohammed Saif
13,358 Points

I compared the codes and I still couldn't find anything. Data in MainActivity seems to be fine. There must be something wrong with getters/setters in Day class. I got no clue about how to get data to show in the UI.

Hi - can you push all your code into a Github repo? I can then replicate the error here (if my Android Studio would work!!) and see if I can figure out the problem. It'll be something really simple, I'm sure - there's just a lot to search through. A rough guide is here.

Mohammed Saif
Mohammed Saif
13,358 Points

Thanks for the reference site. Here is my project.

:+1:

One thing I've noticed in MainActivity. This may be due to a newer version of ButterKnife being used in your app. However, In my code, the TextViews are created with @InjectView not @BindView:

@InjectView(R.id.timeLabel) TextView mTimeLabel;

Similarly, within onCreate, ButterKnife is hooked up using ButterKnife.inject(this); not what you have, which is ButterKnife.bind(this);. It might be worth checking that this is correct. I don't think it relates to the problem in hand but you never know ... the issue is arising when a label is trying to be set.

Mohammed Saif
Mohammed Saif
13,358 Points

So you mean, I have to manually assign them by using findViewById. Right?

Leave this for now ... I don't think it's related to the key issue. One thing at a time!

Another thing; I think your issue might be at line 151 of MainActivity. You have hard coded the string "Asia/Kolkata" as the parameter for getTimezone(). This is also the returned value that your code is looking for; this is the JSON key that you're trying to access. So, in Dark Sky API, the JSON looks like:

"latitude": 12.9,
"longitude": 74.8,
"timezone": "Asia/Kolkata",
"offset": 5.5,

So, you could try changing:

format.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));

to:

format.setTimeZone("Asia/Kolkata");

Or you could check back through the video to see what you app should be doing - it may be correct as-is but that seems strange. This is probably achieved using Java.util.Locale. Check the video, or look at my code. The getTimezone method is expecting a timezone, like 'GMT+8", although it can take a string the same as your code does - so this is just a guess!!

Perhaps assign that expression to a variable first to test what it is bringing back. Have a try at:

String zoneTest = TimeZone.getTimeZone("Asia/Kolkata");
format.setTimeZone(zoneTest);

Make sense? You can see if it is null that way. If it is, we can have a think about a solution there.

Next check what the timezone variable at line 197 holds during run-time. Is that null or does it contain a value? What is that value?

Lastly, for now, what does mTimezone hold at line 55 of Day.java?? That should not be null; I can't see how it can be.

I still can't run the repo as my Android Studio is being irritating. I would have done all the above myself if I could run it!!

Steve.

Mohammed Saif
Mohammed Saif
13,358 Points

I figured it out. The variables in these methods

  @Override                                                                     
    public void writeToParcel(Parcel parcel, int flags) {                                                                      
      parcel.writeString(mIcon);                                                                     
        parcel.writeString(mSummary);                                                                     
        parcel.writeLong(mTime);                                                                     
        parcel.writeDouble(mTempMax);                                                                     
        parcel.writeString(mTimezone);                                                                     
    }                                                                     
    protected Day(Parcel in) {                                                                     
        mIcon = in.readString();                                                                     
        mSummary = in.readString();                                                                     
        mTime = in.readLong();                                                                     
        mTempMax = in.readDouble();                                                                     
        mTimezone = in.readString();                                                                     
    }  

were in different order and after correcting them, BAM! Everything is right now.

Good work - well done!