Reputation: 621
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
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
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
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:
onPostExecute
This is a much clearer way to do things, I suppose. And if you use EventBus once, you're gonna love it.
Upvotes: 0
Reputation: 1023
Yes, it's ok, but in my case, I use another file for this.
In method onPostExecute you can intercat with UI:
You can pass activity/fragment to the AsyncTask and in onPostExecute update your' UI.
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