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 Android Lists and Adapters (2015) Using Parcelable Data Retrieving Parcelable Data

Yeeka Yau
Yeeka Yau
7,410 Points

App 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!!

Could you post your code? That will make it a lilttle easier in trying to find a solution.

8 Answers

Hello,

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
Yeeka Yau
7,410 Points

Oh 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.

You're welcome. Have fun with Android development.

Joseph Ngo
Joseph Ngo
10,020 Points

I 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.

Hello,

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.

Hello,

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
Yeeka Yau
7,410 Points

Hi 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.

mDays 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
Yeeka Yau
7,410 Points

Here 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];
        }
    };

}

I'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
Yeeka Yau
7,410 Points

No 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.

What 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
Yeeka Yau
7,410 Points

Hi 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.

Would you be able to put the whole project on GitHub so I can take a look at everything as a whole?

Yeeka Yau
Yeeka Yau
7,410 Points

Thanks 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
Yeeka Yau
7,410 Points

And 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
Yeeka Yau
7,410 Points

Hi 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.

I currently don't have a bitbucket account. Though I should be able to download the source anyhow so it will work.

Yeeka Yau
Yeeka Yau
7,410 Points

Hi James,

Would you mind sharing your email address with me? I can send you an invite to my repo in Bitbucket.

Hello,

My username on bitbucket is simshawj

Yeeka Yau
Yeeka Yau
7,410 Points

Hi 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
PLUS
anassrigolade
Courses Plus Student 1,959 Points

i'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?