I am facing problem of having one async task, but I need it use twice, because each time I change different part of GUI (updating progress bar).
Is there any way how to determine in if - else clause, which activity does it call and then make appropriate function for each of both of them?
Edit: huh, answer was here and now there isn't...
Thanks
You can hold a member variable which contains the activity/context it is started from.
//pseudocode
AsyncTask task = new AsyncTask();
task.mActivity = this;
task.execute();
Inside doInBackground just check the activity:
//pseudocode
if (mActivity instanceof MyActivity) {
// ....
} else {
// ....
}
Extract the code from the AsyncTask implementation and delegate that to the Activity. Example:
public interface MyDelegate {
public void updateProgress(....)
}
Your AsyncTask takes a delegate and calls it when appropiate:
public class MyAsyncTask .... {
public MyAsyncTask(MyDelegate myDelegate) { ... }
// somewhere in your code (probably onProgressUpdate)
myDelegate.updateProgress(...)
}
Your Activity/ies implement/s the delegate:
public class MyActivity extends Activity implements MyDelegate {
public void updateProgress(...) {
// update ui
}
// somewhere in your code:
new MyAsyncTask(this).execute(...);
}
Related
I'm using another class to run some stuff in the background while the main activity is being displayed, and passing that activity's context to this background class. I'm starting another activity from this background class, but am unable to call overridePendingTransition here because "method overridePendingTransition(int, int) is undefined for the type BackgroundClass."
public class GetUPC extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPreExecute()
{
...
}
#Override
protected Void doInBackground(Void... arg0)
{
...
boolean dairy;
if(theDairy.equals("N"))
{
//milk test
dairy=true;
}
else
{
dairy=false;
}
//depending on if there is a warning it will either display the warning screen or skip it
if(dairy)
{
Intent intent_warn = new Intent(context, WarningScreen.class);
intent_warn.putExtra("Name", str_name);
intent_warn.putExtra("Size", str_size);
intent_warn.putExtra("Price", str_price);
intent_warn.putExtra("Carbs", str_carbs);
intent_warn.putExtra("Protein", str_protein);
intent_warn.putExtra("Fiber", str_fiber);
intent_warn.putExtra("Sugar", str_sugar);
intent_warn.putExtra("SatFat", str_satFat);
intent_warn.putExtra("TotFat", str_totFat);
intent_warn.putExtra("Cholesterol", str_cholesterol);
intent_warn.putExtra("Sodium", str_sodium);
intent_warn.putExtra("Potassium", str_potassium);
intent_warn.putExtra("Calories", str_calories);
intent_warn.putExtra("Warning", "Contains Dairy");
intent_warn.putExtra("WarningRed", true);
Log.e("Warning",intent_warn.getExtras().getString("Warning"));
context.startActivity(intent_warn);
overridePendingTransition(R.layout.fade_in, R.layout.fade_out); //THIS PART ISN'T WORKING//
}
else
{
Intent intent_menu = new Intent(context, DisplayScreen.class);
intent_menu.putExtra("Name", str_name);
intent_menu.putExtra("Size", str_size);
intent_menu.putExtra("Price", str_price);
intent_menu.putExtra("Carbs", str_carbs);
intent_menu.putExtra("Protein", str_protein);
intent_menu.putExtra("Fiber", str_fiber);
intent_menu.putExtra("Sugar", str_sugar);
intent_menu.putExtra("SatFat", str_satFat);
intent_menu.putExtra("TotFat", str_totFat);
intent_menu.putExtra("Cholesterol", str_cholesterol);
intent_menu.putExtra("Sodium", str_sodium);
intent_menu.putExtra("Potassium", str_potassium);
intent_menu.putExtra("Calories", str_calories);
intent_menu.putExtra("Warning", "Contains no allergens");
intent_menu.putExtra("WarningRed", false);
Log.e("Warning",intent_menu.getExtras().getString("Warning"));
context.startActivity(intent_menu);
}
Log.e("KYLE_DATA_UPCH",str_name+" "+str_price+""+str_size);
}
}
catch (JSONException e)
{
e.printStackTrace();
}
}
else
{
Log.e("ServiceHandler", "Couldn't get any data from the url");
_errorCode=3;
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
...
}
}
So I actually was able to solve the problem by calling overridePendingTransition on the context, casted to an Activity.
((Activity) context).overridePendingTransition(R.layout.fade_in, R.layout.fade_out);
I realize that this is not the best practice and could get messy with a more complex application, but for our purposes right now I think this is ok. I would like to investigate #bariscan Kayaoglu's solution eventually, as it seems more robust.
Just to add some safety to Tims answer, check if the context is an instance of Activity before casting it:
if (context instanceof Activity) {
((Activity) context).overridePendingTransition(R.layout.fade_in, R.layout.fade_out);
}
This simply makes sure that you call a method that is really there. You could also use a try/catch, but I figure this will be enough
Better to create an interface and callback its method.
myInterface mInterface;
public interface myInterface {
public abstract void myTask();
}
public GetUPC(myInterface mInterface) {
this.mInterface = mInterface;
}
and in your doInBackground method, when you are done, call
mInterface.myTask();
Don't forget to implement your interface to your activity and send this to your constructor when you are creating your async task.
myAsyncTask = new GetUPC(this);
And your development platform will inform you to implement unimplemented methods like myTask(). You can do whatever you want in that method while you can access your activity.
I don't think that would be a good idea. Since your class is an Async Task, the activity you would choose may not be the active activity since your async task will work in background. But if you consider memory leaks and null pointers, you can just send your activity to your constructor.
You can't do any UI updation part from background thread, But if wanted to do the same then override onProgressUpdate of AsyncTask class and past your activity starting code in that. to invoke this methode call publishProgress . Before starting activity you have to cancel your AsyncTask otherwise your application will cress.
I have an async task with a doInBackground() method like this:
protected String doInBackground(String... params) {
MyClass session = new MyClass("email", "password");
return session.isAuthorized();
}
While MyClass, which is in a completly different package, is something like this:
private class MyClass {
// fields, constructors, etc
public Boolean isAuthorized() {
// some stuff
log("Action 1...");
// some stuff
log("Action 2...");
// some other stuff
return result;
}
public static void log(String str) {
// HERE I would like to publish progress in the Async Task
// but, until now, it's kinda like:
System.out.println(str);
}
}
The question is: how can I pass log descriptions hold in the log() method, external even to the main Activity "container", to publishProgress() method? I already read this thread: Difficulty in changing the message of progress dialog in async task - but it wasn't a valid source of help, since my method isn't contained in the main class public class MainActivity extends Activity {}.
EDIT #1 -
After some work, I realized that the only way is passing to the external class a referece to the "main" thread, and then implement there a specific method to publish progress. In such a way:
public void log(String str) {
if (mThreadReference==null) {
System.out.println(str);
} else {
mThreadReference.doProgress();
}
}
While mThreadReference points to this AsyncTask:
private class MyClassTask extends AsyncTask<String,String,String> {
#Override
protected String doInBackground(String... params) {
// constructs MyClass instance with a reference and run main method
(new MyClass("email", "password", this)).isAuthorized();
}
public void doProgress(String str) {
publishProgress(str);
}
#Override
protected void onProgressUpdate(String... values) {
// some stuff
}
#Override
protected void onPostExecute(String result) {
}
}
But, obviously, Eclipse is warning me: The method publishProgress() is undefined for the type Activity. How can I write a general and absolute method, in the external class, which I can use in more than one specific AsyncThread?
--> LOGs IN THE LOGIN THREAD 1
/
EXTERNAL CLASS ---> LOGs IN THE LOGIN THREAD 2
\
--> LOGs IN THE LOGIN THREAD 3
I figured out that the only way is importing the istance of the AsyncTask, which has to be public (not the default option!), in the main activity. With this trick, I can invoke the publishProgress method even if it's protected.
// MyClass, in a different package
import MainActivity.MyClassTask mThreadReference = null;
// some stuff...
public void log(String str) {
if (mThreadReference==null) {
System.out.println(str);
} else {
mThreadReference.doProgress("");
}
}
While this is the activity:
public class LoginLanding extends Activity {
// stuff...
public class MyClassTask extends AsyncTask<String,String,String> {
// bla bla bla, some stuff...
public void doProgress(String str) {
// do something
}
}
}
Today I faced similar problem. Previous answer helped be solve problem 50%. It was null pointer exception as mThreadReference is null.
Now you need an instance of the enclosing class in order to instantiate the inner class which is AsyncTask class. Since I had such class defined inside my fragment, what I did was below:
MyFragment fm = new MyFragment();
MyFragment.AsyncTaskClassName aTCN = fm.new AsyncTaskClassName();
After that you can call doProgress method the way Gianlunca has suggested.
My 2 cents !!
I have 3 activities A,B,C. In all the 3 activities i'm using Async task. Is it possible to run all the Async task under a single Async task(Common code).
If possible
1. How to check which task called from which activity?
2. How to check whether the task got completed or not?
May be you want to have a Common async task for that can used to perform long running taks and you want a callback machanism to it use this,
You can implement the same by taking async task class a separate abstract and by implementing a callback interface.
Async Class with callback
Yes it is possible.
Add a Parameter that is used to indicate the calling Activity
Look at JavaDoc of AsyncTask method onPostExecute()
Create your AsyncTask class
public class MyTask extends AsyncTask<Void, Void, Void>
{
// Use a WeakReference instead of holding the Activity object
private WeakReference<Activity> mActivity;
public MyTask(Activity activity)
{
mActivity = new WeakReference<Activity>(activity);
}
#Override
protected Void doInBackground(Void... params)
{
// do common work
return null;
}
public Activity getActivity()
{
return mActivity.get();
}
public void setActivity(Activity activity)
{
mActivity = new WeakReference<Activity>(activity);
}
}
And in each Activity:
MyTask t = new MyTask(YourActivity.this)
{
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
// do some work when finished
}
};
I am developing an application in which i need to send the value of the asynctask's onPostExecute method's result in to the previous activity , ie the activity in which the aync task is being called.pls put some codes. Anyhelp is appreciated
Two ways:
Declare class extending AsyncTask as private class in parent Activity
Pass Handler or Activity itself as param of class extending AsyncTask
If I were you, I'd follow the first option.
Look at DOCS:
class MyActivitySubclass extends Activity {
function runOnPostExecute(){
// whatever
}
private class MyTask extends AsyncTask<Void, Void, Void> {
void doInBackground(Void... params){
// do your background stuff
}
void onPostExecute(Void... result){
runOnPostExecute();
}
}
}
Note 1
Code placed in body of function onPostExecute is already run on Activity thread, you should just mention that this keywords leads to MyTask.this and not MyActivitySubclass.this
Well if your AsyncTask is an inner class, you could simply call a method in your activity from onPostExecute():
public class MyActivity extends Activity {
public void someMethod(String someParam) {
// do something with string here
}
public class InnerTask extends AsyncTask<...> {
protected void onPostExecute(result) {
someMethod(Send parameters);
}
}
}
The onPostExecute method is fired on the main UI thread, so anything done there is already on the AsyncTasks caller.
http://developer.android.com/reference/android/os/AsyncTask.html
Fire an event in the OnPostExecute.
Its an add on to the answer by Marek Sebera, he pointed to use a handler. To keep the code simple and intuitive use an interface. This isn't alien concept, we use it all the time for callback functions (eg: OnClickListner etc..). The code would look some thing like this.
public class InnerTask extends AsyncTask<...>
{
interface ResultHandler
{
void gotResult(<> result);
}
private ResultHandler myResult;
//constructor
public InnerTask(....params...,ResultHandler callback)
{
...
this.myResult = callback;
}
protected void onPostExecute(<>result)
{
...
myResult.gotResult(result);
}
}
public class MyActivity extends Activity implements InnerTask.ResultHandler
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
//do something
//if you want the InnerTask to execute here
InnerTask i = new InnerTask(....params...,this); //send 'this' as parameter
i.execute();
}
#Override
public void gotResult(<> result)
{
//from onPostExecute
}
}
If we want to use the same AsynTask class at multiple sites we can use this type of implementation instead of using nested classes implementation.
I am launching a activity, and once a user is logged in, i want to refresh the main activity. To load the data from the logged in user.
Such as the image and name. I have all of this set up already.
I just need to know is it possible to launch another activity and run its async task again.From an launching an intent from inside another activity?
It's not clear what exactly your design is, but if you need to use the same AsyncTask from two different activities, it should be a separate class, not tied to a particular activity. You can have the two activities implement a common interface, so that the AsyncTask doesn't need to know which activity it is updating. Then instantiate the task by passing a reference to the enclosing activity, and start it as needed. There is no need for one activity to start the other.
Something like:
public interface UserActivity {
void updateUserData(UserData userData);
}
public class Activity1 implements UserActivity {
public void onStart() {
UpdateUserDataTask task = new UpdateUserDataTask(this);
task.execute();
}
public void updateUserData(UserData userData) {
// update
}
}
public class UpdateUserDataTask extends AsyncTask<Void, Void, UserData> {
UserActivity userActivity;
public UpdateUserDataTask(UserActivitiy userActivity) {
this.userActivity = userActivity;
}
// doInBackground, etc implementation.
protected void onPostExecute(UserData userData) {
userActivity.updateUserData(userData);
}
}
As far as I'm aware, AsyncTasks aren't supposed to be reused. They're supposed to be run once and then you can create a new one if you need it.
Once an AsyncTask is executed once, you cannot execute it again. What you can do, though, is control it's "refresh" using onProgressUpdate() and publishProgress() as follows. Note that this will only work for a one-time refresh. If you wanted to be more semantically correct, you might do the "normal" operation in onProgressUpdate() and use onPostExecute() for your resfresh.
public class MyAsyncTask extends AsyncTask<Void, String, Void> {
private boolean isRefresh = false;
#Override
protected Void doInBackground(Void... arg0) {
while (!isRefresh){
//Perform your normal operation
}
//When isRefresh is true, you want to refresh.
this.publishProgress(values);
return null;
}
#Override
protected void onProgressUpdate(String... values) {
// Refresh code here
super.onProgressUpdate(values);
}
public void refreshTask(){
this.isRefresh = true;
}
}
You could then maintain a reference to the object of MyAsyncTask and invoke refreshTask() on it whenever you want to refresh it.