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

weather app help URGENT!!

I think I followed the android course almost identically step by step, and I'm on <Hooking Up the Model to the View> stage right now, and when I try to run the code with the geny motion emulator, it stops after 2 seconds and says "Unfortunately stormy has stopped".

this is my logcat

09-21 21:10:06.098 10805-10805/com.example.jaewoobyun.stormy D/MainActivity﹕ Main UI code is running! 09-21 21:10:07.666 10805-10834/com.example.jaewoobyun.stormy V/MainActivity﹕ {"latitude":37.8267,"longitude":-122.423,"timezone":"America/Los_Angeles","offset":-7,"currently":{"time":1442884206,"summary":"Clear","icon":"clear-day","nearestStormDistance":2,"nearestStormBearing":241,"precipIntensity":0,"precipProbability":0,"temperature":72.44,"apparentTemperature":72.44,"dewPoint":55.03,"humidity":0.54,"windSpeed":10.18,"windBearing":269,"visibility":9.81,"cloudCover":0.24,"pressure":1006.93,"ozone":281.02},"minutely":{"summary":"Clear for the hour.","icon":"clear-day","data":[{"time":1442884200,"precipIntensity":0,"precipProbability":0},{"time":1442884260,"precipIntensity":0,"precipProbability":0},{"time":1442884320,"precipIntensity":0,"precipProbability":0},{"time":1442884380,"precipIntensity":0,"precipProbability":0},{"time":1442884440,"precipIntensity":0,"precipProbability":0},{"time":1442884500,"precipIntensity":0,"precipProbability":0},{"time":1442884560,"precipIntensity":0,"precipProbability":0},{"time":1442884620,"precipIntensity":0,"precipProbability":0},{"time":1442884680,"precipIntensity":0,"precipProbability":0},{"time":1442884740,"precipIntensity":0,"precipProbability":0},{"time":1442884800,"precipIntensity":0,"precipProbability":0},{"time":1442884860,"precipIntensity":0,"precipProbability":0},{"time":1442884920,"precipIntensity":0,"precipProbability":0},{"time":1442884980,"precipIntensity":0,"precipProbability":0},{"time":1442885040,"precipIntensity":0,"precipProbability":0},{"time":1442885100,"precipIntensity":0,"precipProbability":0},{"time":1442885160,"precipIntensity":0,"precipProbability":0},{"time":1442885220,"precipIntensity":0,"precipProbability":0},{"time":1442885280,"precipIntensity":0,"precipProbability":0},{"time":1442885340,"precipIntensity":0,"precipProbability":0},{"time":1442885400,"precipIntensity":0,"precipProbability":0},{"time":1442885460,"precipIntensity":0,"precipProbability":0},{"time":1442885520,"precipIntensity":0,"precipProbability":0},{"time":1442885580,"precipIntensity":0,"precipProbability":0},{"time":1442885640,"precipIntensity":0,"precipProbability":0},{"time":1442885700,"precipIntensity":0,"precipProbability":0},{"time":1442885760,"precipIntensity":0,"precipProbability":0},{"time":1442885820,"precipIntensity":0,"precipProbability":0},{"time":1442885880,"precipIntensity":0,"precipProbability":0},{"time":1442885940,"precipIntensity":0,"precipProbability":0},{"time":1442886000,"precipIntensity":0,"precipProbability":0},{"time":1442886060,"precipIntensity":0,"precipProbability":0},{"time":1442886120,"precipIntensity":0,"precipProbability":0},{"time":1442886180,"precipIntensity":0,"precipProbability":0},{"time":1442886240,"precipIntensity":0,"precipProbability":0},{"time":1442886300,"precipIntensity":0,"precipProbability":0},{"time":1442886360,"precipIntensity":0,"precipProbability":0},{"time":1442886420,"precipIntensity":0,"precipProbability":0},{"time":1442886480,"precipIntensity":0,"precipProbability":0},{"time":1442886540,"precipIntensity":0,"precipProbability":0},{"time":1442886600,"precipIntensity":0,"precipProbability":0},{"time":1442886660,"precipIntensity":0,"precipProbability":0},{"time":1442886720,"precipIntensity":0,"precipProbability":0},{"time":1442886780,"precipIntensity":0,"precipProbability":0},{"time":1442886840,"precipIntensity":0,"precipProbability":0},{"time":1442886900,"precipIntensity":0,"precipProbability":0},{"time":1442886960,"precipIntensity":0,"precipProbability":0},{"time":1442887020,"precipIntensity":0,"precipProbability":0},{"time":1442887080,"precipIntensity":0,"precipProbability":0},{"time":1442887140,"precipIntensity":0,"precipProbability":0},{"time":1442887200,"precipIntensity":0,"precipProbability":0},{"time":1442887260,"precipIntensity":0,"precipProbability":0},{"time":1442887320,"precipIntensity":0,"precipProbability":0},{"time":1442887380,"precipIntensity":0,"precipProbability":0},{"time":1442887440,"precipIntensity":0,"precipProbability":0},{"time":1442887500,"precipIntensity":0,"precipProbability":0},{"time":1442887560,"precipIntensity":0,"precipProbability":0},{"time":1442887620,"p 09-21 21:10:07.738 10805-10834/com.example.jaewoobyun.stormy I/MainActivity﹕ From JSON: America/Los_Angeles 09-21 21:10:07.738 10805-10834/com.example.jaewoobyun.stormy D/MainActivity﹕ 6:10 PM 09-21 21:10:07.742 10805-10805/com.example.jaewoobyun.stormy E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.example.jaewoobyun.stormy, PID: 10805 java.lang.NullPointerException: id == null at java.util.TimeZone.getTimeZone(TimeZone.java:328) at com.example.jaewoobyun.stormy.weather.Current.getFormattedTime(Current.java:81) at com.example.jaewoobyun.stormy.ui.MainActivity.updateDisplay(MainActivity.java:147) at com.example.jaewoobyun.stormy.ui.MainActivity.access$500(MainActivity.java:32) at com.example.jaewoobyun.stormy.ui.MainActivity$2$3.run(MainActivity.java:115) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5001) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)

Ben Deitch
Ben Deitch
Treehouse Teacher

Looks like a Null Pointer Exception at line 79 of CurrentWeather.java. Could you post your CurrentWeather.java to help us figure out what's going on?

package com.example.jaewoobyun.stormy.weather;

import com.example.jaewoobyun.stormy.R;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * Created by JaewooByun on 15. 9. 19..
 */
public class Current {
    private String mIcon;
    private long mTime;
    private double mTemperature;
    private double mHumidity;
    private double mPrecipChance;
    private String mSummary;
    private String mTimezone;

    public String getTimezone() {
        return mTimezone;
    }

    public void setTimezone(String timezone) {
        mTimezone = timezone;
    }

    public String getIcon() {
        return mIcon;
    }

    public void setIcon(String icon) {
        mIcon = icon;
    }

    public int getIconId() {
        //clear-day, clear-night, rain, snow, sleet, wind, fog, cloudy, partly-cloudy-day, or partly-cloudy-night
        int iconId = R.drawable.clear_day;

        if (mIcon.equals("clear-day")) {
            iconId = R.drawable.clear_day;
        }
        else if (mIcon.equals("clear-night")) {
            iconId = R.drawable.clear_night;
        }
        else if (mIcon.equals("rain")) {
            iconId = R.drawable.rain;
        }
        else if (mIcon.equals("snow")) {
            iconId = R.drawable.snow;
        }
        else if (mIcon.equals("sleet")) {
            iconId = R.drawable.sleet;
        }
        else if (mIcon.equals("wind")) {
            iconId = R.drawable.wind;
        }
        else if (mIcon.equals("fog")) {
            iconId = R.drawable.fog;
        }
        else if (mIcon.equals("cloudy")) {
            iconId = R.drawable.cloudy;
        }
        else if (mIcon.equals("partly-cloudy-day")) {
            iconId = R.drawable.partly_cloudy;
        }
        else if (mIcon.equals("partly-cloudy-night")) {
            iconId = R.drawable.cloudy_night;
        }

        return iconId;
    }

    public long getTime() {
        return mTime;
    }

    public String getFormattedTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("h:mm a");
        formatter.setTimeZone(TimeZone.getTimeZone(getTimezone()));
        Date dateTime = new Date(getTime() * 1000);
        String timeString = formatter.format(dateTime);

        return timeString;
    }

    public void setTime(long time) {
        mTime = time;
    }

    public int getTemperature() {
        return (int)Math.round(mTemperature);
    }

    public void setTemperature(double temperature) {
        mTemperature = temperature;
    }

    public double getHumidity() {
        return mHumidity;
    }

    public void setHumidity(double humidity) {
        mHumidity = humidity;
    }

    public int getPrecipChance() {
        double precipPercentage = mPrecipChance * 100;
        return (int) Math.round(precipPercentage);
    }

    public void setPrecipChance(double precipChance) {
        mPrecipChance = precipChance;
    }

    public String getSummary() {
        return mSummary;
    }

    public void setSummary(String summary) {
        mSummary = summary;
    }
}

How did you know right away that there is a problem at line 79? could you teach me how to look at the logcat and figure out what went wrong?

Ben Deitch
Ben Deitch
Treehouse Teacher

Hmm not sure where I got line 79 as it looks like line 81 now. Anywho, at the bottom of your logcat Android logged an error:

... 10805/com.example.jaewoobyun.stormy E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.example.jaewoobyun.stormy, PID: 10805 java.lang.NullPointerException: id == null at java.util.TimeZone.getTimeZone(TimeZone.java:328) at com.example.jaewoobyun.stormy.weather.Current.getFormattedTime(Current.java:81) at com.example.jaewoobyun.stormy.ui.MainActivity.updateDisplay(MainActivity.java:147) at ...

This tells us there was a NullPointerException in TimeZone.java on line 328 in the getTimeZone function. Since this class is from the java.util package we can be reasonably sure that's not where the error is. Moving up the call stack from there we get to Current.java line 81 inside the getFormattedTime function. Since this is a class we made it's a good place to start looking for the error. Line 81 inside Current.java is:

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

We know that calling TimeZone.getTimeZone gives us a NullPointerException. This means that getTimezone is probably returning null, and this can only happen if setTimezone was never called. I'm guessing in the getCurrentDetails method in MainActivity.java you're either missing a call to setTimeZone() or you're returning a new CurrentWeather object.

Does you're getCurrentDetails look like the one in the project files:

    private CurrentWeather getCurrentDetails(String jsonData) throws JSONException {
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        Log.i(TAG, "From JSON: " + timezone);

        JSONObject currently = forecast.getJSONObject("currently");

        CurrentWeather currentWeather = new CurrentWeather();
        currentWeather.setHumidity(currently.getDouble("humidity"));
        currentWeather.setTime(currently.getLong("time"));
        currentWeather.setIcon(currently.getString("icon"));
        currentWeather.setPrecipChance(currently.getDouble("precipProbability"));
        currentWeather.setSummary(currently.getString("summary"));
        currentWeather.setTemperature(currently.getDouble("temperature"));
        currentWeather.setTimeZone(timezone);

        Log.d(TAG, currentWeather.getFormattedTime());

        return currentWeather;
    }

4 Answers

Ben Deitch
STAFF
Ben Deitch
Treehouse Teacher

You're never assigning a value to mCurrent. When you call mCurrent.getFormattedTime() (in updateDisplay) mCurrent is null which means its' Timezone is also null (i.e. getTimezone() returns null). And java's TimeZone.getTimeZone throws an error if we give it a null timezone.

thank you so much. I really really appreciate it :)

yes it looks exactly the same except CurrentWeather is current now.. it still isn't working..

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

is this line causing the problem?

Ben Deitch
STAFF
Ben Deitch
Treehouse Teacher

Yep, but it's only causing the problem because it (getTimeZone) is getting bad input.

Could you post your MainActivity.java too?

package com.example.jaewoobyun.stormy.ui;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.example.jaewoobyun.stormy.R;
import com.example.jaewoobyun.stormy.weather.Current;
import com.example.jaewoobyun.stormy.weather.Day;
import com.example.jaewoobyun.stormy.weather.Forecast;
import com.example.jaewoobyun.stormy.weather.Hour;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import butterknife.ButterKnife;
import butterknife.InjectView;

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private Current mCurrent;

    private Forecast mForecast;

    @InjectView(R.id.timeLabel) TextView mTimeLabel;
    @InjectView(R.id.temperatureLabel) TextView mTemperatureLabel;
    @InjectView(R.id.humidityValue) TextView mHumidityValue;
    @InjectView(R.id.precipValue) TextView mPrecipValue;
    @InjectView(R.id.summaryLabel) TextView mSummaryLabel;
    @InjectView(R.id.iconImageView) ImageView mIconImageView;
    @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 = 37.8267;
        final double longitude = -122.423;

        mRefreshImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getForecast(latitude, longitude);
            }
        });

        getForecast(latitude, longitude);

        Log.d(TAG, "Main UI code is running!");
    }

    private void getForecast(double latitude, double longitude) {
        String apiKey = "71a21e692c60ce454016f7f77df6411a";

        String forecastUrl = "https://api.forecast.io/forecast/" + apiKey + "/" + latitude + "," + longitude + "";

        if (isNetworkAvailable()) {
            toggleRefresh();

            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(forecastUrl)
                    .build();

            Call call = client.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Request request, IOException e) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            toggleRefresh();
                        }
                    });
                    alertUserAboutError();
                }

                @Override
                public void onResponse(Response response) throws IOException {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            toggleRefresh();
                        }
                    });

                    try {
                        String jsonData = response.body().string();

                        Log.v(TAG, jsonData);
                        if (response.isSuccessful()) {
                            mForecast = parseForecastDetails(jsonData);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    updateDisplay();
                                }
                            });

                        } else {
                            alertUserAboutError();
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "Exception caught: ", e);
                    } catch (JSONException e) {
                        Log.e(TAG, "Exception caught: ", e);
                    }
                }
            });
        } else {
            Toast.makeText(this, R.string.network_unavailable_message, Toast.LENGTH_LONG).show();
        }
    }

    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() {
        Current current = mForecast.getCurrent();

        mTemperatureLabel.setText(mCurrent.getTemperature() + "");
        mTimeLabel.setText("At " + mCurrent.getFormattedTime() + " it will be");
        mHumidityValue.setText(mCurrent.getHumidity() + "");
        mPrecipValue.setText(mCurrent.getPrecipChance() + "%");
        mSummaryLabel.setText(mCurrent.getSummary());

        Drawable drawable = getResources().getDrawable(mCurrent.getIconId());
        mIconImageView.setImageDrawable(drawable);

    }

    private Forecast parseForecastDetails(String jsonData) throws JSONException {
        Forecast forecast = new Forecast();

        forecast.setCurrent(getCurrentDetails(jsonData));
        forecast.setHourlyForecast(getHourlyForecat(jsonData));
        forecast.setDailyForecast(getDailyForecast(jsonData));

        return forecast;
    }

    private Day[] getDailyForecast(String jsonData) throws JSONException{
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        JSONObject daily = forecast.getJSONObject("daily");
        JSONArray data = daily.getJSONArray("data");

        Day[] days = new Day[data.length()];

        for (int i = 0; i < data.length(); i++) {
            JSONObject jsonDay = data.getJSONObject(i);
            Day day = new Day();

            day.setSummary(jsonDay.getString("summary"));
            day.setIcon(jsonDay.getString("icon"));
            day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
            day.setTime(jsonDay.getLong("time"));
            day.setTimezone(timezone);

            days[i] = day;
        }

        return days;
    }

    private Hour[] getHourlyForecat(String jsonData) throws JSONException{
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        JSONObject hourly = forecast.getJSONObject("hourly");
        JSONArray data = hourly.getJSONArray("data");

        Hour[] hours = new Hour[data.length()];

        for (int i = 0; i < data.length(); i++) {
            JSONObject jsonHour = data.getJSONObject(i);
            Hour hour = new Hour();

            hour.setSummary(jsonHour.getString("summary"));
            hour.setTemperature(jsonHour.getDouble("temperature"));
            hour.setTime(jsonHour.getLong("time"));
            hour.setTimezone(timezone);

            hours[i] = hour;
        }

       return hours;
    }

    private Current getCurrentDetails(String jsonData) throws JSONException{
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        Log.i(TAG, "From JSON: " + timezone);

        JSONObject currently = forecast.getJSONObject("currently");

        Current current = new Current();
        current.setHumidity(currently.getDouble("humidity"));
        current.setTime(currently.getLong("time"));
        current.setIcon(currently.getString("icon"));
        current.setPrecipChance(currently.getDouble("precipProbability"));
        current.setSummary(currently.getString("summary"));
        current.setTemperature(currently.getDouble("temperature"));
        current.setTimezone(timezone);

        Log.d(TAG, current.getFormattedTime());


        return current;
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        boolean isAvailable = false;
        if (networkInfo != null && networkInfo.isConnected()) {
            isAvailable = true;
        }
        return isAvailable;
    }

    private void alertUserAboutError() {
        AlertDialogFragment dialog = new AlertDialogFragment();
        dialog.show(getFragmentManager(), "error_dialog");
    }


}