1) I don't underestand why the samples of Android almost use AsyncTasks as private inner classes. I know it is convenient to make it inner class but it makes our class file longer and hard to read. ShelvesActivity of Shelves sample application have even 845 lines. Don't you think it is a bad design or bad construction?
2) If I make my ScanStorageTask external class, what do I have to pass to it? entire Activity or only used widgets?
Example: If I must use a WebView, a Button and a ProgressBar in ScanStorageTask.
I use this:
ScanStorageTask task = new ScanStorageTask(this); // "this" is activity reference, then get the webView, button, progressBar from it.
or this:
ScanStorageTask task = new ScanStorageTask(webView, button, progressBar);
There's nothing wrong with doing it externally, and it actually might be a better design. Passing UI elements around is the kind of tight coupling that can get you into trouble when you have a really large code base anyway.
Why not do it externally and use the "listener" pattern that the UI controls employ? Make your ScanStorageTask its own class, create an OnCompleteListener interface with an onComplete method, and pass that to your ScanStorageTask instance (expose a setOnCompleteListener method or something to that effect). Then, onPostExecute can just do this:
if(onCompleteListener != null)
onCompleteListener.onComplete(data);
That way, you define your UI updates inside your activity based on the data. It's better separation of concerns and will keep your lines of code per class down, as that seems to be what you'd prefer. If you don't already have this, make a class that represents the data you need to pass in and get out, and that's what you pass in to the task as a param to the execute method and what onPostExecute passes to onComplete.
Inner classes allow you to manipulate the UI of an outer Activity inside onPreExecute(), onPostExecute() and onProgressUpdate() without passing the whole UI structure(s) to the AsyncTask. You are just able to use the activites functions for that.
This is useful since manipulating the UI isn't the main purpose of an AsyncTask. It's doing non-UI background work. And for that, what you usually have to pass is some arguments to do this job (e.g. supplying a URL to download a file).
When you declare your AsyncTask external, you basically can't access your UIs resources inside onPreExecute() (no arguments are passed to this at all), and very hard inside the other two UI functions.
I'd say AsyncTask is just made for beeing used as an inner class to do work and update the UI-thread. See the description:
AsyncTask enables proper and easy use of the UI thread. This class
allows to perform background operations and publish results on the UI
thread without having to manipulate threads and/or handlers.
(from the class documentation)
I had the same problem in may application. I wanted to establish a communitation with a PC using a Socket and I wanted my code to be reusable from several Activities/Fragments.
In the first place I tried not to use an inner class but it is very convenient when you have to update the UI so I found an alternative solution :
I created an outer AsyncTask class wich in charge to communicate with the pc and I created inner classes in each of my activites/fragments with only an override of the onPostExecute() method. this way I can reuse my code AND update the UI.
If you just want to get the result of the task and if responsiveness is not essential for your application, you can use the get() method of the AsyncTask class.
Personally I belive that if you use class only at one point, then it's most readable to also define it there - hence the anon inner class.
It does not matter. From design perspective I'd only pass data that is actually needed. However you need to be aware on one possible pitfall - when activity instance gets deactivated (hidden or orientation changed) and your background thread still runs and tries to show some changes, then you can get various errors or nothing s shown at all.
Related
I am new to android programming. I would like to get some advices about AsyncTask. I have created a main activity and one fragment in which i will display content. The use for AsyncTask in my program is to download data from internet. So, the question would be: where it should be? In the fragment's class or activity's?
You have three choices:
You can make it an inner class inside of the Fragment. This is probably OK if this asynchronous task is only specific to the fragment and you won't ever reuse it
You can make it an inner class inside of the Activity. The is better if you have one activity that controls many Fragments that may reuse the same asynchronous task.
You can make it a class of its own if you plan to reuse it in man places in your application or even if you just want it neater. If you do plan on reusing it but the places that are reusing it may need some slight differences, then you might want to abstract it to make it flexible.
Hope that helps.
I've just started to use SoftReference in my Fragment implementation, which sets setRetainInstance to True, and I have a question concerning..
Overview:
What I'm doing is creating a list of soft references in order to be able to have more than one AsyncTask running and still keep references.
BUT, my list keeps references to custom class objects which wrap an AsyncTask but not one themselves.
Question:
Will the behavior of SoftReference stay the same?
Will Android still "couple" my fragment and all those AsyncTask, or am I missing the
whole idea by doing this?
Code Example:
public class myFragment extends Fragment{
List<SoftReference<MyClass>> myList;
...
}
public class MyClass{
private AsyncTask task;
...
}
Edit: I changed the question to a SoftReference question after NKN's comment about WeakReference being "too weak". Thx for that..
The question still remains though:)
If I understood your idea correctly, you want to have an array of AsyncTask instances which you control. In that case, as they probably are enough critical data to store them into that SoftReference and endangering their content because of the Garbage Collector, I'd personally change the SoftReference approach to get advantage of the AsyncTask structure.
I'd declare that ArrayList but directly of MyClass instances (not SoftReferences). You can implement a method in your MyClass class like setTaskId(int), and before executing any task, you would call it with, e.g., the index in your ArrayList of that instance.
You'd then call the execute() method, and once you've terminated processing that AsyncTask, the onPostExecute() method would be called, from which you'd call a method within your Fragment to say this instance of AsyncTask has already ended, and you could simply use yourArrayList.delete(on_that_id).
In your question you mention you do this to have several AsyncTasks. You may hay as much as you want, but read this before doing it because the execute() method of AsyncTask has suffered drastical changes within version changes and you might need to adequate your code to them.
--- EDIT ---
Using AsyncTasks within Fragments has an additional complication, by the time an AsyncTask finished, precisely as you comment, it might have been detached. So you'll have to implement additional logic mechanisms to check whether it is attached via isDetached(). Keep also in mind that isDetached() only works if you detached your Fragment explicitly, otherwise you'll need isAdded().
You don't need to use it all the time, just keep in mind Fragment lifecycles and call it whenever you'd need to make an action that involves Fragments being attached and so.
To simplify it a little, you could move the AsyncTask initialization and logic to the parent activity, so if a Fragment is detached it may act properly (like, e.g., discarding the results if the fragment is no longer attached, or whatever you need).
This is more like an opinion and subjective question :
Which one is better, using inner class for an asynctask ?
Or,
A one on a seperate class and send it the views that need to be updated onPostExecute, and move them inside the constructor
?
I think it depends on your app architecture.
If the thread is used, for example, only within specific Activity, you can create it as an inner class.
But if you want to create something centralized, for example you want to create one class which will open thread for all http requests in the app, it is more reasonable to create separate class.
I prefer inner classes because it's easy to access to context scope (class fields or final local variables, method parameters) without passing fields values to constructors or to ASyncTask.execute(...)
Subclassing is useful when you create reusable code for example I have a ProgressBarAsyncTask wrapping create/show/progress so inner classes implement only the doInBackground where the business logic changes case by case (download, update database, call webservice)
I am trying to write a AsyncTask generic package. Till now, what I've done is call one activity from another send the instance in the Intent to that Activity. This activity is part of the AsyncTask which calls the AsyncLoader.execute() file. I am doing this so that I don't lose any data that the parent Activity sets on the layout.
The other way to do it would be to call the Intent and return the data from the AsyncActivity to the parent Activity. But, both of these methods are slower than implementing the AsyncTask in the parent activity.
But, that clutters up the code. Thus, is there a better way of implementing it?
It sounds like your tight-coupling between the activity and the AsyncTask is causing you issues that you're trying to overcome with a weird workaround.
In my experience the best way to design activities that need an AsyncTask is:
Keep your AsyncTask out of your activity, i.e. make a separate class
that extends AsyncTask. This allows you to reuse the AsyncTask
between multiple activities and make it easier to test.
If you need to return data back to your activity, use the listener and implement the listener on your activity. Then pass your listener to a class that creates the AsyncTask.
Passing of data between intents should be kept to a minimum, if you need to reuse the same AsyncTask from a separate activity you should follow the steps above and execute the task again. If you're going to be calling this through the lifecycle of the app, then consider using a service instead.
I'm pretty much a noob when it comes to Android development. I have an Activity that has a method that pretty much just sets the text of a TextView to whatever text is provided as an argument. I have a second class, which is a Runnable, and I want to be able to give it the Activity (or obtain the Activity somehow), so it can call said method when it needs to.
This Runnable will eventually connect with a server, so it can update the application with information from the server. I've done client/server Java stuff before, so that's not the issue. I just need to figure out how to communicate between this Runnable and the Activity.
Originally, I was going to just pass the Activity itself in, but I read that it would create problems if I did. Instead, I was supposed to pass in an ApplicationContext via getApplicationContext(). I did that, but now I don't know what to do with the ApplicationContext. I tried casting it to the my Activity class, but the program just crashes.
How do I accomplish what I'm aiming at?
There are a few specific ways in Android to handle threading like AsyncTasks etc., you should read up on how to do 'painless' threading here. If it's just a one-off task where you connect to the server, get the value, set it in the TextView and then finish, I think an AsyncTask would be your best option. Continuing background processes are more suited to being services.
you can pass your activity to the constructor of your second Class like this :
public SecondClass(YourActivity _yourActivity){
this.activity = _yourActivity;
//do stuff
}
and in your Activity , you can instanciate your class like this :
SecondClass instance = new SecondClass(this);
NOTE : in your SecondClass , if you want to change the UI of your application , you can use the method runOnUiThread(Runnable);