how do i send data back from onPostExecute in an AsyncTask? - android

my issue is the same as this
Instance variable of Activity not being set in onPostExecute of AsyncTask or how to return data from AsyncTask to main UI thread
but i want to send the data back to the same calling activity. Doesnt startActivity for intents always restart the activity

On option is to use listeners, where you create an interface that your activity implents, something like:
public interface AsyncListener {
public void doStuff( MyObject obj );
}
That way, if you're subclassing AsyncTask, it is easy to add this listener, then in onPostExecute(), you could do something like:
protected void onPostExecute( MyObject obj ) {
asyncListener.doStuff(obj);
}

This depends on your class structure, but if your AsyncTask is a class within your Activity then you can reference methods of that activity.
What you would do is in your onPostExecute method call a function of your Activity that passes some data that was retrieved in the AsyncTask to the activity where you can then use it..
The code would look like this
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
showDialog(DIALOG_LOADING);
new LongRunningTask1().execute(1,2,3);
}
protected Dialog onCreateDialog(int dialogId) {
switch(dialogId) {
case DIALOG_LOADING:
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setTitle("Loading Data");
pDialog.setMessage("Loading Data, please wait...");
return pDialog;
default:
return super.onCreateDialog(dialogId);
}
}
private void onBackgroundTaskDataObtained(List<String> results) {
dismissDialog(DIALOG_LOADING);
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) {
}
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
So the typical flow is like this, set the view of the current page, and then show a progress dialog. Right after that start the async task (or whenever, it doesn't matter really).
After your async task is complete, call a function of the activity and pass it the data.
Don't use shared data within the async task or you risk issues with threading.. Instead once you are done with it pass it to the activity. If you want to update the view progressively while doing work you can use on onProgressUpdate

Related

AsyncTask cannot update progress when slowly creating an object in background

I know how to use AsyncTask to download file, create a zip file or so.. as I call publishProgress() in my loop.
I got stuck when doInBackground() has a single slow line, no loops here, just creating an object where its constructor has slow loops.
I'm not sure about the reasonable way of updating progress in such case.
Here's a sample code:
public class Session {
private QQActivity activity;
public int createdParts;
public DailyClass daily;
private void checkDaily() {
if(!isDailyReady){
new SetAsyncQQDaily().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
class SetAsyncQQDaily extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
String sdq = null;
daily = new DailyClass(Session.this); //Very very Slow!
// Do other network http
sdq = new String(Base64.encode(bos.toByteArray(),Base64.DEFAULT));
// Do some work
return sdq;
}
#Override
protected void onPostExecute(String sdq) {
//Never mind
}
#Override
protected void onPreExecute() {
Toast.makeText(activity,"Preparing the daily. Get ready!",Toast.LENGTH_LONG).show();
}
#Override
protected void onProgressUpdate(Void... values) {
//TODO: Update Value of leftBar
activity.leftBar.setProgress((100*createdParts)/Utils.DAILY_PART_COUNT);
}
}
}
In the slow constructor class, I can set-back an integer of the current progress: createdParts, but cannot call publishProgress.
public class DailyClass implements Serializable {
public DailyClass(Session session){
for(int i=1;i<=partCount;i++ ){ //Very slow loop
session.createdParts = i; //TODO: reflect value to progress bar!?
for(int j=0;j<questionsCount;j++){
objects[i-1][j] = createDefined(i);
}
Log.d("Daily","created part"+i);
}
}
//Bla .. !
}
I also though of passing the object of the AsyncTask to the slow constructor in order to call publishProgress() from there, but cannot. As publishProgress() is accessible only from doInBackground()
What's the best practice?

AsyncTask android different arguments

I am trying to download a file at android using AsyncTask. Because I want to download many files I want to pass the url of the file by arguments. Also I want to change the UI when a user clicks the button that triggers the AsyncTask. As I figured out, apart from the URL I need to pass and the Activity itself so I can have access to it's views so I can change them. And my problem is this how can make the AsyncTask take multiple and different arguments.
Questions:
1.The only way to do this I think is to pass as Object the first argument so anything can pass, right?
public final Example extends AsyncTask<Objects, String, String>
2.When I call the AsyncTask how do I write the function?
Example a=new Example(??????); //I want to pass both a URL and something to give me access to the UI
a.execute(?????);
3. How can I use the "something" stated above argument to change visibility for example at a view?
Thx In advance for your time
Try this..
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
new Example("URL","something").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{null});
else
new Example("URL","something").execute(new String[]{null});
and Example AsyncTask Class
public class Example extends AsyncTask<String, Void, JSONObject> {
// variables passed in:
String url;
String something;
// constructor
public Example(String url, String something) {
this.url = url;
this.something = something;
}
#Override
protected void onPreExecute() {
}
#Override
protected JSONObject doInBackground(String... args) {
}
#Override
protected void onPostExecute(JSONObject jObjOut) {
}
}
AsyncTask takes 3 generic types:
1st is the argument which you send to execution;
2nd is for publishing updates on UI
3rd is for returning result
If you want to skip one or all of them use Void class:
example: new AsyncTask();
If you want to use your own constructor in AsyncTask you should define it in your own class that extends AsyncTask, something like this:
import android.app.Activity;
import android.os.AsyncTask;
public class Example extends AsyncTask<Object, String, String>{
private Object data;
private String data1;
private String data2;
private Activity activity;
public Example(Object data, String data1, String data2, Activity activity){
this.data = data;
this.data1 = data1;
this.data2 = data2;
this.activity = activity;
}
#Override
protected String doInBackground(Object... arg0) {
//Do here all your needs except UI updating
//transfer data you need for UI updating like this:
publishProgress("UI update data argument");
return null;
}
#Override
protected void onProgressUpdate(String... arg){
super.onProgressUpdate(arg);
//Update your UI using activity
}
}
To execute your Async task :
new Example(data, data1, data2, activity).execute(object);
You can use a parameterized constructor to achieve the same. But be aware that if you have multiple arguments you would end up creating multiple constructors. Since you need to modify multiple activities in response you need to pass a context as well.
query = UtilityClass.getQueries(UtilityClass.CHECKRIDE, paramsList);
new Example(HomeActivity.this, UtilityClass.CHECKRIDE).execute(query);
This is how you can create a query/url and pass it to the AsyncTask.
A Sample AsyncTask :
public class Example extends AsyncTask<String , Void, String> {
public Example(Context contextVar, int calloutTypeVar){
context = contextVar;
CALLOUTTYPE = calloutTypeVar;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(context, "Please wait", "Loading...");
}
#Override
protected String doInBackground(String...sqlQueryList) {
String result = "";
String sqlQuery = sqlQueryList[0];
result = UtilityClass.doHttpCallout(sqlQuery);
return result;
}
// Handle the response from the callout
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
progressDialog.dismiss();
switch (CALLOUTTYPE) {
case UtilityClass.SCHEDULERIDE:
ResponseHandlerClass.scheduleRide(result, context);
break;
}
}
I have implemented a response handler to handle the response from various callout results.
Hope this helps in handling your response from different urls.
The idea behind using AsyncTask is to avoid penalizing the responsiveness of the UI when performing blocking operations such as the file download of your scenario. When you use AsyncTask, the doInBackground method executes in another thread than the UI and the UI can not be modified from it so there is no point in passing any UI related data to it.
In order to modify your UI upon your background work completion, you can use the onPostExecute method. As #Tamilan suggested, you may pass the Activity object to the constructor of your AsyncTask if you want to use it there.
An example task could be as follows. Notice that you may replace String with URL, or even Object if you still want to pass different object types to the doInBackground method. Also, this example does not cover error handling beyond returning a simple boolean.
private class ExampleTask extends AsyncTask<String, Integer, Boolean>
{
private Activity mActivity;
public ExampleTask(Activity activity)
{
mActivity = activity;
}
#Override
protected Boolean doInBackground(String... params) {
// this is executed in a background thread
boolean success = true;
// do your background operations
publishProgress(50);
// more operations
return Boolean.valueOf(success);
}
#Override
protected void onProgressUpdate(Integer... progress) {
updateProgressbar(progress[0]); // update UI
}
#Override
protected void onPostExecute(Boolean result) {
// this is executed on your UI thread
if (!result)
// show error
else
// modify your UI
}
}
In order to execute this task:
ExampleTask task = new ExampleTask(anActivity);
task.execute(new String[] { "oneUrl", "anotherUrl" });

