Filip Rosca
Filip Rosca

Reputation: 621

Is it ok to put AsyncTask class inside Activity class?

Is it ok to write an AsyncTask class inside an Activity class? Or should I create another file for this? But, in this case, I couldn't manipulate the UI, or update it from AsyncTask. So, if I'll create another file for AsyncTask, then how could I update the UI in Activity class? Thanks in advance!

Upvotes: 0

Views: 1205

Answers (4)

Andrei T
Andrei T

Reputation: 3083

In general it is not ok because in software development one should try to avoid coupling as much as possible. Now considering the Android framework, imho, UI is UI and background processes, service are a different thing.
Personally, I do not use this approach because I do not like coupling, plus unit testing will be a pain in the ass. However, if one decides to gor for this approach, one should think what exactly it is desired:
Q1: Which class you want to have?
Inner classes or static nested classes?

- Inner classes - In case of inner classes, because the class is a member of its enclosing class, you can access the enclosing instance's variables and members. Here the problem might that during rotation (Activity) some variables inside activity might change.

- Static nested classes - A static nested class behaves like an outer class and therefore cannot access its enclosing's class member and variables. The only way it can use is through an object reference. This approach basically tells you that there is no difference between another class and static nested classes.


EDIT - Some starting points for rotations

Due to the above conditions and the fact that one should really deal with rotation in Android, I use the following concept:

//changed from activity to fragment if rotation is considered.
public CustomFragment extends Fragment{

    @Override
    void onCreate(Bundle saveInstance){
        super.onCreate(saveInstance);
        //for avoiding rotation problems
        setRetainInstance(true);
        new CustomAsyncTask(this, callback).execute();
    }

    public Callback callback = new Callback{
       @Override
       public void onSuccess(){
           //do something
       }

      @Override
      public void onFail(){
          //do something
      }

      @Override
      public void onTaskStarted(){
          //start showing the progress dialog
      }

      @Override
      public void onTaskInProgress(Integer value){
          //progress dialog has some method set progress, check it out
      }

   }

   public interface Callback {
      void onSuccess();
      void onFail();
      void onTaskStarted();
      void onTaskInProgress(Integer progress);
  }  
}


//asynctask class

public class CustomAsyncTask extends AsyncTask<Void, Void, Void> {

      private CustomFragment callback;
      public AsyncCallerTickets(CustomFragment _callback){
            this.callback = _callback;
      }  

      @Override
      protected void onPreExecute() {
         super.onPreExecute();
         callback.onTaskStarted();
      }

      @Override

     @Override
     protected void onProgressUpdate(Integer...progress) {
        callback.onTaskInProgress(progress[0]);    
     }      

    protected Void doInBackground(Void... params) {
          //long process
          return null;
     }


     @Override
     protected void onPostExecute(Void result) {
          super.onPostExecute(result);
          //dialog.dismiss();?
          if(result!=null){
              callback.onSucces();
          }
          else{
               callback.onFail();
          }
     }
  }


By having things decoupled as much as possible one can ease the rotation pain in Android.

Upvotes: 0

JLT
JLT

Reputation: 3172

Is it ok to write an AsyncTask class inside an Activity class?

Yes, it is OK but not a good practice. It's a matter of clean coding, separating a view from a controller. It might also cause memory leak issues. You can pass the reference of your Activity to AsyncTask, but should only be a weak reference.

I couldn't manipulate the UI, or update it from AsyncTask. So, if I'll create another file for AsyncTask, then how could I update the UI in Activity class?

There are a lot of ways to update the UI in that case. You can use a BroadcastReceiver, Handler, or an Interface.

I suggest you use Interface because it's the simplest and I think the most appropriate one.

Here's a sample:

You create a new file for your AsyncTask. You write a new class subclassing AsyncTask.

public class DownloadTask extends AsyncTask<String, Float, Long>
{
    public DownloadTaskListener mListener;

    public interface DownloadTaskListener
    {
        public void onDownloadFinish();
        public void onDownloadProgress(float progress);
    }

    @Override
    protected Long doInBackground(String... arg0) 
    {
        //start downloading

        return 0L; //return download size
    }

    protected void onProgressUpdate(Float... progress) 
    {
        if(mListener != null)
        {
            mListener.onDownloadProgress(progress[0]);
        }
    }

    protected void onPostExecute(Long result) 
    {
        if(mListener != null)
        {
            mListener.onDownloadFinish();
        }
    }
}

As you can see here, you can use these methods of the interface to notify the updates for your UI.

public interface DownloadTaskListener
{
    public void onDownloadFinish();
    public void onDownloadProgress(float progress);
}

In your MainActivity you have to implement to that interface so that the callbacks would work:

public class MainActivity extends Activity implements DownloadTaskListener
{
    DownloadTask download = new DownloadTask();

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        download.mListener = this;
        download.execute("Download URL");
    }

    @Override
    public void onDownloadFinish() 
    {
        MainActivity.this.runOnUiThread(new Runnable() 
        {
            public void run() 
            {
                //updates to UI here.
            }
        });
    }

    @Override
    public void onDownloadProgress(float progress) 
    {
        MainActivity.this.runOnUiThread(new Runnable() 
        {
            public void run() 
            {
                //updates to UI here.
            }
        });
    }
}

Take a look at this line:

download.mListener = this;

You just pass the reference of your Activity (implementing the interface) so that the AsyncTask could call the interface methods of your Activity in order for you to update your Activity.

And also take note that AsyncTask is a Thread. When you update some changes to the UI, you should always do it in the Main Thread or UI Thread. Check this part:

MainActivity.this.runOnUiThread(new Runnable() 
            {
                public void run() 
                {

                }
            });

Sorry, not a professional explainer but I hope I made this clear for you. :)

Upvotes: 2

Vucko
Vucko

Reputation: 7479

You can do it either way. It doesn't pose a problem if you put the AsyncTask Class in the Activity per se.

I prefer to keep my AsyncTask classes in different files though, and the way I interact with the UI is like this:

  • Use the EventBus library
  • Post the appropriate event in onPostExecute
  • Catch that event in your Activity and do whatever it needs to be done

This is a much clearer way to do things, I suppose. And if you use EventBus once, you're gonna love it.

Upvotes: 0

Suleiman Dibirov
Suleiman Dibirov

Reputation: 1023

Yes, it's ok, but in my case, I use another file for this.

In method onPostExecute you can intercat with UI:

  1. You can pass activity/fragment to the AsyncTask and in onPostExecute update your' UI.

  2. You can use Interface(callback) to get results - Android - Jsoup: How to get RESULT from Jsoup.connect("url").get() from AsyncTask

I prefer 2nd option

Upvotes: 0

Related Questions