New to using REST API and JSON files, but I have retrieved data from a weather API about my current locations weather conditions. The JSON file has data, such as my location, weather speed etc. I wish to sort all these individual parts of data into textViews so they can be clearly seen.
My Asynch Class:
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.AsyncTask;
import java.util.ArrayList;
import android.widget.*;
import java.util.Date;
import android.util.Log;
public class RESTAPI extends Activity {
ArrayList<String> items = new ArrayList<String>();
// json test string
String jsonTest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_restapi);
// start the AsyncTask for calling the REST service using httpConnect class
new AsyncTaskParseJson().execute();
}
// added asynctask class methods below - you can make this class as a separate class file
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
// set the url of the web service to call
String yourServiceUrl = "http://api.apixu.com/v1/current.json?key=e87e62510df946cc84c02652162112&q=LN11RX";
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... arg0) {
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(yourServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
TextView tv1 = (TextView) findViewById(R.id.jsontext);
tv1.setText(jsonTest);
}
}
}
My httpConnect Class to handle the URL:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.util.Log;
public class httpConnect {
final String TAG = "JsonParser.java";
static String json = "";
public String getJSONFromUrl(String url) {
try {
URL u = new URL(url);
HttpURLConnection restConnection = (HttpURLConnection) u.openConnection();
restConnection.setRequestMethod("GET");
restConnection.setRequestProperty("Content-length", "0");
restConnection.setUseCaches(false);
restConnection.setAllowUserInteraction(false);
restConnection.setConnectTimeout(10000);
restConnection.setReadTimeout(10000);
restConnection.connect();
int status = restConnection.getResponseCode();
// switch statement to catch HTTP 200 and 201 errors
switch (status) {
case 200:
case 201:
BufferedReader br = new BufferedReader(new InputStreamReader(restConnection.getInputStream()));
// create a new string builder to store json data returned from the REST service
StringBuilder sb = new StringBuilder();
String line;
// loop through returned data line by line and append to stringbuilder 'sb' variable
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
br.close();
try {
json = sb.toString();
} catch (Exception e) {
Log.e(TAG, "Error parsing data " + e.toString());
}
return json;
}
// HTTP 200 and 201 error handling from switch statement
} catch (MalformedURLException ex) {
Log.e(TAG, "Malformed URL ");
} catch (IOException ex) {
Log.e(TAG, "IO Exception ");
}
return null;
}
So is there anyway to sort the returned data and put each bit of data into its own textbox?
Screenshot of JSON:
If I am understanding your question correctly, try giving this link a go. Let me know if I have misunderstood and I will try and help you with an alternative.
Edit:
Roughly another way to dynamically create new a new TextView and set data:
TextView view;
LinearLayout currLayout = (LinearLayout) findViewById(R.id.LinearLayout);
for(String value : items) {
view = new TextView();
view.setText(value);
currLayout.addView(view);
}
Make pojo classes for the response you get:
Open any converting site like this:-
http://www.jsonschema2pojo.org/
here paste the json reponse, and click zip, all pojo class will be automatically created for you.
Now in your code do this
protected String doInBackground(String... arg0) {
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(yourServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
Gson gson = new Gson();
/*here Example class is the main pojo class, you can use this class which will be there in the zip, which is created from jsontoPojo converting site */
Example response = gson.fromJson(json, Example.class);
/*
*Now to get data
* just do this */
String name = getLocation().getName();
.
.
.
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
For details view this example,example of data parsing using gson
And you need to add this dependency as well in build.gradle file
compile 'com.google.code.gson:gson:2.4'
Related
My app is a game that is a simple words puzzle, so the levels on it is fetched from a Json web service, using AsyncTask class I can fetch the data in doInBackground and onPostExecute methods, I use local variables in FetchData class to hold the fetched data in, the data is simply 6 strings that are an image URL and level id, and 4 words that is for the buttons, here is the game interface, as you can see there are 4 buttons each one has a word and the player must find it by looking at the picture.
So when the player finds for example 1 word and leaves the app and closes it, the word the player found must be saved and when he return back to LevelActivity he is supposed to continue the level by finding more 3 words.
THE PROBLEM: is that when I find a word and the word shows (so it must be saved) when I close the app and return this happens, depending on my testing I found out that those lines of code that effects the data lag
NOTE: That whenever I reload the activity (manually) everything gets good and instead of having an empty button after recreating the activity the word shows.
data fetching method used: in onCreate & onResume
//This is in LevelActivity.java:
//These methods checks if the button is answered previously or not (button1/button2... variables are true when a word is answered)
public void checkButton1() {
if (button1) {
wordButton1.setText(button1Word); //<--- Here if I changed it to .setText("Test")
//the lag will disappear and the button will show "Test" (without the quotation) and everything's good
//So the problem is when I use .setText(button1Word); that is the word fetched from Json web service.
//it doesn't throw NullPointerException and it doesn't show the word
//but what? it set the text to " "? Why?
//Note other buttons are the same thing too
}
}
public void checkButton2() {
if (button2) {
wordButton2.setText(button2Word);
}
}
public void checkButton3() {
if (button3) {
wordButton3.setText(button3Word);
}
}
public void checkButton4() {
if (button4) {
wordButton4.setText(button4Word);
}
}
//This is FetchData class that fetches the data from Json web service (full code)
package com.example.wordspuzzlejsontest;
import android.content.Context;
import android.os.AsyncTask;
import com.squareup.picasso.Picasso;
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;
public class FetchData extends AsyncTask<Void, Void, Void> {
//Local variable that are used to hold fetched data to transfer them to LevelActivity with static variables
static int currentLevel = 0;
String w1;
String w2;
String w3;
String w4;
String data = "";
String id;
String img;
Context context;
#Override
protected Void doInBackground(Void... voids) {
try {
URL url = new URL("https://api.jsonbin.io/b/5e42776dd18e4016617690ce/7");
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while (line != null) {
line = bufferedReader.readLine();
data = data + line;
}
JSONArray JA = new JSONArray(data);
JSONObject JO = (JSONObject) JA.get(currentLevel);
id = (String) JO.get("id");
img = (String) JO.get("img");
w1 = (String) JO.get("w1");
w2 = (String) JO.get("w2");
w3 = (String) JO.get("w3");
w4 = (String) JO.get("w4");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
int levelId = Integer.parseInt(id);
levelId++;
//Loading the words data to buttons
LevelActivity.levelID = String.valueOf(levelId);
LevelActivity.imageURL = img;
LevelActivity.button1Word = w1;
LevelActivity.button2Word = w2;
LevelActivity.button3Word = w3;
LevelActivity.button4Word = w4;
//Loading level image and level number on the screen
LevelActivity.levelIdTextView.setText(LevelActivity.levelID);
loadLevelImage();
}
public void loadLevelImage() {
Picasso.with(context).load(LevelActivity.imageURL).placeholder(R.drawable.loading)
.error(R.drawable.loading)
.into(LevelActivity.imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
}
});
}
}
Thanks for viewing my answer :D tell me if you need any other code.
At the end of onPostExecute() calls to
checkButton1()
checkButton2()
checkButton3()
checkButton4()
are missing. Because of that, buttons cannot know about the new values and therefore remain empty.
I got a method that fetch data from a web service, so Im looking for a way to call it as a method.
How can I turn this code into a method? or can I call a class in a method? because it has a protected method and extended from a class, but if I implemented the extended class AsyncTask to the class I need to use the Json fetching protected method doInBackground it says "Interface expexted here".
Here's my code that I need to convert it to a method or a way to add it to the class I want to use it in:
Notice: I already extended AppCompatActivity to the class I want to call doInBackground in so I cannot extend FetchData class.
Thanks a lot :D
package com.example.wordspuzzlejsontest;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
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;
public class FetchData extends AsyncTask<Void, Void, Void> {
final String update = "4";
String data = "";
int currentLevel;
Context context;
FetchData(Context context) {
this.context = context;
}
#Override
protected Void doInBackground(Void... voids) {
try {
URL url = new URL("https://api.jsonbin.io/b/5e42776dd18e4016617690ce/" + update);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while (line != null) {
line = bufferedReader.readLine();
data = data + line;
}
JSONArray JA = new JSONArray(data);
SharedPreferences sharedPreferences = context.getSharedPreferences("SHARED_PREFS", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
currentLevel = sharedPreferences.getInt("currentLevel", 0);
for (int i = 0; i < JA.length();) {
i = currentLevel;
JSONObject JO = (JSONObject) JA.get(i);
String id = (String) JO.get("id");
String img = (String) JO.get("img");
String w1 = (String) JO.get("w1");
String w2 = (String) JO.get("w2");
String w3 = (String) JO.get("w3");
String w4 = (String) JO.get("w4");
editor.putString("id" + i, id);
editor.putString("img" + i, img);
editor.putString("w1" + i, w1);
editor.putString("w2" + i, w2);
editor.putString("w3" + i, w3);
editor.putString("w4" + i, w4);
editor.apply();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
```
Summing up my comments:
Create a new class called NewClass.java
class NewClass extends FetchData{
public Void fetchData(Void... voids){ return this.doInBackground(voids);}
}
Since doInBackground in a protected method, you will have private access inside NewClass.java
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. Google's response always returns only 10 books. But if you change "startIndex" parameter in your HTTP request then it will show 10 different books. My app used to only show 10 first books. After I'd done changes to my list of books so that it contained all the found books, my app stopped finding anything and started showing "No books found." regardless of what you put in your search query. Loader doesn't even start.
The only thing Logcat shows is this: "unknown bits set in runtime_flags 0x8000".
How could it be fixed?
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;
ProgressBar loadingIndicator;
int loader_id = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadingIndicator = findViewById(R.id.loading_indicator);
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().toString()
.replace("\\s", "+"));
mAdapter = new BookAdapter(MainActivity.this,
new ArrayList<Book>());
ListView listView = findViewById(R.id.list);
listView.setAdapter(mAdapter);
mEmptyStateTextView = 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).
if (loader_id == 0) {
loader_id++;
loaderManager.initLoader(loader_id, null, MainActivity.this);
}
else {
loaderManager.restartLoader(loader_id, 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(getString(R.string.no_internet_connection));
}
}
});
}
#NonNull
#Override
public Loader<List<Book>> onCreateLoader(int id, #Nullable Bundle args) {
// If there were no books found last time, delete "no books found"
// from the screen.
mEmptyStateTextView.setText("");
// Make loading indicator appear because the loading is about to start
loadingIndicator.setVisibility(View.VISIBLE);
// 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
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."
if (!QueryUtils.hasResults)
mEmptyStateTextView.setText(getString(R.string.no_books_found));
}
#Override
public void onLoaderReset(#NonNull Loader<List<Book>> loader) {
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
}
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?startIndex=0&q=";
private static String searchQuery;
// Variable for checking if there are books found
public static boolean hasResults;
private static int numberOfIterations;
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!");
// Check if there are books found
hasResults = false;
if (hasBooksFound(baseJsonResponse))
{
hasResults = true;
}
numberOfIterations = baseJsonResponse.getInt("totalItems")/10 + 1;
// 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 = "No information";
//Extract the JSONArray associated with the key called "authors"
if (volumeInfo.has("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.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();
}
// 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", e);
}
return image;
}
/**
* Check if there are books found
* #param baseJsonResponse — root JSONObject from the Google Books HTTP response
* #return boolean value (true if there are results, false if not)
*/
private static boolean hasBooksFound(JSONObject baseJsonResponse) {
boolean hasBooksFound = true;
try {
if (baseJsonResponse.getInt("totalItems") == 0) {
hasBooksFound = false;
}
}
catch (JSONException e)
{
Log.e("hasBooksFound: ", "JSONException", e);
}
return hasBooksFound;
}
public static int getNumberOfIterations() {
return numberOfIterations;
}
/**
* 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();
}
}
BookLoader.java
package com.example.android.booklisting;
import android.content.Context;
import androidx.annotation.Nullable;
import android.content.AsyncTaskLoader;
import java.util.ArrayList;
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;
}
// List of 10 books from Google's JSON response
List<Book> currentBooks;
// List of all books combined from the different queries
// to Google Books changing startIndex parameter
List<Book> books = new ArrayList<>();
/*for (int i = 0; i < QueryUtils.getNumberOfIterations(); i = i + 10) {
mUrl = mUrl.replace("x=0", "x=" + i);*/
// Perform the network request, parse the response, and extract a list of earthquakes.
currentBooks = QueryUtils.fetchEarthquakeData(mUrl);
//books.addAll(currentBooks);
//}
return currentBooks;
//return books;
}
}
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;
}
}
Changes that cause the error can be found in BookLoader.java's loadInBackground() method:
public List<Book> loadInBackground() {
if (mUrl == null) {
return null;
}
// List of 10 books from Google's JSON response
List<Book> currentBooks;
// List of all books combined from the different queries
// to Google Books changing startIndex parameter
List<Book> books = new ArrayList<>();
/*for (int i = 0; i < QueryUtils.getNumberOfIterations(); i = i + 10) {
mUrl = mUrl.replace("x=0", "x=" + i);*/
// Perform the network request, parse the response, and extract a list of earthquakes.
currentBooks = QueryUtils.fetchEarthquakeData(mUrl);
//books.addAll(currentBooks);
//}
return currentBooks;
//return books;
}
Also here in QueryUtils class:
numberOfIterations = baseJsonResponse.getInt("totalItems")/10 + 1;
I have made a java class to handle HTTP post requests and it sends back the result in string form. For some reason when I send the request I can print the response to the log but when return the string of the response to update the UI in the method my app crashes. Could any one explain whats happening here? I am trying to get better at java so pointing out any other bad practices would be appreciated .
log:
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
D/OKHTTP3: Request body created
D/OKHTTP3: Request body created 2
D/OKHTTP3: Got Response
D/OKHTTP3: {
"failed": "Asset already exists"
}
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.example.john.okhttp, PID: 3166
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.IllegalStateException: closed
at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:374)
at okio.Buffer.writeAll(Buffer.java:993)
at okio.RealBufferedSource.readByteArray(RealBufferedSource.java:106)
at okhttp3.ResponseBody.bytes(ResponseBody.java:128)
at okhttp3.ResponseBody.string(ResponseBody.java:154)
at com.example.john.okhttp.PostEx.doPostRequest(PostEx.java:40)
at com.example.john.okhttp.MainActivity$Requesting.doInBackground(MainActivity.java:59)
at com.example.john.okhttp.MainActivity$Requesting.doInBackground(MainActivity.java:51)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Application terminated.
the code from both java files will be posted below:
MainActivity.java:
package com.example.john.okhttp;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.json.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class MainActivity extends AppCompatActivity {
private Button btnSendHttpRequest;
private EditText etJsonResponse;
private TextView View;
private TextView View2;
private OkHttpClient okHttpClient;
private Request request;
public final String URL = "http://www.mocky.io/v2/582ac99c280000d50953c316";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//set button and text view values
btnSendHttpRequest = (Button) findViewById(R.id.btnSendRequest);
View = (TextView) findViewById(R.id.view1);
View2 = (TextView) findViewById(R.id.textView4);
etJsonResponse = (EditText) findViewById(R.id.etjson);
//response for button
btnSendHttpRequest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//http request
PostEx example = new PostEx();
new Requesting().execute();
}
});
}
public class Requesting extends AsyncTask<String, String, String> {
// post request stuff
#Override
protected String doInBackground(String... params) {
String id = "444454";
String userName = "john";
PostEx example = new PostEx();
String jsonstr = example.makeJsonForUser(id, userName);
if(example.doPostRequest(jsonstr)== null){
Log.d("OKHTTP3", "null pointer");
}
String response = example.doPostRequest(jsonstr);
Log.d("OKHTTP3", "sending response");
return response;
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
//rewrite text view
try {
// create json ob from response
if(response == null){
Log.d("OKHTTP3", "null pointer");
}
JSONObject jsonObj = new JSONObject(response);
//get the values from the json key value pairs
String id = jsonObj.toString();
//update the text views
TextView textView = (TextView) findViewById(R.id.view1);
textView.setText(id);
} catch (Exception e) {
}
}
}
}
PostEx.java:
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class PostEx {
public String doPostRequest(String jsonstr) {
String url = "http://45.55.92.243/newuser";
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON,jsonstr);
Log.d("OKHTTP3","Request body created");
Request newReq = new Request.Builder()
.url(url)
.post(body)
.build();
Log.d("OKHTTP3","Request body created 2");
try {
Response response = client.newCall(newReq).execute();
Log.d("OKHTTP3","Got Response");
Log.d("OKHTTP3",response.body().string());
String Fresponse = response.body().string();
response.close();
return Fresponse;
} catch (IOException e) {
Log.d("OKHTTP3","Got Exception");
e.printStackTrace();
return null;
}
}
public String makeJsonForUser(String id, String Username){
JSONObject data = new JSONObject();
try {
data.put("id", id);
data.put("name", Username);
return data.toString();
} catch (JSONException e) {
Log.d("OKHTTP3", "JSON Exeption");
e.printStackTrace();
return null;
}
}
}
at okhttp3.ResponseBody.string(ResponseBody.java:154):
public final String string() throws IOException {
return new String(bytes(), charset().name());
}
at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:374):
#Override public long read(Buffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
if (bytesRemaining == 0) return -1;
long read = source.read(sink, Math.min(bytesRemaining, byteCount));
if (read == -1) {
endOfInput(false); // The server didn't supply the promised content length.
throw new ProtocolException("unexpected end of stream");
}
bytesRemaining -= read;
if (bytesRemaining == 0) {
endOfInput(true);
}
return read;
}
Your code works, mostly, up to this point
D/OKHTTP3: Request body created
D/OKHTTP3: Request body created 2
D/OKHTTP3: Got Response
I remember reading, you can only receive the body string once
// Log.d("OKHTTP3",response.body().string());
String Fresponse = response.body().string();
// log Fresponse here
And close the resources in a finally block after the catch
More importantly you are using Okhttp. You don't need Asynctasks! Use the enqueue method instead of execute on the client call object
client.newCall(newReq).enqueue(new Callback() {
// handle response here
});
And you're processing JSON, so Retrofit would help you implement what you're already trying to do
The problem is you can call string() once. But I don't know why
Here is my code:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import android.os.AsyncTask;
import android.util.Log;
public class JsonController
{
private JSONObject inputData, json, finalResult;
private String authentication;
public JsonController()
{
json = new JSONObject();
inputData = new JSONObject();
}
public void createAuthentication(String userName, String apiKey)
{
authentication = "";
}
public void setModel(String model) throws JSONException
{
json.put("model",model);
}
public void setData(String id, String deviceType) throws JSONException
{
inputData.put(id, deviceType);
}
public void getPrediction()
{
new sendJSon().execute("");
return finalResult.toString();
}
private class sendJSon extends AsyncTask<String,Void,String>
{
#Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(authentication);
httppost.setHeader("Content-type", "application/json; charset=utf-8");
try {
// Add your data
json.put("input_data", inputData);
StringEntity se = new StringEntity( json.toString());
httppost.setEntity(se);
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
BufferedReader reader = new BufferedReader(
new InputStreamReader(
response.getEntity().getContent(), "UTF-8"));
String jsonString = reader.readLine();
JSONTokener tokener = new JSONTokener(jsonString);
finalResult = new JSONObject(tokener);
}
catch(Exception e)
{
Log.d("Error here", "Error is here",e);
}
return null;
}
}
}
This code always crashes in getPrediction() because of NulPointerException. NullPointerException is because the Async task take time to generate the String, and the getPrediction() method returns the string before it is ready. All of these methods get called via external classes, so how can I solve this?
you can check whether ASYNCTASK has finished execution or not until then you can halt the returning of string from method getPrediction();
if(CLASSOBJECT!= null && CLASSOBJECT.getStatus() == Status.RUNNING) {
//DO NOT RETURN ANY VALUE
}else{
//RETURN VALUE
}
Try to return the String in your doInBackground method as :
return jsonString;
As you have pointed
outNullPointerException is because the Async task take time to generate the
String, and the getPrediction() method returns the string before it is ready.
You should run your network based operation in thread in doInBackground and then join that thread. Then you should call getPrediction() in onPostExecute(). Thus you'll have the data before the method is called.
Use onPostExecute() instead. onPostExecute() receives the return value from doInBackground() after it finishes. From there you can do whatever needs to be done with your result.
If onPostExecute() isn't flexible enough for you, consider using a CountDownLatch to stall your main code execution until AsyncTask returns.
Here is an sample code which you can implement
public interface AsyncResponseHandler {
public String resultCall(String resultStr);
}
public class MyMainClass extends Activity implements AsyncResponseHandler{
public void doProcessing(){
new AsynTasker(this).execute(null); //here this is instance of AsyncResponseHandler
}
#Override
public String resultCall(String resultStr) {
//here you will receive results from your async task after execution and you can carry out whatever process you want to do.
}
}
public class AsynTasker extends AsyncTask<String,Void,String>{
AsyncResponseHandler handler=null;
public AsynTasker(AsyncResponseHandler handler){
this.handler = handler
}
#Override
protected String doInBackground(String... params) {
// do your processing
return resultString;
}
#Override
protected void onPostExecute(String result) {
this.handler.resultCall(result);
}
}