How to load EditText fields with data fetched from server

I've got an Activity where before showing the Text/EditText fields, I want to make a call to the server to get the details and then setText of the fields based on the data gotten back from the server.
Below is what I'm doing but the fields don't seem to have the data fetched from the server. I think because I am calling an AsyncTask which gets run in the background and in the mean time the fields are shown to the user.
Question
How does android deal with this? What pattern should I be using?
This activity gets called from MainActivity.java like so:
Intent act = new Intent(getApplicationContext(), MySecondActivity.class);
create.putExtra("theId", "138");
startActivity(create);
in MySecondActivity.java i do the following:
public class MySecondActivity extends SherlockActivity {
private EditText fieldOne;
private EditText fieldTwo;
private MyObj obj = new MyObj();
private int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shared_activity);
fieldOne = (EditText)findViewById(R.id.field_one);
fieldTwo = (EditText)findViewById(R.id.field_two);
id = Integer.parseInt(getIntent().getStringExtra("theId"));
new FetchDetail().execute();
//If I put the below two lines inside the AsyncTask then I get an error:
//"Only the original thread that created a view hierarchy can touch its views."
fieldOne.setText(obj.getOne()); //
fieldTwo.setText(obj.getTwo()); //
}
class FetchDetail extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... strings) {
final RestAdapter restAdapter = new
RestAdapter.Builder().setServer("http://10.0.2.2:8080").build();
final MyTaskService apiManager = restAdapter.create(MyTaskService.class);
final MyObj obj = apiManager.getDetails(id);
return null;
}
}
}
If I put the below two lines inside the AsyncTask then I get an error
Have these in onPostExcute
fieldOne.setText(obj.getOne());
fieldTwo.setText(obj.getTwo());
Do your background computation in doInbackground. Return result in doInbackground. The result of doInbackground computation is a pram to onPostExecute.
So you can update ui in onPostExecute which is invoked on the ui thread
Example:
protected String doInBackground(String... params)
{
// background computation
return "hello"; // return string
}
#Override
protected void onPostExecute(String result) // string
{
super.onPostExecute(result);
fieldOne.setText(result); // hello is set to field One
}
For more info read the topic under The4Steps in the docs
http://developer.android.com/reference/android/os/AsyncTask.html
AsyncTask has 3 methods to override:
1: onPreExecute
Executes on UI thread. So do what you want to do on UI before service call here(Ex: show a progress dialog).
2: doInBackground
Executes in background so perform long running task like fetching data from server.
3: onPostExecute
Executes on UI thread and gets called once doInBackground is completed you can process the result here and update the UI
Ex:
public class RestServiceTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
}
#Override
protected void onPostExecute(String result) {
}
}

