I am new to android and I was trying to learn android using the UDACITY course on building the sunshine app.
Thanks to a couple of stackoverflow posts I was able fix my app from crashes and then get data from the internet using the API key.
Everything works fine except that the data is duplicated and there are 2 actionbars and hereby 2 refresh buttons.
This was just after I completed lesson 2.
My code for forecastfragment.java class and MainActivity.java class are as follows-:
package com.example.hemant.sunshine.app;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* A placeholder fragment containing a simple view.
*/
public class ForecastFragment extends Fragment {
ArrayAdapter<String> mForecastAdapter;
public ForecastFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.forecastfragment,menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
int id=item.getItemId();
if(id==R.id.action_refresh){
FetchWeatherTask weatherTask = new FetchWeatherTask();
weatherTask.execute("47408");
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
new FetchWeatherTask().execute("47408");
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String[] forecastArray={
"Mon 6/23 - Sunny - 31/17",
"Tue 6/24 - Foggy - 21/8",
"Wed 6/25 - Cloudy - 22/17",
"Thurs 6/26 - Rainy - 18/11",
"Fri 6/27 - Foggy - 21/10",
"Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
"Sun 6/29 - Sunny - 20/7"
};
List<String> weekForecast = new ArrayList<String>
(Arrays.asList(forecastArray));
// Now that we have some dummy forecast data, create an ArrayAdapter.
// The ArrayAdapter will take data from a source (like our dummy
forecast) and
// use it to populate the ListView it's attached to.
// Now that we have some dummy forecast data, create an ArrayAdapter.
// The ArrayAdapter will take data from a source (like our dummy
forecast) and
// use it to populate the ListView it's attached to.
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(), // The current context (this activity)
R.layout.list_item_forecast, // The name of the layout
ID.
R.id.list_item_forecast_textview, // The ID of the
textview to populate.
weekForecast);
View rootView= inflater.inflate(R.layout.fragment_main, container,
false);
ListView listView = (ListView) rootView.findViewById(
R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
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);
impleDateFormat format = new SimpleDateFormat("EEE MMM dd");
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);
// OWM returns daily forecasts based upon the local time of the
//city that is being
// asked for, which means that we need to know the GMT offset to
//translate this data
// properly.
// Since this data is also sent in-order and the first day is
//always the
// current day, we're going to take advantage of that to get a nice
// normalized UTC date for all of our weather.
/* Time dayTime = new Time();
dayTime.setToNow();
// we start at the day returned by local time. Otherwise this is a mess.
int julianStartDay = Time.getJulianDay(System.currentTimeMillis(),
dayTime.gmtoff);
// now we work exclusively in UTC
dayTime = new Time();*/
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) {
//If there's no zzip code, there's nothing to look up. Verify size of
//params.
if (params.length==0){
return null;
}
// 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
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 = "units";
final String DAYS_PARAM = "cnt";
final String APPID_PARAM = "appid";
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))
.appendQueryParameter(APPID_PARAM, BuildConfig.OPEN_WEATHER_MAP_API_KEY)
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
/* String baseUrl =
"http://api.openweathermap.org/data/2.5/forecast/daily?
q=94043&mode=json&units=metric&cnt=7";
String apiKey = "&APPID=" + BuildConfig.OPEN_WEATHER_MAP_API_KEY;
URL url = new URL(baseUrl.concat(apiKey));*/
// 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.
return 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;
}
#Override
protected void onPostExecute(String[] result) {
if (result != null) {
mForecastAdapter.clear();
for(String dayForecastStr : result) {
mForecastAdapter.add(dayForecastStr);
}
// Data has been returned from the server
mForecastAdapter.notifyDataSetChanged();
}
}
}
}
MainActivity.java is as follows-:
package com.example.hemant.sunshine.app;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new ForecastFragment())
.commit();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
You are using same fragment via static and dynamic way. That is FrameLayout and fragment in activity_main.xml. You are initiating first fargment in MainActivity via FragmentTransaction as below.
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new ForecastFragment())
.commit();
}
And another fragment is created statically in the content_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment"
android:name="com.sunshine.ForecastFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:layout="#layout/fragment_main" />
As a result you are initiating 2 fragment and see duplicated data.
To remove one of them will work for you. For example remove FrameLayout in the activity_main.xml:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#android:drawable/ic_dialog_email" />
and remove the following part in the MainAcitivty.java:
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new ForecastFragment())
.commit();
}
Why you have 2 refresh button. Because you are using Toolbar item in acitivity_main.xml and AppTheme with DarkActionBar. To use toolbar you must use a theme with No Actionbar.
In the AndroidManifest.xml replace this line.
"android:theme="#style/AppTheme">"
with
android:theme="#style/AppTheme.NoActionBar">
Then go to MainActivity and init your Toolbar as follows
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowTitleEnabled(true);
}
Related
Here's my app. It's supposed to get a JSON String based on a HTTP query to Google Books and show the list of all the found books. But the app always finds nothing.
I use an Android Emulator in Android Studio.
MainActivity.java
package com.example.android.booklisting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.app.LoaderManager;
import android.content.Loader;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>> {
/** Adapter for the list of earthquakes */
private BookAdapter mAdapter;
TextView mEmptyStateTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button searchButton = findViewById(R.id.search_button);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText searchText = findViewById(R.id.search_text);
QueryUtils.setSearchQuery(QueryUtils.getINIITIAL_GOOGLE_BOOKS_URL()
+ searchText.getText());
mAdapter = new BookAdapter(MainActivity.this,
new ArrayList<Book>());
ListView listView = findViewById(R.id.list);
listView.setAdapter(mAdapter);
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
listView.setEmptyView(mEmptyStateTextView);
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID 1 and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(1, null, MainActivity.this);
}
else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText("No Internet connection.");
}
}
});
}
#NonNull
#Override
public Loader<List<Book>> onCreateLoader(int id, #Nullable Bundle args) {
// Create a new loader for the given URL
return new BookLoader(this, QueryUtils.getSearchQuery());
}
#Override
public void onLoadFinished(#NonNull Loader<List<Book>> loader, List<Book> books) {
// Hide loading indicator because the data has been loaded
ProgressBar loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Clear the adapter of previous book data
mAdapter.clear();
// If there is a valid list of {#link Book}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (books != null && !books.isEmpty()) {
mAdapter.addAll(books);
}
// Set empty state text to display "No books found."
mEmptyStateTextView.setText("No books found.");
}
#Override
public void onLoaderReset(#NonNull Loader<List<Book>> loader) {
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/search_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="#+id/search_text"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
<Button
android:id="#+id/search_button"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Search"
android:textAllCaps="true" />
</LinearLayout>
<ListView
android:id="#+id/list"
android:layout_below="#id/search_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium"/>
<ProgressBar
android:id="#+id/loading_indicator"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
Book.java
package com.example.android.booklisting;
import android.graphics.Bitmap;
public class Book {
private String mTitle;
private String mAuthor;
private String mReleaseDate;
private Bitmap mBookCover;
public Book(String title, String author, String releaseDate, Bitmap bookCover) {
mTitle = title;
mAuthor = author;
mBookCover = bookCover;
mReleaseDate = releaseDate;
}
public String getTitle() {
return mTitle;
}
public String getAuthor() {
return mAuthor;
}
public Bitmap getBookCover() {
return mBookCover;
}
public String getReleaseYear() {
return mReleaseDate;
}
}
BookAdapter.java
package com.example.android.booklisting;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class BookAdapter extends ArrayAdapter<Book> {
public BookAdapter(Activity context, ArrayList<Book> books) {
super(context, 0, books);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View listItemView = convertView;
if (listItemView == null)
{
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.book_item, parent, false);
}
Book currentBook = getItem(position);
TextView bookTitleView = listItemView.findViewById(R.id.book_title);
bookTitleView.setText(currentBook.getTitle());
TextView bookAuthorView = listItemView.findViewById(R.id.book_author);
bookAuthorView.setText(currentBook.getAuthor());
TextView bookReleaseYearView = listItemView.findViewById(R.id.book_release_year);
bookReleaseYearView.setText(currentBook.getReleaseYear());
ImageView bookCoverView = listItemView.findViewById(R.id.book_cover);
bookCoverView.setImageBitmap(currentBook.getBookCover());
return listItemView;
}
}
BookLoader.java
package com.example.android.booklisting;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import androidx.annotation.Nullable;
import android.content.AsyncTaskLoader;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
public class BookLoader extends AsyncTaskLoader<List<Book>> {
/** Query URL */
private String mUrl;
/**
* Constructs a new {#link BookLoader}.
*
* #param context of the activity
* #param url to load data from
*/
public BookLoader(Context context, String url) {
super(context);
mUrl = url;
}
#Override
protected void onStartLoading() {
forceLoad();
}
/**
* This is on a background thread.
*/
#Nullable
#Override
public List<Book> loadInBackground() {
if (mUrl == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of earthquakes.
List<Book> books = QueryUtils.fetchEarthquakeData(mUrl);
return books;
}
}
QueryUtils.java
package com.example.android.booklisting;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.lang.StringBuilder;
public class QueryUtils {
private static final String INIITIAL_GOOGLE_BOOKS_URL = "https://www.googleapis.com/books/v1/volumes?q=";
private static String searchQuery;
private QueryUtils() {
}
public static String getINIITIAL_GOOGLE_BOOKS_URL() {
return INIITIAL_GOOGLE_BOOKS_URL;
}
public static void setSearchQuery(String searchQuery) {
QueryUtils.searchQuery = searchQuery;
}
public static String getSearchQuery() {
return searchQuery;
}
/**
* Query the Google Books dataset and return an {#link ArrayList} object to represent a list of books.
*/
/**
* Return a list of {#link Book} objects that has been built up from
* parsing the given JSON response.
*/
private static List<Book> extractBooksFromJson(String bookJSON) {
// If the JSON string is empty or null, then return early.
if (TextUtils.isEmpty(bookJSON)) {
Log.e("nojson", "No JSON returned");
return null;
}
// Create an empty ArrayList that we can start adding books to
List<Book> books = new ArrayList<>();
// Try to parse the JSON response string. If there's a problem with the way the JSON
// is formatted, a JSONException exception object will be thrown.
// Catch the exception so the app doesn't crash, and print the error message to the logs.
try {
// Create a JSONObject from the JSON response string
JSONObject baseJsonResponse = new JSONObject(bookJSON);
Log.v("success", "JSONObject sucessfully created!");
// Extract the JSONArray associated with the key called "items",
// which represents a list of items (or books).
JSONArray bookArray = baseJsonResponse.getJSONArray("items");
// For each book in the bookArray, create a {#link Book} object
for (int i = 0; i < bookArray.length(); i++) {
// Get a single Book at position i within the list of books
JSONObject currentBook = bookArray.getJSONObject(i);
// For a given book, extract the JSONObject associated with the
// key called "volumeInfo", which represents a list of all information
// for that book.
JSONObject volumeInfo = currentBook.getJSONObject("volumeInfo");
// Extract the value for the key called "title"
String title = volumeInfo.getString("title");
//Extract the JSONArray associated with the key called "authors"
JSONArray jsonAuthors = volumeInfo.getJSONArray("authors");
ArrayList<String> authors = new ArrayList<>();
StringBuilder authorsStringBuilder = new StringBuilder();
for (int j = 0; j < jsonAuthors.length(); j++)
{
// Add a specific author to the "authors" list
// and add it to a the "authorsStringBuilder"
authors.add(jsonAuthors.getJSONObject(j).toString());
authorsStringBuilder.append(authors.get(j) + ", \n");
}
//Remove the last 3 elements in the "authorsStringBuilder"
authorsStringBuilder.delete(authorsStringBuilder.length() - 3,
authorsStringBuilder.length() - 1);
//Convert StringBuilder to String
String authorsString = authorsStringBuilder.toString();
// Extract the value for the key called "publishedDate"
String releaseDate = volumeInfo.getString("publishedDate");
//The link for a book cover
String imageLink;
// Get the JSONObject "readingModes" having information about
// whether or not a given book has a cover
JSONObject readingModes = volumeInfo.getJSONObject("readingModes");
// Extract the value for the key called "image"
boolean hasACover = readingModes.getBoolean("image");
//Check if a given book has a cover
if (hasACover)
{
// If yes, then assign a link for the book
// to the imageLink
imageLink = volumeInfo.getJSONObject("imageLinks")
.getString("thumbnail");
}
else
{
// If not, then assign the imageLink the default cover
imageLink = "https://books.google.ru/googlebooks/images/no_cover_thumb.gif";
}
//Get a book cover
Bitmap bookCover = getImage(imageLink);
// Create a new {#link Book} object with the magnitude, location, time,
// and url from the JSON response.
Book book = new Book(title, authorsString, releaseDate, bookCover);
// Add the new {#link Book} to the list of books.
books.add(book);
}
} catch (JSONException e) {
// If an error is thrown when executing any of the above statements in the "try" block,
// catch the exception here, so the app doesn't crash. Print a log message
// with the message from the exception.
Log.e("QueryUtils", "Problem parsing the book JSON results", e);
}
// Return the list of books
return books;
}
/**
* Get a book cover.
*/
/**
* Return a book cover
*/
private static Bitmap getImage(String imageLink)
{
Bitmap image = null;
try {
URL url = new URL(imageLink);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
image = BitmapFactory.decodeStream(input);
} catch (IOException e) {
// Log exception
}
return image;
}
/**
* Query the Google Books dataset and return a list of {#link Book} objects.
*/
public static List<Book> fetchEarthquakeData(String requestUrl) {
// Create URL object
URL url = createUrl(requestUrl);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
Log.e("", "Problem making the HTTP request.", e);
}
// Extract relevant fields from the JSON response and create a list of {#link Book}s
List<Book> books = extractBooksFromJson(jsonResponse);
Log.v("QueryUtils", "fetching data");
// Return the list of {#link book}s
return books;
}
/**
* Returns new URL object from the given string URL.
*/
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
Log.e("QueryUtils: : ", "Error with creating URL", e);
}
return url;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e("", "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e("", "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {#link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
}
An example query:
Logcat:
Here's my app. It gets a JSON String based on an HTTP query to Google Books and shows the list of all the found books. However, for some reason it doesn't show book covers for the books that have covers. For the books not having them it works fine.
I use an Android Emulator in Android Studio.
MainActivity.java
package com.example.android.booklisting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.app.LoaderManager;
import android.content.Loader;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>> {
/** Adapter for the list of earthquakes */
private BookAdapter mAdapter;
TextView mEmptyStateTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button searchButton = findViewById(R.id.search_button);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText searchText = findViewById(R.id.search_text);
QueryUtils.setSearchQuery(QueryUtils.getINIITIAL_GOOGLE_BOOKS_URL()
+ searchText.getText());
mAdapter = new BookAdapter(MainActivity.this,
new ArrayList<Book>());
ListView listView = findViewById(R.id.list);
listView.setAdapter(mAdapter);
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
listView.setEmptyView(mEmptyStateTextView);
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID 1 and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(1, null, MainActivity.this);
}
else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText("No Internet connection.");
}
}
});
}
#NonNull
#Override
public Loader<List<Book>> onCreateLoader(int id, #Nullable Bundle args) {
// Create a new loader for the given URL
return new BookLoader(this, QueryUtils.getSearchQuery());
}
#Override
public void onLoadFinished(#NonNull Loader<List<Book>> loader, List<Book> books) {
// Hide loading indicator because the data has been loaded
ProgressBar loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Clear the adapter of previous book data
mAdapter.clear();
// If there is a valid list of {#link Book}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (books != null && !books.isEmpty()) {
mAdapter.addAll(books);
}
// Set empty state text to display "No books found."
mEmptyStateTextView.setText("No books found.");
}
#Override
public void onLoaderReset(#NonNull Loader<List<Book>> loader) {
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/search_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="#+id/search_text"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
<Button
android:id="#+id/search_button"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Search"
android:textAllCaps="true" />
</LinearLayout>
<ListView
android:id="#+id/list"
android:layout_below="#id/search_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium"/>
<ProgressBar
android:id="#+id/loading_indicator"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
Book.java
package com.example.android.booklisting;
import android.graphics.Bitmap;
public class Book {
private String mTitle;
private String mAuthor;
private String mReleaseDate;
private Bitmap mBookCover;
public Book(String title, String author, String releaseDate, Bitmap bookCover) {
mTitle = title;
mAuthor = author;
mBookCover = bookCover;
mReleaseDate = releaseDate;
}
public String getTitle() {
return mTitle;
}
public String getAuthor() {
return mAuthor;
}
public Bitmap getBookCover() {
return mBookCover;
}
public String getReleaseYear() {
return mReleaseDate;
}
}
BookAdapter.java
package com.example.android.booklisting;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class BookAdapter extends ArrayAdapter<Book> {
public BookAdapter(Activity context, ArrayList<Book> books) {
super(context, 0, books);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View listItemView = convertView;
if (listItemView == null)
{
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.book_item, parent, false);
}
Book currentBook = getItem(position);
TextView bookTitleView = listItemView.findViewById(R.id.book_title);
bookTitleView.setText(currentBook.getTitle());
TextView bookAuthorView = listItemView.findViewById(R.id.book_author);
bookAuthorView.setText(currentBook.getAuthor());
TextView bookReleaseYearView = listItemView.findViewById(R.id.book_release_year);
bookReleaseYearView.setText(currentBook.getReleaseYear());
ImageView bookCoverView = listItemView.findViewById(R.id.book_cover);
bookCoverView.setImageBitmap(currentBook.getBookCover());
return listItemView;
}
}
BookLoader.java
package com.example.android.booklisting;
import android.content.Context;
import androidx.annotation.Nullable;
import android.content.AsyncTaskLoader;
import java.util.List;
public class BookLoader extends AsyncTaskLoader<List<Book>> {
/** Query URL */
private String mUrl;
/**
* Constructs a new {#link BookLoader}.
*
* #param context of the activity
* #param url to load data from
*/
public BookLoader(Context context, String url) {
super(context);
mUrl = url;
}
#Override
protected void onStartLoading() {
forceLoad();
}
/**
* This is on a background thread.
*/
#Nullable
#Override
public List<Book> loadInBackground() {
if (mUrl == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of earthquakes.
List<Book> books = QueryUtils.fetchEarthquakeData(mUrl);
return books;
}
}
QueryUtils.java
package com.example.android.booklisting;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.lang.StringBuilder;
public class QueryUtils {
private static final String INIITIAL_GOOGLE_BOOKS_URL = "https://www.googleapis.com/books/v1/volumes?q=";
private static String searchQuery;
private QueryUtils() {
}
public static String getINIITIAL_GOOGLE_BOOKS_URL() {
return INIITIAL_GOOGLE_BOOKS_URL;
}
public static void setSearchQuery(String searchQuery) {
QueryUtils.searchQuery = searchQuery;
}
public static String getSearchQuery() {
return searchQuery;
}
/**
* Query the Google Books dataset and return an {#link ArrayList} object to represent a list of books.
*/
/**
* Return a list of {#link Book} objects that has been built up from
* parsing the given JSON response.
*/
private static List<Book> extractBooksFromJson(String bookJSON) {
// If the JSON string is empty or null, then return early.
if (TextUtils.isEmpty(bookJSON)) {
Log.e("nojson", "No JSON returned");
return null;
}
// Create an empty ArrayList that we can start adding books to
List<Book> books = new ArrayList<>();
// Try to parse the JSON response string. If there's a problem with the way the JSON
// is formatted, a JSONException exception object will be thrown.
// Catch the exception so the app doesn't crash, and print the error message to the logs.
try {
// Create a JSONObject from the JSON response string
JSONObject baseJsonResponse = new JSONObject(bookJSON);
Log.v("success", "JSONObject sucessfully created!");
// Extract the JSONArray associated with the key called "items",
// which represents a list of items (or books).
JSONArray bookArray = baseJsonResponse.getJSONArray("items");
// For each book in the bookArray, create a {#link Book} object
for (int i = 0; i < bookArray.length(); i++) {
// Get a single Book at position i within the list of books
JSONObject currentBook = bookArray.getJSONObject(i);
// For a given book, extract the JSONObject associated with the
// key called "volumeInfo", which represents a list of all information
// for that book.
JSONObject volumeInfo = currentBook.getJSONObject("volumeInfo");
// Extract the value for the key called "title"
String title = volumeInfo.getString("title");
String authorsString = "";
//Extract the JSONArray associated with the key called "authors"
try {
JSONArray jsonAuthors = volumeInfo.getJSONArray("authors");
ArrayList<String> authors = new ArrayList<>();
StringBuilder authorsStringBuilder = new StringBuilder();
for (int j = 0; j < jsonAuthors.length(); j++) {
// Add a specific author to the "authors" list
// and add it to a the "authorsStringBuilder"
authors.add(jsonAuthors.getString(j));
authorsStringBuilder.append(authors.get(j) + ", \n");
}
//Remove the last 3 elements in the "authorsStringBuilder"
authorsStringBuilder.delete(authorsStringBuilder.length() - 3,
authorsStringBuilder.length() - 1);
//Convert StringBuilder to String
authorsString = authorsStringBuilder.toString();
}
catch (JSONException e)
{
// If an error is thrown when executing any of the above statements in the "try" block,
// catch the exception here, so the app doesn't crash. Print a log message
// with the message from the exception.
Log.e("QueryUtils", "No authors found", e);
}
// Extract the value for the key called "publishedDate"
String releaseDate = volumeInfo.getString("publishedDate");
//The link for a book cover
String imageLink;
// Get the JSONObject "readingModes" having information about
// whether or not a given book has a cover
JSONObject readingModes = volumeInfo.getJSONObject("readingModes");
// Extract the value for the key called "image"
boolean hasACover = readingModes.getBoolean("image");
//Check if a given book has a cover
if (hasACover)
{
// If yes, then assign a link for the book
// to the imageLink
imageLink = volumeInfo.getJSONObject("imageLinks")
.getString("thumbnail");
Log.v("Cover check", "Book has a cover");
}
else
{
// If not, then assign the imageLink the default cover
imageLink = "https://books.google.ru/googlebooks/images/no_cover_thumb.gif";
}
//Get a book cover
Bitmap bookCover = getImage(imageLink);
// Create a new {#link Book} object with the magnitude, location, time,
// and url from the JSON response.
Book book = new Book(title, authorsString, releaseDate, bookCover);
// Add the new {#link Book} to the list of books.
books.add(book);
}
} catch (JSONException e) {
// If an error is thrown when executing any of the above statements in the "try" block,
// catch the exception here, so the app doesn't crash. Print a log message
// with the message from the exception.
Log.e("QueryUtils", "Problem parsing the book JSON results", e);
}
// Return the list of books
return books;
}
/**
* Get a book cover.
*/
/**
* Return a book cover
*/
private static Bitmap getImage(String imageLink)
{
Bitmap image = null;
try {
URL url = new URL(imageLink);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
image = BitmapFactory.decodeStream(input);
} catch (IOException e) {
// Log exception
Log.e("getImage: ", "Image problems");
}
return image;
}
/**
* Query the Google Books dataset and return a list of {#link Book} objects.
*/
public static List<Book> fetchEarthquakeData(String requestUrl) {
// Create URL object
URL url = createUrl(requestUrl);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
Log.e("", "Problem making the HTTP request.", e);
}
// Extract relevant fields from the JSON response and create a list of {#link Book}s
List<Book> books = extractBooksFromJson(jsonResponse);
Log.v("QueryUtils", "fetching data");
// Return the list of {#link book}s
return books;
}
/**
* Returns new URL object from the given string URL.
*/
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
Log.e("QueryUtils: : ", "Error with creating URL", e);
}
return url;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e("", "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e("", "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {#link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
}
Here's the example of what I'm talking about:
And a logcat:
ADDED: I've added a more specific exception logging. Here's the new logcat:
I am running an app on Android Studio and while adding string values to arrayadapter object, I get null pointer exception. My exact relevant code is:
protected void onPostExecute(String[] result) {
List<String> wordList = new ArrayList<>();
wordList = Arrays.asList(result);
if(wordList!= null)
{
for(String forecast: wordList)
{
Log.v(LOG_TAG,"forecast value: "+forecast);
mForecastAdapter.add(forecast);
}
// mForecastAdapter.addAll(wordList);
}
Log.v(LOG_TAG,"Sorry!! Result is null");
}
I tried with addAll() method as well, but for that I need to upgrade to API 11. So that is not an option. For Information: This result variable is not empty and in logs, I do get correct value of forecast every time I execute this set of code.
The exception that I get is:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.add(java.lang.Object)' on a null object reference
Here's my code:
package com.example.khatri.sunshine.app;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ForecastFragment extends Fragment {
String[] resultfinal = new String[100];
public ForecastFragment() {
}
ArrayAdapter<String> mForecastAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
FetchWeatherTask fetchWeatherTask = new FetchWeatherTask();
fetchWeatherTask.execute("94043");
}
return super.onOptionsItemSelected(item);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
List<String> weekForecast = new ArrayList<String>();
weekForecast.add("Today-Sunny-88/63");
weekForecast.add("Tomorrow FOggy 77/46");
weekForecast.add("Day after sunny again 45/34");
weekForecast.add("Thur-foggy again 65/34");
weekForecast.add("Fri afternoon rainy 76/32");
weekForecast.add("Sat happy sat 65/12");
weekForecast.add("Day after sunny again 45/34");
weekForecast.add("Thur-foggy again 65/34");
weekForecast.add("Fri afternoon rainy 76/32");
weekForecast.add("Sat happy sat 65/12");
ArrayAdapter<String> mForecastAdapter = new ArrayAdapter<String>(
getContext(),
R.layout.list_item_forecast,
R.id.list_item_forecast_textview,
weekForecast);
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
return rootView;
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
protected String[] doInBackground(String... params) {
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;
String appid = "c70ffaa8bf3c10556dc3e82d72d23677";
try {
final String FORECAST_BASE_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?";
final String FORMAT_PARAM = "mode";
final String QUERY_PARAM = "q";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
final String APPID_PARAM = "appid";
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))
.appendQueryParameter(APPID_PARAM, appid)
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
// 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.
forecastJsonStr = null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
forecastJsonStr = null;
}
forecastJsonStr = buffer.toString();
Log.v(LOG_TAG, "forecast JSON string: " + forecastJsonStr);
} catch (Exception e) {
Log.e("PlaceholderFragment", "Error ", e);
forecastJsonStr = null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
resultfinal = getWeatherDataFromJson(forecastJsonStr, numDays);
reader.close();
} catch (final IOException e) {
Log.e("PlaceholderFragment", "Error closing stream", e);
}
catch (JSONException jsonexception)
{
Log.e(LOG_TAG,"JSON Exception",jsonexception);
}
}
}
return resultfinal;
}
private String getReadableDateString(long time) {
SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd");
return shortenedDateFormat.format(time);
}
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;
}
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_DESCRIPTION = "main";
JSONObject forecastJson = new JSONObject(forecastJsonStr);
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
Time dayTime = new Time();
dayTime.setToNow();
int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), dayTime.gmtoff);
dayTime = new Time();
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;
// Cheating to convert this to UTC time, which is what we want anyhow
dateTime = dayTime.setJulianDay(julianStartDay+i);
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);
}
//onPostExecute(resultStrs);
return resultStrs;
}
#Override
protected void onPostExecute(String[] result) {
List<String> wordList = new ArrayList<>();
wordList = Arrays.asList(result);
if(wordList!= null)
{
// mForecastAdapter.add(result.toString());
for(String forecast: wordList)
{
Log.v(LOG_TAG,"forecast value: "+forecast);
mForecastAdapter.add(forecast);
}
// mForecastAdapter.addAll(wordList);
}
Log.v(LOG_TAG,"Sorry!! Reuslt is null");
}
}
}
when you take global variable then you just initialize first time and you use in you whole class. I hope this will help you and you got your solution.
public class ForecastFragment extends Fragment {
ArrayAdapter<String> mForecastAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
......
mForecastAdapter = new ArrayAdapter<String>(
getContext(),
R.layout.list_item_forecast,
R.id.list_item_forecast_textview,
weekForecast);
.....
}
}
You initialised the local mForecastAdapter variable in oncreateView() and adding values to global mForecastAdapter which is not initialized. Thats why it is giving you null pointer.
the point is the lifeCycle of fragment.
Can you ensure that the onCreteView() method has been called be before you call the onOptionsItemSelected() menthod ?
I suggest you to add some log code or set some breadpoints to see which method has been called first.
I got the answer for this from another thread:
clear the arrayadapter, mforecastAdapter.clear() then,
add values to this adapter and then,
mForecastAdapter.notifyDataSetChanged();
This depicted the change in my list view :)
Im a beginner in Android Development and i have been following the SUNSHINE App. Everything seemed to be going great until today that I ran the App and found an Error: java.io.FileNotFoundException:http://api.openweathermap.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7
I did my research and it appears The Openweathermap api now requires an API key.
As a beginner I do not know how to implement this to my current app so as I can continue with my course. I'm simply STUCK because of this.and I would appreciate any work through/help to overcome this:(THANK YOU)
Here is my ForecastFragment.java:
package com.example.android.sunshine.app;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* A placeholder fragment containing a simple view.
*/
public class ForecastFragment extends Fragment {
private ArrayAdapter<String> mForecastAdapter;
public ForecastFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
int id = item.getItemId();
if (id == R.id.action_refresh){
FetchWeatherTask weatherTask = new FetchWeatherTask();
weatherTask.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_main, container, false);
String[] data = {
"Today - Sunny - 88/63",
"Tommorow-Foggy-70/40",
"weds - Cloudy - 72/63",
"Thurs - Asteroids - 75/65",
"Fri - Heavy Rains - 65/56",
"Sat - HELP TRAPPED IN WEATHERSATION - 60/51",
"Sun - Sunny - 80/68"
};
List<String> weekForecast = new ArrayList<String>(Arrays.asList(data));
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(), R.layout.list_item_forcast,
R.id.list_item_forecast_textview,
weekForecast);
ListView listView = (ListView) rootView.findViewById(
R.id.listView_forecast);
listView.setAdapter(mForecastAdapter);
return rootView;
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
private String getReadableDateString(long time){
Date date = new Date(time*1000);
SimpleDateFormat format = new SimpleDateFormat("E,MMM d");
return format.format(date).toString();
}
private String formatHighLows(double high,double low){
long roundedHigh = Math.round(high);
long roundedLow = Math.round(low);
return null;
}
private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays)
throws JSONException{
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++){
String day;
String description;
String highAndLow;
JSONObject dayForecast = weatherArray.getJSONObject(i);
long dateTime = dayForecast.getLong(OWM_DATETIME);
day = getReadableDateString(dateTime);
JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
description = weatherObject.getString(OWM_DESCRIPTION);
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){
if (params.length == 0){
return null;
}
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
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
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 = "units";
final String DAYS_PARAM = "cnt";
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();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG,"Built URI" + builtUri.toString());
// 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.
forecastJsonStr = 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.
forecastJsonStr = null;
}
forecastJsonStr = buffer.toString();
} 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;
}
#Override
protected void onPostExecute(String[] result) {
if (result !=null){
mForecastAdapter.clear();
for (String dayForecastStr : result){
mForecastAdapter.add(dayForecastStr);
}
}
}
}
}
To add the API key you just need to append the parameter to the end of the query:
http://api.openweathermap.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7&appid=[your_api_key]
If you click through the examples on their page, you'll see that they've updated their samples to include an API key: http://openweathermap.org/forecast5
First sign up for an API key here
Then add the API key to the end of the query:
http://api.openweathermap.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7&appid=[your_api_key]
I m trying here to display a 7 day weather forecast from OWM API using AsyncTask.
doInBackground(String...param) method is also working fine. I have checked the LOGCAT.
After the async has finished the execution. I tried to refresh the ListView on refresh button in the menu. But it seems the onPostExecute() does care all about.
ForecastFragment.java
package com.example.sunshine;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* A placeholder fragment containing a simple view.
*/
public class ForecastFragment extends Fragment {
private ArrayAdapter<String> mForeCastAdapter;
public String[] forecastArray;
public ForecastFragment() {
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
new FetchWeatherTask().execute("44700");
return true;
}
return super.onOptionsItemSelected(item);
}
protected void onPostExecute(String[] result) {
if (result != null) {
Log.v("msg", result[0]);
mForeCastAdapter.clear();
// forecastArray = result;
for (String dayForecastStr : result) {
mForeCastAdapter.add(dayForecastStr);
// mForeCastAdapter.notifyDataSetChanged();
}
}
mForeCastAdapter.notifyDataSetChanged();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
String[] list = { "1", "2", "3" };
List<String> weekForeCast = new ArrayList<String>(
Arrays.asList(list));
mForeCastAdapter = new ArrayAdapter<String>(getActivity(),
R.layout.list_item_forecast, R.id.list_item_forecast_textview,
weekForeCast);
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
ListView weekList = (ListView) rootView
.findViewById(R.id.list_item_forecast);
weekList.setAdapter(mForeCastAdapter);
return rootView;
}
}
class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
protected ArrayAdapter<String> mForeCastAdapter;
/*
* 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 Array : " + 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
// URL url = new
// URL("http://api.openweathermap.org/data/2.5/forecast/daily?" +
// "q=94043&mode=json&units=metric&cnt=7");
final String FORECAST_BASE_URL= ="http://api.openweathermap.org/
data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String MODE_PARAM = "mode";
final String UNIT_PARAM = "units";
final String DAYS_PARAM = "cnt";
Uri urlBuild = Uri
.parse(FORECAST_BASE_URL)
.buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(MODE_PARAM, format)
.appendQueryParameter(UNIT_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.build();
URL url = new URL(urlBuild.toString());
//Log.v(LOG_TAG, "Build URL " + urlBuild.toString());
// 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.
return 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;
}
}
Try using the method onPostExecute in your AsyncTask class rather than your Fragment class. The AsyncTask class looks like this:
private class MyTask extends AsyncTask<String, Void, String[]> {
#Override
protected String[] doInBackground(String... params) {
}
#Override
protected void onPostExecute(String result[]) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
For your case, you will want to put the onPostExecute method inside your FetchWeatherTask class as such:
class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
....
#Override
protected void onPostExecute(String result[]) {
if (result != null) {
Log.v("msg", result[0]);
mForeCastAdapter.clear();
//forecastArray = result;
for (String dayForecastStr : result) {
mForeCastAdapter.add(dayForecastStr);
//mForeCastAdapter.notifyDataSetChanged();
}
}
mForeCastAdapter.notifyDataSetChanged();
}
....
}
You should be overriding the OnPostExecute() in your FetchWeatherTask the same way you are overriding doInBackground()
The async task doesm't have an onPostExecute() method. You have a method called onPostExecute() in your fragment but not in asynctask.
Note that your IDE didn't auto-complete #Override there. If you added it there yourself, you'd get an error about not overriding a base class method.
First time solved the answer on my own by proper use of LogCat - This is great
The error was causing due to double declaration of public static ArrayAdapter<String> mForeCastAdapter=null.
Solved it using the line numbers given along each error.
P.S: I was searching for answers for about 4 hours before I posted the question. I guess it was for some good reason.