How to get result from AsyncTask without blocking UI thread? - java

I have external class extending AsyncTask to get string from website to parse it as JSONObject or JSONArray. Currently i am using method .get() to get the result, but app is dropping frames, while waiting for server to respond. I want to use it reusable because I am getting data from many different classes.
My Asynctask class:
public class JsonTask extends AsyncTask<String, String, String> {
protected String doInBackground(String...params) {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
Log.d("Response: ", "> Establishing Connection" );
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
Log.d("Response: ", "> " + line);
}
return buffer.toString();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}}
Now i am getting data by simply:
String result = new JsonTask().execute(url).get();

As per documentation for the get method:
Waits if necessary for the computation to complete, and then retrieves its result.
Therefore it will block the UI until it finishes the background task which will return the result.
You can create a listener that accepts the return value of the doInBackground in your JsonTask which the onPostExecute will call the listener.

I think you can read document again .
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute()
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

Related

Getting JSON Array from top/new category of Reddit in Java not working as expected

I am passing the url https://www.reddit.com/r/wallpapers/top/.json into my method for getting the JSON array of a subreddit. However, it only returns the JSON array for the hot category rather than the top or new categories. I have checked the URL and code thoroughly and have tried other different formats of the URL to only get the same results. For some reason all JSON gets all return only the hot page or default subreddit URL. But when I visit the URL in my browser that I've linked, it displays the correct JSON array for the top category. (Android Studio)
Here's the beginning of my JSON task that returns the array:
private class JsonTask extends AsyncTask<String, String, String> {
protected void onPreExecute() {
super.onPreExecute();
}
protected String doInBackground(String... params) {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream stream = null;
try {
stream = connection.getInputStream();
} catch (Exception e) {
Log.e("Subreddit Closed", urlString);
connection.disconnect();
return null; //if can't retrieve JSON file
}
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
return buffer.toString();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (connection != null) {
connection.disconnect();
}
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
Update: This was an issue with Reddit's API, it is now working as expected. Take caution of URL formats as */hot/.json is equivalent to */.json

Android app not Responding in Background when trying to get HttpURLConnection

I am having an issue, when I try to update some information in the background in my app I constantly get reports of ANR (Are not responding).
The code block is as follows
class getServerTime extends AsyncTask<String, Long, Long> {
private Long getInternetData() {
String sURL = "http://crystalmathlabs.com/tracker/api.php?type=time"; //just a string
// Connect to the URL using java's native library
HttpURLConnection connect = null;
try {
int responseCode = -1;
List<String> listSet = new ArrayList();
URL url = new URL(sURL);
connect = (HttpURLConnection) url.openConnection();
connect.setConnectTimeout(R.integer.timeoutLengthWithACapitalT);
connect.setReadTimeout(R.integer.timeoutLengthWithACapitalT);
connect.connect(); //this is the timeout line
responseCode = connect.getResponseCode();
if (responseCode < 400) {
BufferedReader in = new BufferedReader(new InputStreamReader(connect.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
listSet.add(inputLine);
in.close();
if (listSet.get(0) != null)
return Long.parseLong(listSet.get(0));
}
return -1L;
}
catch (java.io.FileNotFoundException e) {
Log.e("getServerTime", "File Not Found: " + e.getMessage());
} catch (Exception e) {
Log.e("getServerTime", "Unknown Exception " + e.getMessage());
}
finally{
if (connect != null)
connect.disconnect();
}
return -1L;
}
#Override
protected Long doInBackground(String[] params) {
Long response;
new Scores();
try {
response = getInternetData();
if (response != null) {
return response;
}
return -1L;
} catch (Exception e) {
Log.d("Error in getServerTime", "" + Log.getStackTraceString(e.getCause().getCause()));
}
return -1L;
}
}
On connect.connect I receive timeouts.
Attached below are two examples of the ANR reports, I simply can't figure this out, please help!
https://pastebin.com/F7iZ267D
https://pastebin.com/W1eSdH4F
at android.os.AsyncTask.get (AsyncTask.java:507)
at
com.yargonauts.burk.runescapemarketwatch.crystalMathLabs.a.a
(crystalMathFunctions.java:30)
You are calling AsyncTask.get(), this pretty much negates the use of an asynctask since it blocks the thread until the async task is done.
This blocking causes the ANR since the main thread is stuck waiting for the result.
You need to override the onPostExecute(Result) method and perform the follow-up work there. Check out the Usage section of the asynctask javadoc: https://developer.android.com/reference/android/os/AsyncTask.html

Run code in AsyncTask stucking UI

I'm trying a copule of days to figure it out, without success,
Target: get use country by IP using asyncTask without stucking UI, and save it into string parm.
Problem: The UI doen't move until the asyncTask finish it job :/
MainActivity ,onCreate method:
.......
JSONObject jsonObject;
try {
jsonObject = new getJSONObjectFromURL(LoginActivity.this).execute("http://ip-api.com/json").get();
if (jsonObject != null) {
if (!jsonObject.getString("status").equals("success"))
throw new InterruptedException();
Log.d(TAG, "onCreate: jsonObject.tostring:" + jsonObject.toString());
countryLocation = jsonObject.getString("country");
Log.d(TAG, "onCreate: countryByIP " + countryLocation);
}
if (!isNetworkAvailable()/* || countryLocation == null*/)
Toast.makeText(MainActivity.this, "Please enable data.", Toast.LENGTH_SHORT).show();
//in button click redo all that code for no internet connection,.
} catch (InterruptedException | ExecutionException | JSONException e) {
e.printStackTrace();
}
asyncTask:
public class getJSONObjectFromURL extends AsyncTask<Object, Object, JSONObject> {
private Context context;
public getJSONObjectFromURL(Context context) {
this.context = context;
}
#Override
protected JSONObject doInBackground(Object... params) {
HttpURLConnection urlConnection = null;
URL url = null;
try {
url = new URL((String) params[0]);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setDoOutput(true);
urlConnection.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
String jsonString;
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
jsonString = sb.toString();
return new JSONObject(jsonString);
} catch (JSONException | IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(JSONObject result) {
//Update the String..
}
}
Everything works perfect, i get the Json, able to read the Country from it, but MainActivity waiting for the onPostExecute to continue.
You are running your AsyncTask in a synchrounous state.
jsonObject = new getJSONObjectFromURL(LoginActivity.this).execute("http://ip-api.com/json").get();
By calling get() you are waiting until the Task has finished it's job. Do not use get() if you want to have a truly asynchronous process. You can use a callback to get notified.

AsyncTask LIFX Bulb response

I have got problem with read output form request.
public JSONArray listLights()
{
try
{
URL adres = new URL("https://api.lifx.com/v1/lights/all");
HttpURLConnection polaczenie = (HttpURLConnection) adres.openConnection();
polaczenie.setRequestProperty("Authorization", "Bearer " + apiKey);
polaczenie.setRequestMethod("GET");
BufferedReader wejscie = new BufferedReader(new InputStreamReader((polaczenie.getInputStream())));
StringBuilder odpowiedz = new StringBuilder();
String json;
while ((json = wejscie.readLine()) != null)
odpowiedz.append(json);
wejscie.close();
return new JSONArray(odpowiedz.toString());
}
catch (Exception wyjatek)
{
wyjatek.printStackTrace();
}
return new JSONArray();
}
StackTrace
I added to AndroidManifest Internet access too.
Welcome to leave any comments. :P
EDIT:
I google internet and found partial solution. Added AsyncTask, but now I'm receiving '429' response code.
public class JSONTask extends AsyncTask<String, String, String>
{
String apiKey = "blah_blah_blah";
String txtresult;
#Override
protected String doInBackground(String... params) {
HttpsURLConnection connection = null;
BufferedReader reader = null;
try
{
URL adres = new URL(params[0]);
HttpsURLConnection polaczenie = (HttpsURLConnection) adres.openConnection();
polaczenie.setRequestProperty("Authorization", "Bearer " + apiKey);
polaczenie.setRequestMethod("GET");
System.out.println(polaczenie.getResponseCode());
InputStream stream = polaczenie.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null)
{
buffer.append(line);
}
return buffer.toString();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally {
if (connection != null)
connection.disconnect();
try
{
if (reader != null)
reader.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(String s)
{
super.onPostExecute(s);
widok.setText(s);
}
}
My current StackTrace
EDIT2:
New day, new surprise. I figure out that I'm making connection with Bulb once/twice on every 10 attempts. Any ideas?
HTTP Status code 429 means too many requests in a given an amount of time. So how many requests exactly are you doing?
android.os.NetworkOnMainThreadException it means, that You have to make a htttp request from another threat than UIthread. Why are you using async task ?
Edit: You can also try make a call from postman and maybe You will see the problem.
In the end, everything is working. Problem was on the side of bulb or Lifx Cloud.

Code skips to finally after HTTPURLConnection connect

I'm trying to learn Android development by creating the movies app from the Google Udacity course. In my code below upon executing urlConnection.connect(), the code automatically goes to the finally block without any errors/exceptions.
Can you please help me see what's wrong with my code? Thanks!
public class FetchMoviesTask extends AsyncTask<Void, Void, String> {
private final String LOG_TAG = FetchMoviesTask.class.getSimpleName();
protected String doInBackground(Void... params) {
String JSONResponse = null;
//These are declared outside as they'll be used in both try and finally blocks
BufferedReader reader = null;
HttpURLConnection urlConnection = null;
try {
//construct your URL from a URI
Uri.Builder URIbuilder = new Uri.Builder();
URIbuilder.scheme("http")
.authority("api.themoviedb.org")
.appendPath("3")
.appendPath("movie")
.appendPath("popular")
.appendQueryParameter("api_key", BuildConfig.TMDB_API_KEY);
//instantiate URL
URL popularURL = new URL(URIbuilder.toString());
Log.v(LOG_TAG, "Built URL: " + popularURL.toString());
//create and open HTTP connection
urlConnection = (HttpURLConnection) popularURL.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
//InputStream is needed to read the response
//http://developer.android.com/reference/java/net/HttpURLConnection.html
InputStream inputStream = urlConnection.getInputStream();
if (inputStream == null) {
Log.e(LOG_TAG, "Null input stream");
return null; //no data returned from HTTP request
}
//!!want to see what InputStream looks like
Log.v(LOG_TAG, "inputStream.toString(): " + inputStream.toString());
//BufferedReader is used to wrap a Reader and buffer its input
//to read InputStream, a "reader" is required and that's InputStreamReader (duh)
//http://developer.android.com/reference/java/io/BufferedReader.html
reader = new BufferedReader(new InputStreamReader(inputStream));
//!!want to see what BufferedReader looks like
Log.v(LOG_TAG, "reader.toString(): " + reader.toString());
//replaced StringBuffer w/ StringBuilder. will it work?
StringBuilder builder = new StringBuilder();
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.
builder.append(line + "\n");
}
if (builder.length() == 0) return null; //empty stream. no point in parsing
JSONResponse = builder.toString();
Log.v(LOG_TAG, "JSON Response: " + JSONResponse);
return parseJSON(JSONResponse);
} catch (IOException e) {
Log.e(LOG_TAG, "Error", e);
return null;
} catch (JSONException e) {
Log.e(LOG_TAG, "Error parsing JSON", e);
return null;
} catch (Error e) {
Log.e(LOG_TAG, "Unknown error", e);
} finally {
if (urlConnection != null) urlConnection.disconnect();
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
//will only be triggered if there's an error getting/parsing data
return null;
}
}
CommonsWare pointed me to the possible cause of the issue, which was a missing INTERNET permission. Adding it solved my problem. Thanks for all the responses!
The problem is this comment in your code:
//will only be triggered if there's an error getting/parsing data
That's false.
The return in the try block won't be ignored if a finally block is defined, only if that finally block also includes a return.
In other words, if you have "return" in both try and finally, the one inside finally is the one which gets executed.
Source: Java try-finally return design question
Edit:
You may want to check this out: Does finally always execute in Java?

Categories