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 trialYeeka Yau
7,410 PointsApp unfortunately has stopped when 7 Days Button is tapped
Hi, my app is crashing when I tap on the 7 Days button. Running the debugger, I seem to have data at the point of intent.putExtra(), but it seems that in the DailyForecastActivity the Parceable object is empty. The debugger has a message saying that the parcelables.length = java.lang.NullPointerException:
Not sure how to trouble shoot this further...
Thanks for any help you can give me in advance!!
8 Answers
James Simshaw
28,738 PointsHello,
I'm sorry I didn't see this sooner, but it was much easier to find once I had the code in front of me in a full and testable manner. In your creator, you have
private static final Creator<DailyForecast> CREATOR = new Creator<DailyForecast>() {
@Override
public DailyForecast createFromParcel(Parcel source) {
return new DailyForecast(source);
}
@Override
public DailyForecast[] newArray(int size) {
return new DailyForecast[size];
}
};
Change it from private to public and you should be able to see the 7 day activity.
Yeeka Yau
7,410 PointsOh wow, thanks so much - that has solved the problem. Can't thank you enough, not sure when I would have been able to pick up on that - especially since there were no error messages. Definitely need to read up more on this Creator thing, seemed like we just implemented it, and never really used it.
Thanks again for all the effort.
James Simshaw
28,738 PointsYou're welcome. Have fun with Android development.
Joseph Ngo
10,628 PointsI have this same problem and my Creater class is public so I don't know what else is going on. I do know that my array being parced into DailyForcastActivity is empty though from debugging.
James Simshaw
28,738 PointsHello,
WIth just the error message given, its likely that parcelables is never assigned a value and thus is null, so when you call parcelables.length, it gives the NullPointerException.
James Simshaw
28,738 PointsHello,
I think the problem is that in your DailyForecastActivity, you are calling
// Get the data from main activity
Intent intent = new Intent();
Parcelable[] parcelables = intent.getParcelableArrayExtra(MainActivity.DAILY_FORECAST);
mDays = Arrays.copyOf(parcelables, parcelables.length, DailyForecast[].class);
This is creating an empty intent. To get the calling intent, you need to use getIntent() so your code would become
// Get the data from main activity
Intent intent = getIntent();
Parcelable[] parcelables = intent.getParcelableArrayExtra(MainActivity.DAILY_FORECAST);
mDays = Arrays.copyOf(parcelables, parcelables.length, DailyForecast[].class);
Yeeka Yau
7,410 PointsHi James, Thanks a lot. I thought for sure that would have fixed my problem! But the debugger still has mDays as null, however, I am not getting the null pointer exception on the parcelables object. Now I'm really confused...Any ideas? Thanks again for your effort.
James Simshaw
28,738 PointsmDays is null, but parcelables has data after the line
mDays = Arrays.copyOf(parcelables, parcelables.length, DailyForecast[].class);
Do you have the constructor, Creator, and writeToParcel function written working? Could you post your DailyForcast class file?
Yeeka Yau
7,410 PointsHere is my DailyForecast class:
import android.os.Parcel;
import android.os.Parcelable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class DailyForecast implements Parcelable{
private long mTime;
private String mSummary;
private double mTemperatureMax;
private String mIcon;
private String mTimezone;
// A default public constructor
public DailyForecast(){ }
public long getTime() {
return mTime;
}
public void setTime(long time) {
mTime = time;
}
public String getSummary() {
return mSummary;
}
public void setSummary(String summary) {
mSummary = summary;
}
public int getTemperatureMax() {
return (int) Math.round(mTemperatureMax);
}
public void setTemperatureMax(double temperatureMax) {
mTemperatureMax = temperatureMax;
}
public String getIcon() {
return mIcon;
}
public void setIcon(String icon) {
mIcon = icon;
}
public String getTimezone() {
return mTimezone;
}
public void setTimezone(String timezone) {
mTimezone = timezone;
}
public int getIconId(){
return Forecast.getIconId(mIcon);
}
public String getDayOfTheWeek(){
SimpleDateFormat formatter = new SimpleDateFormat("EEEE");
formatter.setTimeZone(TimeZone.getTimeZone(mTimezone));
Date dateTime = new Date(mTime * 1000);
return formatter.format(dateTime);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mTime);
dest.writeString(mSummary);
dest.writeDouble(mTemperatureMax);
dest.writeString(mIcon);
dest.writeString(mTimezone);
}
// This method 'unpacks' the parcel with data
private DailyForecast(Parcel in){
// Here the order needs to match the writeToParcel method
mTime = in.readLong();
mSummary = in.readString();
mTemperatureMax = in.readDouble();
mIcon = in.readString();
mTimezone = in.readString();
}
private static final Creator<DailyForecast> CREATOR = new Creator<DailyForecast>() {
@Override
public DailyForecast createFromParcel(Parcel source) {
return new DailyForecast(source);
}
@Override
public DailyForecast[] newArray(int size) {
return new DailyForecast[size];
}
};
}
James Simshaw
28,738 PointsI'm not seeing anything jumping out at me right now that could be wrong with the code as given. Could you try without the debugger and let me know what happens when you press the 7 days button now? I'll take another look through after I've gotten some sleep and see if fresh eyes will show something new.
Yeeka Yau
7,410 PointsNo worries, thanks James. I still haven't managed to get the app to run, but I feel like the getIntent() should have fixed it. Now, when I run the debugger, I got an error message: no such instance field: 'persistentState' - however after googling - does seem like I can find anythiing. I have tried rebuilding and cleaning my project but no luck.
James Simshaw
28,738 PointsWhat is the behavior without the debugger, still crashes? If so, could you get the new error message? If not, what behavior does it exhibit? I'm wondering if at this point the debugger is causing a problem. Even if it isn't, knowing the current behavior without the debugger doing things could point towards the problem, especially if there's an exception that gets printed out to logcat.
Yeeka Yau
7,410 PointsHi James,
I have run the program without the debugger and when I tap on the 7 Days button the app just crashes with: Unfortunately CityWeather has stopped (I have called my version CityWeather).
But I don't get any exceptions/error messages in the logcat. I am running the app on my phone as opposed to emulators - if that adds any additional info.
The closest I have found to my error is this: http://stackoverflow.com/questions/28646999/no-such-instance-field
I'm suspecting whether I have some old files, or some cached data from previous which is causing the error? Because your catch of the getIntent() line made perfect sense given what I was experiencing with the debugger.
I have cleaned and rebuilt the code, restarted Android Studio obviously but nothing prevails. Is there any other way to 'clean' out the code?
My error message: no such instance field: 'persistentState' doesn't come up on the logcat. It came up with the debugger.
James Simshaw
28,738 PointsWould you be able to put the whole project on GitHub so I can take a look at everything as a whole?
Yeeka Yau
7,410 PointsThanks for the help James, here is my main activity code. Please note that I have called the Day object DailyForecast instead:
public class MainActivity extends ActionBarActivity {
public static final String TAG = MainActivity.class.getSimpleName();
public static final String DAILY_FORECAST = "DAILY_FORECAST";
private Forecast mForecast;
@InjectView(R.id.summaryTextView) TextView mSummaryTextView;
@InjectView(R.id.temperatureLabel) TextView mTemperatureLabel;
@InjectView(R.id.timeTextView) TextView mTimeTextView;
@InjectView(R.id.precipLabel) TextView mPrecipLabel;
@InjectView(R.id.precipValue) TextView mPrecipValue;
@InjectView(R.id.iconImageView) ImageView mIconImageView;
@InjectView(R.id.humidityLabel) TextView mHumidityLabel;
@InjectView(R.id.humidityValue) TextView mHumidityValue;
@InjectView(R.id.locationTextView) TextView mLocationTextView;
@InjectView(R.id.refreshImageView) ImageView mRefreshImageView;
@InjectView(R.id.progressBar) ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mProgressBar.setVisibility(View.INVISIBLE);
final double latitude = 33.8863;
final double longitude = 151.1999;
mRefreshImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getForecast(latitude, longitude);
}
});
getForecast(latitude, longitude);
}
private void getForecast(double latitude, double longitude) {
OkHttpClient client = new OkHttpClient();
String apiKey = "870b0ec7496e45c7ceb728f251edf1ed";
// Create a request object
Request request = new Request.Builder()
.url("https://api.forecast.io/forecast/" + apiKey +"/" + latitude +"," + longitude)
.build();
//Check whether the network is available first
if(networkIsAvailable()) {
toggleRefresh();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError("Sorry", "There appears to be no response from the server.");
}
@Override
public void onResponse(Response response) throws IOException {
try {
//First put all JSON data to a string
String jsonData = response.body().string();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
@Override
public void run() {
toggleRefresh();
updateDisplay();
}
});
} else {
alertUserAboutError("Sorry", "There appears to be no response from the server.");
}
} // Here we catch different exceptions
catch (IOException e) {
Log.e(TAG, "Exception caught: " + e);
}
catch (JSONException e){
Log.e(TAG, "Exception caught: " + e);
}
}
});
} else {
alertUserAboutError("Network Error", "There appears to be Connection Problem. Please check your " +
"Network Connection settings");
}
Log.d(TAG, "Main UI Code is running");
}
private void toggleRefresh() {
if(mProgressBar.getVisibility() == View.INVISIBLE) {
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImageView.setVisibility(View.INVISIBLE);
} else {
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImageView.setVisibility(View.VISIBLE);
}
}
private void updateDisplay() {
CurrentForecast current = mForecast.getCurrentForecast();
mSummaryTextView.setText(current.getSummary());
mHumidityValue.setText(current.getHumidity() + "");
mPrecipValue.setText(current.getPrecipChance() + "%");
mTimeTextView.setText(current.getFormattedTime() + "");
mLocationTextView.setText(current.getTimezone());
mTemperatureLabel.setText(current.getTemperature() + "");
Drawable iconDrawable = getResources().getDrawable(current.getIconId());
mIconImageView.setImageDrawable(iconDrawable);
}
private Forecast parseForecastDetails(String jsonData)throws JSONException{
Forecast forecast = new Forecast();
//Sets the current object in the forecast by using the original method below.
forecast.setCurrentForecast(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyDetails(jsonData));
forecast.setDailyForecast(getDailyDetails(jsonData));
return forecast;
}
private DailyForecast[] getDailyDetails(String jsonData)throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray dailyData = daily.getJSONArray("data");
DailyForecast[] dailyForecasts = new DailyForecast[dailyData.length()];
for(int i = 0; i < dailyData.length(); i++){
JSONObject jsonDay = dailyData.getJSONObject(i);
// Create a new daily forecast and set it up - then plug into the DailyForecast array
DailyForecast dailyForecast = new DailyForecast();
dailyForecast.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
dailyForecast.setTime(jsonDay.getLong("time"));
dailyForecast.setSummary(jsonDay.getString("summary"));
dailyForecast.setIcon(jsonDay.getString("icon"));
dailyForecast.setTimezone(timezone);
dailyForecasts[i]= dailyForecast;
}
return dailyForecasts;
}
private HourlyForecast[] getHourlyDetails(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray hourlyData = hourly.getJSONArray("data");
// Create a new array of HourForecast objects and set it to the length of the array of jsonobjects in data
HourlyForecast[] hours = new HourlyForecast[hourlyData.length()];
for(int i=0; i < hourlyData.length(); i++ ){
// Get the jsonObject at i
JSONObject jsonHour = hourlyData.getJSONObject(i);
// Each time around the loop we are creating a new Hour object and putting it into the HourlyForecast array
HourlyForecast hour = new HourlyForecast();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon("icon");
hour.setTime(jsonHour.getLong("time"));
hour.setTimezone(timezone);
hours[i] = hour;
}
return hours;
}
private CurrentForecast getCurrentDetails(String jsonData) throws JSONException{
JSONObject currentData = new JSONObject(jsonData);
String timezone = currentData.getString("timezone"); //gets this data using a key in the json file
Log.i(TAG, "Json Data check " + timezone); //test that we get the data back
JSONObject currently = currentData.getJSONObject("currently");
CurrentForecast currentForecast = new CurrentForecast();
currentForecast.setSummary(currently.getString("summary"));
currentForecast.setIcon(currently.getString("icon"));
currentForecast.setHumidity(currently.getDouble("humidity"));
currentForecast.setTime(currently.getLong("time"));
currentForecast.setTemperature(currently.getDouble("temperature"));
currentForecast.setPrecipChance(currently.getDouble("precipProbability"));
currentForecast.setTimezone(timezone);
Log.d(TAG, currentForecast.getFormattedTime());
return currentForecast;
}
private void alertUserAboutError(String title, String message) {
Bundle errorMessages = new Bundle();
errorMessages.putString(AlertDialogFragment.TITLE_ID, title);
errorMessages.putString(AlertDialogFragment.MESSAGE_ID, message);
AlertDialogFragment dialog = new AlertDialogFragment();
dialog.setArguments(errorMessages);
dialog.show(getFragmentManager(),"error_dialog");
}
public boolean networkIsAvailable(){
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if(networkInfo != null && networkInfo.isConnected()){
isAvailable = true;
}
return isAvailable;
}
@OnClick(R.id.sevenDayButton)
public void startDailyForecastActivity(View view){
Intent intent = new Intent(this, DailyForecastActivity.class);
intent.putExtra(DAILY_FORECAST, mForecast.getDailyForecast()); // the first argument is the key, next is the data being passed
startActivity(intent);
}
}
Yeeka Yau
7,410 PointsAnd here is the code from the DailyForecastActivity:
public class DailyForecastActivity extends ListActivity {
private DailyForecast[] mDays;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_daily_forecast);
// Get the data from main activity
Intent intent = new Intent();
Parcelable[] parcelables = intent.getParcelableArrayExtra(MainActivity.DAILY_FORECAST);
mDays = Arrays.copyOf(parcelables, parcelables.length, DailyForecast[].class);
DayAdapter adapter = new DayAdapter(this, mDays);
setListAdapter(adapter);
}
}
Yeeka Yau
7,410 PointsHi James no problem. I'll get it uploaded, do you by any chance have a bitbucket account? I already have it on there - if you do. Otherwise I can create a Github account.
James Simshaw
28,738 PointsI currently don't have a bitbucket account. Though I should be able to download the source anyhow so it will work.
Yeeka Yau
7,410 PointsHi James,
Would you mind sharing your email address with me? I can send you an invite to my repo in Bitbucket.
James Simshaw
28,738 PointsHello,
My username on bitbucket is simshawj
Yeeka Yau
7,410 PointsHi James, I have sent you an invite to my repo. Thanks so much! Sorry in advance for the messy code, I have commented the crap out of it.
anassrigolade
Courses Plus Student 1,959 Pointsi'm getting the same error, but nothing shows up on the debugger ... and all seems to be fine with no errors on my project... i just can't figure out what's going wrong.. any suggestions guys?
James Simshaw
28,738 PointsJames Simshaw
28,738 PointsCould you post your code? That will make it a lilttle easier in trying to find a solution.