Wait for AsyncTask to finish

Hi I'm making Login page that access MySQL database. But my Activity always runs the code that check fail/success before it finishes the AsyncTask.
I tried using asynctask.get() method, but it just freeze my UI and doesn't work.
I tried this answer that said I should call the result-checker method inside onPostExecute().
But since I need to change the TextView to show success/failed, it results in NullPointerException because I instantiate the TextView inside onCreate().
I can't move the TextView instantiation into constructor because it will return NullPointerException unable to instantiate activity ComponentInfo.
Login.java
public class Login extends Activity{
//declare global Views here
protected void onCreate(Bundle bundle){
//Setup views
}
protected void onClick(View v){
//Setup necessary variables
AsyncClass async = new AsyncClass(this);
async.execute(username, password);
}
public void checkSuccess(boolean success){
if(success)
textView1.setText("Success");
else
textView1.setText("Failed");
}
}
AsyncClass.java
public class AsyncClass extends AsyncTask<String, String, JSONObject>{
protected JSONObject doInBackground(String... params){
//access database
}
protected void onPostExecute(JSONObject json){
//read the json result
Login login = new Login();
login.checkSuccess(true);
}
}
Any solution? Thanks
How about making AsyncTask as your inner class?
So your code should look something like below.
public class Login extends Activity {
//declare global Views here
protected void onCreate(Bundle bundle) {
//Setup views
}
protected void onClick(View v) {
new AsyncClass().execute(username, password);
}
public void checkSuccess(boolean success) {
if (success) textView1.setText("Success");
else textView1.setText("Failed");
}
class AsyncClass extends AsyncTask < String, String, JSONObject > {
protected JSONObject doInBackground(String...params) {
//access database
}
protected void onPostExecute(JSONObject json) {
checkSuccess(true / false);
}
}
}
try this
protected void onPostExecute(JSONObject json){
//read the json result
Login login = (Login)context; // object that you pass to task constructor
login.checkSuccess(true);
}
Also you can add progress dialog to your task to indicate some job execution
public class BaseTask<T> extends AsyncTask<Object, Void, T> {
public Context context;
public ProgressDialog dialog;
public BaseTask(Context context) {
this.context = context;
this.dialog = new ProgressDialog(context);
}
#Override
protected void onPreExecute() {
this.dialog.setMessage(context.getResources().getString(R.string.loading));
this.dialog.show();
}
#Override
protected T doInBackground(Object... objects) {
//....
return something;
}
#Override
protected void onPostExecute(T result) {
if (dialog != null && dialog.isShowing())
dialog.dismiss();
// do something
}
}
You cannot edit the UI from the async task thread. In order to make updates to the UI thread, use the onProgressUpdate() method. This method is part of your AsyncTask class, is actually executed in the main UI Thread (I hope you use the async task as a nested class btw, since it is declared public I guess your not. You should change that). The onProgressUpdate() Method is called by the OS itself if you call publishProgress(...) inside your Async task.
A small sample:
protected JSONObject doInBackground(String... params){
publishProgress("test");
}
/**
* This method is part of the Async Task
*/
protected void onProgressUpdate(String... progress) {
login.checkSuccess(true);
}
I would use it this way, just override your onPostExecute where you need it or create a own interface
//create a object f your asyncclass and
//override the onPostExecute where you need it
mInfo = new ASYNCCLASS({
#Override
public void onPostExecute(Object result){
//doSomething something with your views!
}
}).execute();
Waiting is not the answer, because you do not know how long your Asynctask will take to end.
Code above is not tested, just pseudoce, but it should show what i mean.
Do not have my IDE round here, so if anybody would correct the brackets if neccessary would be great!
Greetz

