I'm trying to make an application that uses Asynctask. Particularly, I want to make different http petitions with different JSON in different activities without the activity being frozen while the communication is done.
At first I thought to use asynctask as a private inner class in those activities, but I saw that they share a lot of code. So I thought to make a single class and play with broadcast receivers as I need to monitorize when I receive the result of the http petition, and isn't good to interfere with activity directly in the onPostExecute while in a different class.
What I want to know is, what is more efficient and better practice. Make a class that has the shared code and extends asynctask, then doing inner classes for each activity that extends that one or make a single asynctask that sends broadcast and receive them with each activity when needed.
Excuse my poor english, if needed I'll try to specify more clearly.
Thanks in advance
Background
What I want to know is, what is more efficient and better practice. Make a class that has the shared code and extends asynctask, then doing inner classes for each activity that extends that one or make a single asynctask that sends broadcast and receive them with each activity when needed.
I'm unclear as to why these are your only two options. Create a single AsyncTask, such as JsonPetitionTask, then push a new JsonPetitionTask.Data object. This object would contain your URL, your JSON, and any other data you need.
Setting up the AsyncTask
Something like this:
public class JsonPetitionTask extends AsyncTask<JsonPetitionTask.Data, Integer, Boolean> {
protected Boolean doInBackground(JsonPetitionTask.Data... args) {
for (int i = 0; i < args.length; i++) {
JsonPetitionTask.Data data = args[i];
// Send your JSON; check for errors, and return false if needed.
if (isCancelled()) break;
}
return true;
}
protected void onProgressUpdate(Integer... progress) {
// Show progress?
}
protected void onPostExecute(Boolean result) {
// result is your success true/false.
}
public static class Data {
public String jsonContent;
public String petitionUrl;
public Data(String content, String url) {
jsonContent = content;
petitionUrl = url;
}
}
}
Calling the JsonPetitionTask
Then you can call it like so:
JsonPetitionTask.Data data = new JsonPetitionTask.Data(myJSON, myURL);
new JsonPetitionTask().execute(data);
And voilĂ , you've executed your AsyncTask using only one class with no receivers.
Implementing a callback
Now, if you want to register a callback (something to execute that is specific to the calling code), that's a bit trickier. If this is part of what you're looking for, I'll be glad to edit this post and explain it.
To add a callback, we can use the Runnable class to execute some code after the job is done.
Firstly, we need to add a new field in the Data inner class:
public Runnable callback;
Next, before we call execute(), we need to add a new callback to our data object.
data.callback = new Runnable() {
public void run() {
// Whatever code you want to run on completion.
}
};
Third, in the JsonPetitionTask class, we need a list of things to run:
private ArrayList<Runnable> mRunnables = new ArrayList<Runnable>();
Make sure, in each iteration of the doInBackground() loop, that you do mRunnables.add(data.callback);.
Lastly, in onPostExecute(), we need to call this:
protected void onPostExecute(Boolean result) {
for (Runnable r : mRunnables)
if (r != null) r.run();
}
I do realize I didn't send result to the Runnable, however I didn't feel like implementing a new Runnable type just to handle that. If you need this, I guess that's a bit of homework for you!
The way I found the best is just simply create public class that extends AsyncTask and then you just override onPostExecute function in every activity you use it.
Example:
MyDataTask dataTask = new MyDataTask() //you can add your parameters in class constructor
{
#Override
protected void onPostExecute(Object result) //replace Object with your result type
{
MyActivity.this.doStuff(result); //use result in current activity
}
};
you can also create some custom functions to set private variables in datatask
dataTask.AddParam("user", username);
dataTask.AddParam("pass", pass);
and then just execute it with your args...
dataTask.execute(myArgs);
I have used Async task class as single class. And for every Webservice call i have used unique IntentFilter to Broadcast response.
Put that Broadcast receiver in every class. You have perfect solution.
Its working well.
Related
I have a inner class of asynctask in a class. I defined some variables arrays in that inner class. In doitbackground, i put some values into them.
When i go go onpostexecute, i can use them because they are all in same inner class.
So, i dont need to return something for pass? I am really confused. Is that a bad way, should i define all those inside doitbackground?
Because if i have to pass values, i need to define wrapper because i have to pass 4-5 arraylists. They are different types. But now i can use them and no need return or pass.
I am searching but there is no information about this. In all asyntask examples, they define variables outside of doitbackground.
I usually create a class with these fields in a doInBackground, and pass this class as a result.
This also allows you to pass null if an error happened.
class DataClass {
private int someIntData;
private String someStringData;
}
new AsyncTask<Void, Void, DataClass>() {
#Override
protected DataClass doInBackground(Void... params) {
DataClass data = new DataClass();
// doing some job
if (!errorHappened) {
data.someIntData = 5;
data.someStringData = "Just an example string";
return data;
}
return null;
}
#Override
protected void onPostExecute(DataClass result) {
if (result != null) {
// handle the result
} else {
// error happened
}
}
};
Technically you can do it, but think about encapsulation concept
as you know,
doitbackground happen in another thread than ui thread
onpostexecute happen in ui thread
so there is common pattern that send AsyncTask parameters in Constructor or Execute Argument, by this way, you can make your self sure that Async class can be reused maybe in another application and fully encapsulated.
I am developing an Android application, where most of my activities are fetching content from a MySQL database separately, through an http request.
For this, I am required to add the AsyncTask class separately to each such activity.
Is there a possibility that I may use a common AsyncTask class for all such data fetching and create their objects separately in the activities and fetch data based on the parameters?
Of course, you can create separate class extending AsyncTask for making HTTP requests, which will take appropriate parameters and re-use it in many activities. You don't have to create nested private classes in each activity. You can use input parameters of AsyncTask in doInBackground(parameters) method. See documentation of AsyncTask at: http://developer.android.com/reference/android/os/AsyncTask.html
Below, you can see very simple example of using AsyncTask with some pseudo-code:
public class DownloadDataTask extends AsyncTask<String, Integer, String> {
protected String doInBackground(String address) {
// you should have method for performing HTTP request
// and return result - in this case as String
String result = performHttpRequest(address);
return result;
}
protected void onPostExecute(String result) {
// this method is executed after downloading result
// now, you can perform some action - e.g. display data in a TextView
myTextView.setText(result);
}
}
In this definition: AsyncTask<String, Integer, String> first parameter is type of input parameter (in this case address of the end-point), second parameter is type of progress value (we are not using it in this example), last parameter is type of output value (in our case, HTTP response as String).
When DownloadDataTask class is placed in a separate file, you can re-use it in many activities with the following call:
new DownloadDataTask().execute(address);
Of course, it's very basic example with some pseudo-code. I haven't initialized TextView and I haven't provided implementation of performHttpRequest() method. You should adjust it to your needs. You can also upgrade this solution and pass reference to a TextView or another widget in which your result will be displayed.
Nevertheless, consider using RxJava and RxAndroid instead of AsyncTask. It will make you app simpler and less error-prone. I do not recommend using AsyncTask, because we have many better solutions for Android right now. Regardless of this fact, you decide which solution will be used in your project.
You can achieve this in much the same way as you would with any other Class - create a separate class file that extends AsyncTask and create an object where it's required.
For more flexibility, you could declare the class abstract and use an unknown type parameter, so that you can adjust it as your requirements change.
public abstract class MyTask<T> extends AsyncTask<T, Void, Void>{
#Override
protected void onPreExecute() {
Log.i("Tag", "onPreExecute");
}
#Override
protected void onPostExecute(Void aVoid) {
Log.i("Tag", "onPostExecute");
}
}
And then, whenever you want to create an instance:
MyTask<String> mt = null; //Or URL etc.
//...
if (mt == null) {
mt = new MyTask() {
#Override
protected Void doInBackground(String... params) {
return null;
}
};
}
mt.execute(params);
I've been researching all day trying to find out how to retrieve the values computed in the doInBackground async task. No luck at all.
I'm doing a basic HttpURLConnection request and parsing some XML data from a webpage using the DOM. I successfully store the data in two different arrays
///////////inside doInBackground:
for(int x=0; x<10; x++)
{
username[x] = element.getFirstChild().getNodeValue();
score[x] = anotherElement.getFirstChild().getNodeValue();
}
Now, all I want to do is simply output the values onto a textView.
Among many other things, I have attempted:
protected void onPostExecute(String result)
{
for (int xx = 0; xx<10; xx++)
{
theMainTextView.append(username[xx] + " scored " + score[xx] +"\n");
}
}
Nothing I have attempted works. A recurring error I'm receiving is the NullPointerException. Am I doing something dramatically incorrect? Know of any other (even obscure) methods I could try? Ignore the for loops if that helps...I've omitted a lot of code. Just assume I want to retrieve two values...a username and a score.
Edit: I should probably mention that the AsyncTask ends with return null;
Edit: apparently the code is not faulty but I had a globally declared button which was causing a null Pointer Exception. Sorry about that.
If you get a NullPointerException as stated in the question and this is all of your onPostExecute() code than the field theMainTextView must be null.
You must initialize it before starting the AsyncTask - best place to do so is in onCreate() for Activities or onCreateView() for Fragments.
Although it's not the best practice, your code should work. I think the problem comes from another part. Can you please specify what line is throwing the NullPointerException?
To retrieve values from an AsyncTask you can use listener.
First create interface listner (new file):
public interface AsyncListener {
void onAsyncFinishMethod(String params);
}
Second, use implement for your main class where you call async task (example)
public class MainActivity implements AsyncListener {
Third, create full body for listener method in your main class. You are overriding method from interface. So if you change params you will have to change too in interface. Here you will get all results after task finish and call onPostExecute.
#Override
public void onAsyncFinishMethod(String params) {
Log.d("xxx", params);
}
Fourth, set listener for your async task. It means: In your async task class create this method
public void setOnAsyncFinishedMethod(AsyncListener listener) {
this.listener = listener;
}
Make sure, your async task has private param with type that listener
private AsyncListener listener;
In onPostExecute in async task class call listener method as a last (if you don't have this method, please create it)
#Override
protected void onPostExecute(String params) {
listener.onAsyncFinishMethod(param);
}
Last step, during calling async task in your main class don't forget bind setOnAsyncFinishedMethod method to it
My Example:
private void runMyAsyncTask() {
CustomAsync async = new CustomAsync();
async.setOnAsyncFinishedMethod(this);//<<< before execute use setOnAsyncFinishedMethod
thread.execute();
}
Of course, params used in onAsyncFinishedMethod could be different than you, also onPostExecute.
I've already developed many Android apps that make web service requests, always with the following approach:
In every activity that need to make a web service request, I define an inner AsyncTask that shows a ProgressDialog in onPreExecute(), makes the web service call in doInBackground, and dismisses the progressDialog and updates the results in the UI from onPostExecute().
My concern is: Is there a better (shorter) way to do it? Does it make sense to repeat all that code in every activity? I've been googling a lot, but I've found nothing.
My question is: Couldn't I define a Callback interface? for example this one:
public interface RequestCallback {
public void onSuccess(Whatever whatever);
public void onError(ErrorCode errorCode, String message);
}
... and then define an external class, for example AsyncRequest, that wraps the AsyncTask definition and the ProgressDialog show() and dismiss() statements. So, all activities would just need to instantiate that class, and pass in the following parameters:
1) The method of the web service to run
2) A Bundle with all the parameters of that method of the web service
3) A RequestCallback instance (that could be an anonymous inline instance, where I could update the UI from onSuccess())
4) The context of the Activity (necessary to show the ProgressDialog(), so I would still need a way to prevent configuration change exceptions and so...),
Do you find this a good design? It could save hundreds of lines of code...
Your approach is what I did on my project. And it saved a lot of code as you said, I don't have any complaint about it. But here is some issues that I want to tell you:
You should create new instance of AsyncTask every time you do a background thread to avoid to pile callback.
For the progress dialog, I use it as Singleton, because you don't show many dialogs at the same time. The dialog will be showed when you call the background job, and will be dismiss in the callback. Here is what I did:
private void showProgressDialog(String strMess){
if(null == progressDialog){
progressDialog = new ProgressDialog(MainActivity.this);
}
if(!progressDialog.isShowing()){
progressDialog.setMessage(strMess);
progressDialog.show();
}
}
private void hideProgressDialog(){
if(null != progressDialog && progressDialog.isShowing()){
progressDialog.dismiss();
}
}
void someMethod(){
showProgressDialog("Loading...");
doBackgroundJob(param, new RequestCallBack() {
public void onRequestCompleted(String message, boolean isSuccess) {
hideProgressDialog();
if(isSuccess){
}else{
//do something on error
}
}
});
}
It is an optional, I defined an interface to notify instead of specific class, for each response I use one class, so in base class, I don't care what the response is. Here is it:
public interface OnRequestCompleted<TResponse> {
void requestCompleted(TResponse response);
}
public abstract class BaseRequest<TResponse> implements IRequest{
protected OnRequestCompleted<TResponse> delegate;
protected Class<TResponse> responseClass;
#Override
public void send() {
new HttpTask().execute();
}
private class HttpTask extends AsyncTask<Void, Void, String> {
//...
#Override
protected void onPostExecute(String result) {
if (null != response && null != delegate) {
delegate.requestCompleted(response);
}
}
}
// the response example
public class GroupResponse {
public static class Clip {
public int clipId;
public String detail;
}
public static class Movie {
public int movieId;
public String detail;
}
}
In the subclass of BaseRequest, I will tell it exactly what the response class is (Movie, Clip...)
Hope this help.
If you use it already and it works for you, then yes it makes sense to make it generic and save the time (and bugs) of reimplementing the same thing dozens of times. If you ever find yourself copy-pasting large sections of code with few to no differences you should turn it into a library function or class of some sort. Otherwise if you find a problem later you'll have to fix it in a dozen places. It doesn't even matter if you think of a better way to do things later- its still easier to change it in one place than a dozen.
The only real issue I'd have with your solution is I wouldn't add the progress bar to it- I'd handle it in the calling code and the onSuccess/onError implementations. That way you could also reuse it for a background call that doesn't need to put up a UI. I try to keep my UI decisions as far away from data grabbing code as possible, MVC patterns are good.
I would like to achieve the following behaviour, but I'm not sure how:
User start an activity
Activity starts an AsyncTask
User performs some action that creates a new activity
The AsyncTask finishes and somehow returns the result to the new activity
Is there a way of achieving this behaviour?
Thank you
Create a Service that itself spawns its own thread and does your background processing. You can bind your activities to the service so you can call back into an activity when your processing is complete.
I've been using a variation of what was suggested by Chris:
Start by creating an IntentService, which is the easiest kind of Service to create. Then use SharedPreferences to indicate the state of your IntentService and share values between your Service and Activities. Your Activity can register itself as an OnSharedPreferenceChangeListener in order to know when your Service is done doing work and/or another SharedPreference it cares about has changed.
With IntentService, all you need to do is override the onHandleIntent method. Everything inside onHandleIntent will run on a background thread.
This is a way to do exactly what you want, assuming that the result is an int. You can extend this property, using a parcelable object. Probably, using a Service is still the best choice.
1) Create a class, called Result, that is a wrapper for your result. It must implement the Parcelable interface:
public class Result implements Parcelable {
private int result;
public Result(int i) {
super();
result = i;
}
public void setResult(int result) {
this.result = result;
}
public int getResult() {
return result;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
}
public static final Parcelable.Creator<Result> CREATOR = new Parcelable.Creator<Result>() {
public Result createFromParcel(Parcel in) {
return new Result(in);
}
public Result[] newArray(int size) {
return new Result[size];
}
};
public Result(Parcel in) {
result = in.readInt();
}
}
2) Now, you can use a Result object as a private variable of the first activity:
public class FirstActivity extends Activity {
private Result result;
....
}
3) In your firstActivity, you can start an AsyncTask with a line like this:
new MyAsyncTask(result).execute();
4) Your AsyncTask can be made in this way:
class MyAsyncTask extends AsyncTask<Void, Void, Void> { // you can modify types as you want
Result result;
public MyAsyncTask(Result result) {
this.result = result;
}
....
public mySetResult() {
result.setResult(...); //set your value
}
....
}
5) When you start the second Activity, you can pass your result object to the second activity:
Intent i = new Intent(getApplicationContext(), SecondActivity.class);
i.putExtra("parc", result);
startActivity(i);
6) Finally, from the second activity, you can obtain the result using this code:
Result res = (Result) getIntent().getParcelableExtra("parc");
For more details about parcelable object, can see Android Developer
A Service is a component that allows some code to have a separate lifetime outside of activities without interacting with the user. As others have mentioned, that's certainly one option to consider. If you go with that, IntentService is the easiest way to make the work asynchronous.
However, you could continue to use AsyncTask and just add some code to signal that it's "complete". This is the case when the background work no longer matters if your application is killed, and you're OK with your app being killed before this work completes if the user leaves the application. Another way to see this is if the result of the AsyncTask only matters to either/both of these two activities and not outside. This is an important difference in requirements from needing a Service which again, provides a lifetime outside of activities.
To pass the data, take a look at this doc. There are a lot of ways you could tackle this, but for this kind of thing I prefer a pseudo-singleton approach. (I don't like to use SharedPreferences to pass data, because frankly I don't think that's what the class is for. I prefer this pseudo-singleton approach over a pure singleton because it's more testable. Android uses the singleton approach all over the place though.) I'd create a reference to some sort of AsyncTask registrar class in the Application object. As the Application object is accessible from both activities, the first one can register your AsyncTask with the registrar and the second one can get that AsyncTask and register to listen for completion if it hasn't already finished.