2014-07-20 186 views
0

我從提供的gist Udacity中複製了getWeatherDataFromJson(forecastJsonStr,numDays)。但是,它會提供空指針異常。我無法弄清楚。你能檢查代碼並告訴我我做錯了什麼嗎?Udacity Android JSON解析

ForecastFragment.java

public class ForecastFragment extends Fragment { 


    public ForecastFragment() { 
     // Required empty public constructor 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setHasOptionsMenu(true); 
    } 

    @Override 
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
     inflater.inflate(R.menu.forecast_fragment, menu); 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml 
     int id = item.getItemId(); 
     if (id == R.id.action_refresh) { 
      new FetchWeatherTask().execute("94043"); 
      return true; 
     } 
     return super.onOptionsItemSelected(item); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     View rootView = inflater.inflate(R.layout.fragment_forecast, container, false); 

      /*//My way 
      ArrayList<String> fakeData = new ArrayList<String>(); 
      fakeData.add("Today-Sunny 50/60"); 
      fakeData.add("Tomorrow-Cloudy 20/30"); 
      fakeData.add("Wednesday-Snowy 40/50"); 
      fakeData.add("Thursday-Rainy 20/40"); */ 

     //Google's way 
     String[] forecastArray={ 
       "Today-Sunny 50/60", 
       "Tomorrow-Cloudy 20/30", 
       "Wednesday-Snowy 40/50", 
       "Thursday-Rainy 20/40", 
       "Friday-Funny 20/50", 
       "Sat-Sunny 70/80", 
       "Sun-Sunny 90/100" 
     }; 

     ArrayList<String> weekForecast=new ArrayList<String>(Arrays.asList(forecastArray)); 


     ArrayAdapter adapter = new ArrayAdapter<String> 
       (getActivity(), R.layout.list_item_forecast, R.id.list_item_forecast_textview, weekForecast); 

     ListView listview = (ListView) rootView.findViewById(R.id.listview_forecast); 

     listview.setAdapter(adapter); 

     return rootView; 
    } 

    public class FetchWeatherTask extends AsyncTask<String, Void, String[]>{ 

     private final String LOG_TAG = FetchWeatherTask.class.getSimpleName(); 

     /* The date/time conversion code is going to be moved outside the asynctask later, 
* so for convenience we're breaking it out into its own method now. 
*/ 
     private String getReadableDateString(long time){ 
      // Because the API returns a unix timestamp (measured in seconds), 
      // it must be converted to milliseconds in order to be converted to valid date. 
      Date date = new Date(time * 1000); 
      SimpleDateFormat format = new SimpleDateFormat("E, MMM d"); 
      return format.format(date).toString(); 
     } 

     /** 
     * Prepare the weather high/lows for presentation. 
     */ 
     private String formatHighLows(double high, double low) { 
      // For presentation, assume the user doesn't care about tenths of a degree. 
      long roundedHigh = Math.round(high); 
      long roundedLow = Math.round(low); 

      String highLowStr = roundedHigh + "/" + roundedLow; 
      return highLowStr; 
     } 

     /** 
     * Take the String representing the complete forecast in JSON Format and 
     * pull out the data we need to construct the Strings needed for the wireframes. 
     * 
     * Fortunately parsing is easy: constructor takes the JSON string and converts it 
     * into an Object hierarchy for us. 
     */ 
     private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays) 
       throws JSONException { 

      // These are the names of the JSON objects that need to be extracted. 
      final String OWM_LIST = "list"; 
      final String OWM_WEATHER = "weather"; 
      final String OWM_TEMPERATURE = "temp"; 
      final String OWM_MAX = "max"; 
      final String OWM_MIN = "min"; 
      final String OWM_DATETIME = "dt"; 
      final String OWM_DESCRIPTION = "main"; 

      JSONObject forecastJson = new JSONObject(forecastJsonStr); 
      JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST); 

      String[] resultStrs = new String[numDays]; 
      for(int i = 0; i < weatherArray.length(); i++) { 
       // For now, using the format "Day, description, hi/low" 
       String day; 
       String description; 
       String highAndLow; 

       // Get the JSON object representing the day 
       JSONObject dayForecast = weatherArray.getJSONObject(i); 

       // The date/time is returned as a long. We need to convert that 
       // into something human-readable, since most people won't read "1400356800" as 
       // "this saturday". 
       long dateTime = dayForecast.getLong(OWM_DATETIME); 
       day = getReadableDateString(dateTime); 

       // description is in a child array called "weather", which is 1 element long. 
       JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0); 
       description = weatherObject.getString(OWM_DESCRIPTION); 

       // Temperatures are in a child object called "temp". Try not to name variables 
       // "temp" when working with temperature. It confuses everybody. 
       JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE); 
       double high = temperatureObject.getDouble(OWM_MAX); 
       double low = temperatureObject.getDouble(OWM_MIN); 

       highAndLow = formatHighLows(high, low); 
       resultStrs[i] = day + " - " + description + " - " + highAndLow; 
      } 

      /*for(String s: resultStrs) 
      { 
       Log.v(LOG_TAG, "Forecast entry"+s); 
      }*/ 

      return resultStrs; 
     } 

     @Override 
     protected String[] doInBackground(String... params) { 

      // These two need to be declared outside the try/catch 
      // so that they can be closed in the finally block. 
      HttpURLConnection urlConnection = null; 
      BufferedReader reader = null; 

      // Will contain the raw JSON response as a string. 
      String forecastJsonStr = null; 

      String format="json"; 
      String units="metric"; 
      int numDays=7; 

      try { 
       // Construct the URL for the OpenWeatherMap query 
       // Possible parameters are avaiable at OWM's forecast API page, at 
       // http://openweathermap.org/API#forecast 

       //My way 
       /*Uri.Builder builder = new Uri.Builder(); 
       builder.scheme("http") 
         .authority("api.openweathermap.org") 
         .appendPath("data") 
         .appendPath("2.5") 
         .appendPath("forecast") 
         .appendPath("daily") 

         .appendQueryParameter("q", params[0]) 
         .appendQueryParameter("mode", "json") 
         .appendQueryParameter("units", "metric") 
         .appendQueryParameter("cnt", "7"); 
       */ 

       //Google's way 
       final String FORECAST_BASE_URL="http://api.openweathermap.org/data/2.5/forecast/daily?"; 
       final String QUERY_PARAM="q"; 
       final String FORMAT_PARAM="mode"; 
       final String UNITS_PARAM="metric"; 
       final String DAYS_PARAM="cnt"; 

       Uri.Builder builder = new Uri.Builder(); 
       builder.scheme("http") 
         .authority(FORECAST_BASE_URL) 
         .appendQueryParameter(QUERY_PARAM,params[0]) 
         .appendQueryParameter(FORMAT_PARAM, format) 
         .appendQueryParameter(UNITS_PARAM, units) 
         .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)) 
         .build(); 


       String myUrl = builder.toString(); 

       URL url = new URL(myUrl); 

       // Create the request to OpenWeatherMap, and open the connection 
       urlConnection = (HttpURLConnection) url.openConnection(); 
       urlConnection.setRequestMethod("GET"); 
       urlConnection.connect(); 

       // Read the input stream into a String 
       InputStream inputStream = urlConnection.getInputStream(); 
       StringBuffer buffer = new StringBuffer(); 
       if (inputStream == null) { 
        // Nothing to do. 
        return null; 
       } 
       reader = new BufferedReader(new InputStreamReader(inputStream)); 

       String line; 
       while ((line = reader.readLine()) != null) { 
        // Since it's JSON, adding a newline isn't necessary (it won't affect parsing) 
        // But it does make debugging a *lot* easier if you print out the completed 
        // buffer for debugging. 
        buffer.append(line + "\n"); 
       } 

       if (buffer.length() == 0) { 
        // Stream was empty. No point in parsing. 
        return null; 
       } 

       forecastJsonStr = buffer.toString(); 

       Log.v(LOG_TAG,"Forecast Json String"+forecastJsonStr); 

      } catch (IOException e) { 
       Log.e(LOG_TAG, "Error ", e); 
       // If the code didn't successfully get the weather data, there's no point in attemping 
       // to parse it. 
       forecastJsonStr = null; 
      } finally{ 
       if (urlConnection != null) { 
        urlConnection.disconnect(); 
       } 
       if (reader != null) { 
        try { 
         reader.close(); 
        } catch (final IOException e) { 
         Log.e(LOG_TAG, "Error closing stream", e); 
        } 
       } 
      } 

      try { 
       return getWeatherDataFromJson(forecastJsonStr,numDays); 
      } catch (JSONException e) { 
       Log.e(LOG_TAG, e.getMessage(), e); 
       e.printStackTrace(); 
      } 
      return null; 
     } 
    } 

} 