Android: How to update an UI from AsyncTask if AsyncTask is in a separate class?

I hate inner class.
I've a main activity who launches a 'short-life' AsyncTask.
AsyncTask is in a separate file, is not an inner class of main activity
I need async task updates a textView from main Activity.
I know i can update a TextView from onProgressUpdate, if AsyncTask is a inner class
But how from an external, indipendent, async task ?
UPDATE: This looks like working :
In acitivty i call the task
backgroundTask = new BackgroundTask(this);
backgroundTask.execute();
In the constructor i've
public BackgroundTask(Activity myContext)
{
debug = (TextView) myContext.findViewById(R.id.debugText);
}
where debug was a private field of AsyncTask.
So onProgressUpdate I can
debug.append(text);
Thanks for all of you suggestions
AsyncTask is always separate class from Activity, but I suspect you mean it is in different file than your activity class file, so you cannot benefit from being activity's inner class. Simply pass Activity context as argument to your Async Task (i.e. to its constructor)
class MyAsyncTask extends AsyncTask<URL, Integer, Long> {
WeakReference<Activity> mWeakActivity;
public MyAsyncTask(Activity activity) {
mWeakActivity = new WeakReference<Activity>(activity);
}
...
and use when you need it (remember to NOT use in during doInBackground()) i.e. so when you would normally call
int id = findViewById(...)
in AsyncTask you call i.e.
Activity activity = mWeakActivity.get();
if (activity != null) {
int id = activity.findViewById(...);
}
Note that our Activity can be gone while doInBackground() is in progress (so the reference returned can become null), but by using WeakReference we do not prevent GC from collecting it (and leaking memory) and as Activity is gone, it's usually pointless to even try to update it state (still, depending on your logic you may want to do something like changing internal state or update DB, but touching UI must be skipped).
Using Interface
1) Create one Interface
public interface OnDataSendToActivity {
public void sendData(String str);
}
2) Implements it in your Activity
public class MainActivity extends Activity implements OnDataSendToActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
new AsyncTest(this).execute(new String[]{"AnyData"}); // start your task
}
#Override
public void sendData(String str) {
// TODO Auto-generated method stub
}
}
3) Create constructor in AsyncTask(Activity activity){}
Register your Interface in AsyncTask file
and call interface method as below.
public class AsyncTest extends AsyncTask<String, Integer, String> {
OnDataSendToActivity dataSendToActivity;
public AsyncTest(Activity activity){
dataSendToActivity = (OnDataSendToActivity)activity;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
dataSendToActivity.sendData(result);
}
}
Here, your OnPostExecute will call after all task done by AsyncTask and will get "result"
as a parameter, returned by doInBackground(){ return "";}.
While "dataSendToActivity.sendData(result);" it will call activity's overrided method "public void sendData(String str) {}".
An edge case to remember: Be sure to pass this, i.e. you current activity's context to AsyncTask and not create another instance of your activity, otherwise your Activity will be destroyed and new one is created.
Make an static function in your activity class passing context in it to update your text view and then call this function in your AsynkTask class to update.
In Activity class:
public static void updateTextView(){
//your code here
}
In AynckTask class call this function.
Just pass the context (activity or whatever) to your AsyncTask in a constructor and then in onSuccess or onProgressUpdate call whatever you need on the context.
I wrote a small extension to AsyncTask for this kind of scenario. It allows you to keep your AsyncTask in a separate class, but also gives you convenient access to the Tasks's completion:
public abstract class ListenableAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
#Override
protected final void onPostExecute(Result result) {
notifyListenerOnPostExecute(result);
}
private AsyncTaskListener<Result> mListener;
public interface AsyncTaskListener<Result>{
public void onPostExecute(Result result);
}
public void listenWith(AsyncTaskListener<Result> l){
mListener = l;
}
private void notifyListenerOnPostExecute(Result result){
if(mListener != null)
mListener.onPostExecute(result);
}
}
So first you extend ListenableAsyncTask instead of AsyncTask. Then in your UI code, make a concrete instance and set listenWith(...).
The Question has already been answered, still im posting how it should be done i guess..
Mainactivity class
public class MainActivity extends Activity implements OnClickListener
{
TextView Ctemp;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Ctemp = (TextView) findViewById(R.id.Ctemp);
doConv = (Button) findViewById(R.id.doConv);
doConv.setOnClickListener(this);
}
#Override
public void onClick(View arg0) // The conversion to do
{
new asyncConvert(this).execute();
}
}
now in the async class
public class asyncConvert extends AsyncTask<Void, Void, String>
{
SoapPrimitive response = null;
Context context;
public asyncConvert(Context callerclass)
{
contextGUI = callerclass;
}
.
.
.
.
protected void onPostExecute(String result)
{
((MainActivity) contextGUI).Ctemp.setText(result); // changing TextView
}
}
/**
* Background Async Task to Load all product by making HTTP Request
* */
public static class updateTExtviewAsyncTask extends AsyncTask<String, String, String> {
Context context;
ProgressDialog pDialog;
String id, name;
String state_id;
//--- Constructor for getting network id from asking method
public updateTExtviewAsyncTask(Context context,String id,String city)
{
context = context;
state_id = id;
city_name = city;
}
/* *
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute()
{
super.onPreExecute();
pDialog = ProgressDialog.show(context, "","Please wait...", true, true);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args)
{
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
YourClass.UpdateTextViewData("Textview data");
}
}
// place this code inside your activity class and also declare updating textview static
public static void UpdateTextViewData(String tvData)
{
tv.setText(tvData);
}

Categories

Resources