I can download data using the downloadJSON class for 1 URL using the below code no problem, but I want to get another set of data so it can be displayed with the other. I've tried several different method but to no avail.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_json);
TextView textView = (TextView)findViewById(R.id.JSONTextView);
textView.setText("Downloading JSON!");
new downloadJSON().execute("www.exampleURL.com/data1");
//new downloadJSON().execute(url2??);
}
private class downloadJSON extends AsyncTask<String, String, String>
{
protected String doInBackground(String... args) {
String result = "";
String formattedResult = "";
try {
InputStream stream = (InputStream)new URL(args[0]).getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line = "";
while (line != null) {
result += line;
line = reader.readLine();
}
JSONObject json = new JSONObject(result);
formattedResult = "Downloadable Puzzles\r\n--------------\r\n";
JSONArray puzzles = json.getJSONArray("PuzzleIndex");
for (int i = 0;i < puzzles.length(); ++i) {
formattedResult += puzzles.get(i) + "\r\n";
}
} catch (Exception e) {
e.printStackTrace();
}
return formattedResult;
}
protected void onPostExecute(String pResult) {
TextView textView = (TextView)findViewById(R.id.JSONTextView);
textView.setText(pResult);
}
}
Edit: my question is not a duplicate of the link posted below as my question has much more weight to it with the fact that JSON and URL's are involved. The link is in no way specific to my problem and doesn't help the question.
The Async task doesn't return the control to the calling method. It only runs the onPostExecute() in the main thread after completing the doInBackground() on a background thread.
One approach to transfer control back into calling method is to use interface.
public class DownloadJSON extends AsyncTask<String, String, String> {
private AsyncCallback mCallback;
public DownloadJSON(AsyncCallback callback) {
mCallback = callback;
}
protected String doInBackground(String... args) {
// process background task
}
protected void onPostExecute(String result) {
if (mCallback != null)
mCallback.onComplete(result);
}
public interface AsyncCallback {
void onComplete(String result);
}
}
And then start asynctask using
new DownloadJSON(new DownloadJSON.AsyncCallback() {
#Override
public void onComplete(String result) {
textView.setText(result);
}
}).execute("www.exampleURL.com/data1");
I can suggest you to abandon this old way of picking a thread from thread pool and execute this heavy operation at background thread
check out this awesome lib. RxJava
Related
I need to yield a JsonObject in a class MainActivity from a method doInBackground() in a class Post.
I instantiated the class Post, called the method in it which is being passed parameters into, and tried to assign it to a variable of type JSONObject.
This is the class Post:
class Post extends AsyncTask<String, Void, JSONObject> {
#Override
protected JSONObject doInBackground(String... args) {
JSONObject jsonObject = null;
try {
//Connect to the website
Connection.Response response =
Jsoup.connect(args[0])
.method(Connection.Method.POST)
.data("text", args[1])
.data("language", args[2])
.ignoreContentType(true)
.execute();
Document document = response.parse();
jsonObject = new JSONObject(document.text());
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException err) {
Log.d("Error", err.toString());
}
return jsonObject;
}
}
And this is how I tried to retrieve the object in the class MainActivity:
Post post = new Post();
JSONObject object = post.execute(stringurl, text, "en");
The Java error I get is incompatible types. Required is org.json.JSONObject and found is android.os.AsyncTask <java.lang.String, java.lang.Void, org.json.JSONObject>.
I should be able to capture the JSONObject... how?
You can declare a method in MainActivity which can be called from the AsyncTask once it has fetched the JSONObject:
private onObtainJSONObject(JSONObject jsonObject){
if(jsonObject != null){
// do something with the JSONObject
} else{
// something went wrong - maybe show an error message?
}
}
And you need to override onPostExecute() in the AsyncTask:
public void onPostExecute(JSONObject jsonObject){
// Note: this will be executed on the main thread
MainActivity.this.onObtainJSONObject(jsonObject);
}
If the AsyncTask is not an inner class of your Activity, you can use a callback (a simple interface) as follows
public interface PostCallback{
void onSuccess(JSONObject data);
void onError(Exception exception);
}
Then you let the AsyncTask have a field of type PostCallback and a setter setCallback(PostCallback).
In MainActivity:
Post post = new Post();
post.setPostCallback(new PostCallback(){
#Override
onSuccess((JSONObject data){
onObtainJSONObject(data);
}
#Override
onError(Exception exception){
// exception handling ...
}
});
JSONObject object = post.execute(stringurl, text, "en");
In Post:
private PostCallback callback;
private Exception exception;
public setPostCallback(PostCallback callback){
this.callback = callback;
}
#Override
protected JSONObject doInBackground(String... args){
// keep everything as before but when an Exception occurs,
// assign it to *exception* in the catch block
}
#Override
public void onPostExecute(JSONObject jsonObject){
// Note: this will be executed on the main thread
if(exception == null){
callback.onSuccess(jsonObject);
} else {
callback.onError(exception);
}
}
I have a MainActivity in which I instantiate a class. This class contains basic data and two important methods: GetRequestAccessUrl(params) and getToken(string) which returns an AuthResponse.
The first method runs fine, the string is generated and processed in the application. However, the getToken-method involves networking and is therefore prohibited to run on the main thread and AsyncTask is recommended. The implementation of the latter method is as following:
public AuthResponse getToken(String code) {
if (secrete == null) {
throw new IllegalStateException("Application secrete is not set");
}
try {
URI uri = new URI(TOKEN_URL);
URL url = uri.toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
try {
StringBuilder sb = new StringBuilder();
sb.append("client_id=" + clientId);
sb.append("&client_secret=" + secrete);
sb.append("&code=" + code);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(sb.toString().getBytes("UTF-8"));
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
Reader br = new InputStreamReader((conn.getInputStream()));
Gson gson = new Gson();
return gson.fromJson(br, AuthResponse.class);
} finally {
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
In the MainActivity the entire class is created, the first method is called, some actions are executed and the the getToken-method should run. However I seem to be completely stuck on how to do this, or how to create a (working) AsyncTask regarding this method. Any help is appreciated.
new YourAsyncTask ().execute(code);
private class YourAsyncTask extends AsyncTask<String, Integer, Integer> {
protected Long doInBackground(String... codes) {
AuthResponse res = getToken(codes[0]);
doSthWithRes(res);
}
protected void onProgressUpdate(Integer... progress) {}
protected void onPostExecute(Integer result) {}
}
This will probably work. Depending on what you wanna do with the AuthResponse.
As you can see the ASyncTask is more of a batch processing in the background. I prefer just using a Standard Thread. Additionally you may want to process the AuthResponse in the UIThread.
Here the Quick and dirty version:
/* It would be better to create a subclass of Runnable and pass the Code in the constructor*/
final String code = "testcode";
//Create the new Thread
Thread t = new Thread(new Runnable() {
#Override
public void run() {
final AuthResponse res = getToken(code);
//RunOnUiThread is a method of the Activity
runOnUiThread(new Runnable() {
#Override
public void run() {
doSomethingWithResponse(res);
}
});
}
});
t.start()
Try something like this
new AsyncTask<String, void, AuthResponse>() {
#Override
protected String doInBackground(String... params) {
String id = params[0];
String secret = params[1];
String code = params[2];
//do your stuff
return myAuthResponse;
}
#Override
protected void onPostExecute(AuthReponse result) {
//do stuff with AuthResponse
}
}.execute(clientId, clientSecret, code);
In onPostExecute you can handle the AuthResponse on the UIThread.
I think that I answered this in the following thread :
Java AsyncTask passing variable to main thread
A Http request is done in background in an Asyntask and the result is sent to the main activity thanks to a Callback. I gave a sample code on the answer.
I am getting the following errors:
09-13 06:27:02.268: E/AndroidRuntime(476): at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1961)
09-13 06:27:02.268: E/AndroidRuntime(476): at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794)
09-13 06:27:02.268: E/AndroidRuntime(476): at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1315)
09-13 06:27:02.268: E/AndroidRuntime(476): at android.os.AsyncTask.execute(AsyncTask.java:394)
And below is my code:
private class DownloadVerses extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
String resultString = "";
try {
boolean resultBoolean = Utils.downloadTurboVerseFile(params[0]);
if(resultBoolean){
int progressPercentage = Integer.parseInt(params[2].substring(0,params[2].indexOf(".")));
resultString = "Downloading: "+params[1];
}
else{
resultString = "ERROR Downloading: "+params[1];
this.doInBackground(params);
}
} catch (Exception e) {
Thread.interrupted();
String exceptionString = e.toString();
}
return resultString;
}
#Override
protected void onPostExecute(String result) {
if(result.contains("ERROR")){
downloading.setTextColor(Color.parseColor("#f05036"));
}
else{
downloading.setTextColor(Color.parseColor("#79a1ad"));
}
downloading.setText(result);
if(checkIfVersesDownloaded()){
downloadProgressBar.setVisibility(View.INVISIBLE);
downloading.setText("Verses Have Been Downloaded.");
homeButton.setEnabled(true);
homeButton.setVisibility(View.VISIBLE);
}
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
I am executing the code like this:
while(i < verseTitles.size()){
new DownloadVerses().execute(verseMp3s.get(i), verseTitles.get(i),progressString);
i++;
}
I am thinking there is probably a simple solution that involves adding some code to my DownloadVerses private class?
You cannot create a large number of AsyncTasks in a loop like that. You can create slightly more than 128 before you will get the error that you see.
Your options are:
Do not create individual tasks for each verse. For example, have one task that handles all verses.
Supply your own custom ThreadPoolExecutor that has a larger queue for pending tasks, and use that with executeOnExecutor() on AsyncTask.
Do not use AsyncTask at all, but something other background threading solution.
currently I'm doing something for my project wherein I created a separate Class that will only handle the Asynctask and get the value of the webservices I passed and that class should return the JSON response as a String. Now I already achieved it using taskName.execute().get(); wherein it will wait for the task to complete but the problem is it is also waiting for the task to complete before displaying the screen layout. Making my progressDialog useless and cause a delay on switching screens. Here's my code for now:
For the Class with AsyncTask:
public class UtilGetResponse {
Context context;
Map hash_values = new HashMap();
int DialogType;
String response;
/*
PLAN FOR DialogTypes:
* 0 - Standard Please wait dialog
* 1 - Progress dialog
* 2 - Camera upload dialog
* */
InputStream is = null;
StringBuilder string_builder = null;
public UtilGetResponse(Map values, Context baseContext, int type){
/*initialize class and pass the hash values for parameters*/
context = baseContext;
hash_values.putAll(values);
DialogType = type;
}
public String startTask(){
//TODO CASE WHEN BASED ON THE DIALOG TYPE SPECIFIED
Utilities util = new Utilities();
if(util.isOnline(context)){
try {
new UploaderTaskStandard().execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return response; //THE RESPONSE ONLY SHOW ONCE THE WHOLE TASK IS COMPLETED
}
public class UploaderTaskStandard extends AsyncTask<Map, Void, Void> {
ProgressDialog simpleDialog;
#Override
protected void onPreExecute() {
/*Do something before the async task starts*/
simpleDialog = new ProgressDialog(context);
simpleDialog.setMessage("Please wait");
simpleDialog.show();
}
#Override
protected Void doInBackground(Map... maps) {
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
}
}
private void uploadData() {
response = "null";
String url = hash_values.get("url").toString().replace(" ", "%20"); //get the URL replacing the space with %20
//If the user is trying to upload a file use this part
try {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
/*This will convert the hashMap sent into individual part per key per value*/
Set set = hash_values.entrySet();
Iterator iterator = set.iterator();
/*do a loop passing all the data on a string*/
while(iterator.hasNext()) {
Map.Entry mapEntry = (Map.Entry)iterator.next();
String keyword = String.valueOf(mapEntry.getKey());
String value = String.valueOf(mapEntry.getValue());
/*this will check if the passed data is a URL, file or a simple value*/
if(!keyword.equals("url")){
if(value.matches("(.*)/(.*)")){
File file = new File(value);
Log.v("Does this exists?",String.valueOf(file.exists()));
if(file.exists()){
FileBody upload_file;
upload_file = new FileBody(file);
/*not url but file*/
mpEntity.addPart(keyword, upload_file);
}else{
/*not url and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}else{
/*not URL and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}
}
post.setEntity(mpEntity);
HttpResponse response = client.execute(post);
HttpEntity resEntity = response.getEntity();
is = resEntity.getContent();
} catch (Exception e) {
e.printStackTrace();
response = "null";
}
/*convert JSON to string*/
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
string_builder = new StringBuilder();
String line = "0";
while ((line = reader.readLine()) != null) {
string_builder.append(line + "\n");
}
is.close();
response = string_builder.toString();
}catch(Exception e){
e.printStackTrace();
}
}
}
And to call this:
Map hash_values = new HashMap();
try{
HashMap params = new HashMap<String,String>();
params.put("param1", "YOUR_PARAM");
params.put("url", "YOUR_WEBSERVICE_URL");
//pass parameters
hash_values.putAll(params);
//start async task
UtilGetResponse util = new UtilGetResponse(hash_values, getActivity(), 0);
String result = util.startTask();
Log.v("The result string",result);
}catch (Exception e){
e.printStackTrace();
e.getCause();
Toast.makeText(getActivity(), "Oops problem", Toast.LENGTH_SHORT).show();
}
Is there's a way for me to do this properly without really waiting for the whole task to finish before moving to the next screen? I'm thinking of using a Handler but I'm not really familiar on how to use it anyway.
Your issue is with usage of this
new UploaderTaskStandard().execute().get();
Although you using AsynTask, but still making system wait until result which is against your requirement, what you need is a delivery mechanism, which will notify you back once results are ready. You can take either of two approaches.
change to this, and implement one of below mechanism.
new UploaderTaskStandard().execute();
Implementing handler, and posting result back once result available.
Implementing observer design pattern, where you create an interface with methods such as onResultReady, and passing an object of class implementing above interface to your method startTask, and posting result back from AsyncTask onPostExecute once it is available via interface mechanism.
Going via interface will be very easy and in this way your code will be independent of your network logic, sample code below
// Observer listener interface design
interface ResultListener{
// You can overload this method with data type you want to return
public void onResultReceived();
// Use them in a proper way for sending error message back to your program
public void onTaskCancelled();
public void onError();
}
// This will be your new method signature
public String startTask(ResultListener listener){
// Call it liske this, passing listener reference
new UploaderTaskStandard().execute(listener);
}
// This is your AsyncTask model
public class UploaderTaskStandard extends AsyncTask<ResultListener, Void, Void> {
ResultListener listener;
#Override
protected Void doInBackground(ResultListener... maps) {
this.listener = maps[0];
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
// Notify back to calling program
listener.onResultReceived();
}
}
I am developing an app where in a number of the activities I need to retrieve data via Http.
Once I have the data I process it in the onPostExecute() callback method.
This works fine if I define the async task as an inline class, but as I want to do the same processing in a number of activities I have defined it as an external class.
So the question is, using an external class how do I signal an "event" back to the calling class as a means of passing the data back. I know how to do this in C# but I am new to Java and can not see how to achieve this.
While it is true that a Listener technically correct, I would claim that it is either too complicated or not complicated enough.
Here's an easier solution:
class Foo {
class Bar extends AsyncTask<Void, Void, Result> {
public void onPostExecute(Result res) {
doSomething(res);
}
}
public void doSomething(Result res) {
/// process result
}
public void startTask() {
new Bar().execute();
}
}
Is equivalent to:
class Bar extends AsyncTask<Void, Void, Result> {
private final Foo foo;
public Bar(Foo foo) { this.foo = foo; }
public void onPostExecute(Result res) {
foo.doSomething(res);
}
}
class Foo {
public void doSomething(Result res) {
/// process result
}
public void startTask() {
new Bar(this).execute();
}
}
... which is what you asked: when you pull the inner class out, you lose the implicit pointer. Just make it explicit and pass it in.
The bad news is that there are all sorts of memory leak and lifecycle issues that arise from this solution. I would very much recommend that, before your program gets even bigger, you looking into using an IntentService instead of an AsyncTask and use a Handler, or Activity.runOnUiThread to get the results back onto the UI thread.
One approach:
Define a parent abstract class called ParentActivity (extend Activity of course). Have that contain an abstract method called onAsyncCallback();
Have each class that uses the task to extend that class and implement the method.
In your AsyncTask constructor, have it accept in a ParentActivity.
Eg
ParentActivity activity;
public MyTask (ParentActivity myActivity)
{
this.activity = myActivity;
}
When done in onPostExecute(), simply do
activity.onAsyncCallback(data);
This would also work with an interface, it does the same thing, except intead you have the Constructor accept in the Listener instance.
If you want to do it in a clean way try following approach
First create an enum which contains all your async call names
public enum TaskType {
USER_LOGIN(1), GET_PRODUCTS(2), GET_EMPLOYEE(3);
int value;
private TaskType(int value) {
this.value = value;
}
}
Then create an interface
public interface AsyncTaskListener {
public void onTaskCompleted(String result, TaskType taskType);
}
Now implement this interface in the activity which you are going to call the GetAsyncTask
eg:-
public class LoginActivity extends Activity implements AsyncTaskListener {
protected void onCreate(Bundle savedInstanceState) {
String url = ".....";
new GetAsyncTask(LoginActivity.this, LoginActivity.this, TaskType.USER_LOGIN).execute(url);
}
...
public void onTaskCompleted(String result, TaskType taskType) {
if(taskType == TaskType.USER_LOGIN){
//your login result handling here
}
}
Lastly, this is your AsyncTask
public class GetAsyncTask extends AsyncTask<String, Void, String> {
String outputStr;
ProgressDialog dialog;
Context context;
AsyncTaskListener taskListener;
TaskType taskType;
public GetAsyncTask(Context context, AsyncTaskListener taskListener, TaskType taskType){
this.context = context;
this.taskListener = taskListener;
this.taskType = taskType;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(context, "Loading", "Please wait...", true);
}
#Override
protected String doInBackground(String... params) {
String urlString = params[0];
try {
URL url = new URL(urlString);
HttpURLConnection conn
= (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
if (conn.getResponseCode() != 200) {
throw new IOException(conn.getResponseMessage());
}
// Buffer the result into a string
BufferedReader rd = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
rd.close();
conn.disconnect();
String jsonStr = sb.toString();
outputStr = jsonStr;
} catch (SocketTimeoutException e) {
outputStr = "timeout";
}catch(Exception e){
e.printStackTrace();
outputStr = "error";
}
return outputStr;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
taskListener.onTaskCompleted(result, taskType);
dialog.dismiss();
}
}
Use Listener pattern. Take a look here TaskListener, Task that uses this listener and fragment that calls task
Try making your own http requesting class that extends AsyncTask and than put your code in "doInBackground" call. Thyt way you only have to instantiate your class with url and params and read response. That worked for me.
Here is example:
public class HttpRequest extends AsyncTask<String, Void, String> {
public String url;
public List<NameValuePair> nameValuePairs;
public String httpResponse =null;
public HttpRequest(String _url, List<NameValuePair> _nameValuePairs) {
url = _url;
nameValuePairs=_nameValuePairs;
}
#Override
protected String doInBackground(String... params) {
httpResponse = getWebPageWithFormData(url, nameValuePairs);
return "";
}
public static String getWebPageWithFormData( String url, List<NameValuePair> nameValuePairs ){
String html = "";
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
if (nameValuePairs!=null){
try {httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));}
catch (Exception e){}
}
HttpResponse response = null;
try {
response = httpclient.execute(httppost);
}
catch (ClientProtocolException e) { return ""; }
catch (IOException e) { return ""; }
int responseCode = response.getStatusLine().getStatusCode();
switch(responseCode) {
case 200:
HttpEntity entity = response.getEntity();
if(entity != null) {
try{ html = EntityUtils.toString(entity);}
catch (Exception e) {}
}
break;
}
return html;
}
}