logcat的

07-20 23:01:19.030: E/AndroidRuntime(7325): FATAL EXCEPTION: AsyncTask #1 
07-20 23:01:19.030: E/AndroidRuntime(7325): java.lang.RuntimeException: An error occured while executing doInBackground() 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at android.os.AsyncTask$3.done(AsyncTask.java:299) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.lang.Thread.run(Thread.java:856) 
07-20 23:01:19.030: E/AndroidRuntime(7325): Caused by: java.lang.NullPointerException 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at org.json.JSONTokener.nextValue(JSONTokener.java:94) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at org.json.JSONObject.<init>(JSONObject.java:154) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at org.json.JSONObject.<init>(JSONObject.java:171) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at app.sunshine.android.example.com.sunshine.ForecastFragment$FetchWeatherTask.getWeatherDataFromJson(ForecastFragment.java:149) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at app.sunshine.android.example.com.sunshine.ForecastFragment$FetchWeatherTask.doInBackground(ForecastFragment.java:296) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at app.sunshine.android.example.com.sunshine.ForecastFragment$FetchWeatherTask.doInBackground(ForecastFragment.java:103) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at android.os.AsyncTask$2.call(AsyncTask.java:287) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
07-20 23:01:19.030: E/AndroidRuntime(7325):  ... 5 more 
07-20 23:01:19.080: E/android.os.Debug(2128): [email protected] > dumpstate -k -t -z -d -o /data/log/dumpstate_app_error 
+0

發表您的JSON也並指示代碼 – Raghunandan

+0

線149 =>的JSONObject forecastJson =新的JSONObject(forecastJsonStr)線149 –

+0

你'forecastJsonStr'爲空。堆棧跟蹤指示相同。所以檢查響應。看看你是否得到了一個有效的json,然後登錄來交叉檢查相同的 – Raghunandan

回答

0

是開放的建設者,我試圖尋找Uri.Builder文檔做,但後來改變我的方式與本教程的方式問題和沒有正確更新所有內容,這就是爲什麼我搞砸了。

Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon() 
         .appendQueryParameter(QUERY_PARAM,params[0]) 
         .appendQueryParameter(FORMAT_PARAM, format) 
         .appendQueryParameter(UNITS_PARAM, units) 
         .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)) 
         .build(); 
+0

它應該被更新,新的版本必須在字符串/ URL的末尾添加API密鑰... APPID = 716497154971549175941971「 – wuno