I got an activity which should do 2 different things in parallel act as SplashScreen Activity:
wait for 1.5 seconds to display app splash screen
copy some files from assets to device storage in background
Activity implement initial delay (task1) by a handler and file copy (task2) by AsyncTask
Problem: delay of this activity should be such that which both task complete and then start next activity.
I should note that these two task are running in parallel in background and time of copying files may differ each time (some times longer than 1.5seconds, some times shorter).
In other word starting next activity must be synchronized by finishing of both background tasks.
So, How this can be implemented?
I think the easiest thing to do is in the beginning of your async task, get the current time. When you're done with the work, get the current time again. If this is less than 1.5 secs, then do a Thread.Sleep() for the difference.
Start the next activity in the postExecute(). Something like this:
private class DoStuffAsync extends AsyncTask<Void, Integer, Long> {
protected Long doInBackground() {
long start = new Date().getTime();
// copy assents
long end = new Date().getTime();
if ( end-start < 1500 )
Thread.sleep( 1500-(end-start));
return totalSize;
}
protected void onPostExecute(Long result) {
startActivity( new Intent(Activity.this, NewActivity.class));
}
}
Updated: Fixed math problems.
Your design is solid. The detail that you're after to sync the handler and AsyncTask chimes from the realization that both will be executed from the UI thread and therefore you don't need to worry about any concurrency issues. The easiest way then to check whether your new Activity should be started, would be to create two boolean flags. One for the 1.5s timer and the other for the file copy task. Then when either process finished, it checks whether the other flag is set to complete, and if it is, a new Activity will be launched, otherwise the completed task will set it's completed flag to true and when the remaining task completes it will launch the Activity.
Related
In my android application, I need to execute some operations, like creating some files in the internal storage and download some data from a server on start of the app. Only when these operations are finished, the rest of the application can work properly. So it’s my intention to show an image and a progress bar when the app is opened, and after all these operations are done, a new activity is opened.
Now, I know it’s common to put potentially long running operations like downloads and file reading/writing into async tasks. However, in my case, I can’t really continue until these operations are finished, so do I need to use Async Tasks, or is it ok for the application to block in this case?
Or should I start an Async Task and then wait for it, using the get() method?
Thanks for advices, I'd really like to do this the right way.
You care create a splash activity which should be your launcher activity.
Then in the splah, start an async task for downloading and all, then after the completion of the task, you can move to your desired activities.
You can also use a thread for this purpose; in that case, you need to handle the task completion callback using a handler or runOnUIThread mechanisms.
Better to use Async task for this.
You need to put it in an async task or different thread. Internet requests on the main ui thread will create a fatal error on the newest (android 4+) systems.
It is always better to show a loading image or progress dialog than showing a hanging white screen.
For all long blocking jobs, async is required.
If you have the splash screen as your main activity, add this code for your async task:
private class YourTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
//Do your work here
//Note that depending on the type of work, you can change the parameters
//of the async task.
return count;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
//Put here the code when your job is done, for example:
//start a new activity (your real main activity).
}
}
After that, in your oncreate method of the activity, call
new YourTask ().execute(url1, url2, url3);
Android Asynctask Generally Understanding Questions.
If I want to make Asynctask SyncTask, how do I do that?
AsyncTask A = new AsyncTask();
AsyncTask B = new AsyncTask();
A.execute();
B.execute();
If I want A to finish before B starts how should I do that?
If I close an Activity, does the AsyncTask call on that activity gets destroy?
If I close the whole application, does the AsyncTask call on that application gets destroy?
call b.execute() in onPostExecute() of A
AsyncTask is an abstract class so you must extend it in order to add your app specific functionality to it. You implement the doInBackground() method to do what ever work is required. The AsyncTask documentation explains it in detail. I will give a brief answer to each of your question.
If I want to make Asynctask SyncTask, how do I do that?
You have the right idea with creating the async task, however as I mentioned before you have to subclass the async task to actually do some work.
Here is an example (note that the Void types do have meaning however the documentation covers them in great detail)
public class MyTask extends AsyncTask<Void, Void, Void>
{
//This method will run on the back ground thread
#Override
protected Void doInBackground(Void... params)
{
// All the heavy work should be done here e.g.
// loading from network, database etc.
return null;
}
}
Then in your activity you would create and run this task as follows :
MyTask myTask = new MyTask();
myTask.execute()
If I want A to finish before B starts how should I do that?
As the documentation states:
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
Which means if you are using honeycomb or later async tasks will run on a single thread. So normally A should get executed before B if you execute in that order. But I would do something like this: Launch A, then when onPostExecute() of A gets called, you know that A is done, you can start your task B from this method to be sure that A finishes before B.
If I close an Activity, does the AsyncTask call on that activity gets
destroy?
The short answer to this question is No. The async task will continue to run even if the activity has called on destroy. However if the async task is tied to the activity life cycle you need to make sure you end the task when the activity dies or you will run into problems.
If I close the whole application, does the AsyncTask call on that
application gets destroy?
I am not 100% sure about this one. I believe the behavior is unspecified since now its up to Android to collect resources. It may decide to kill the task right away if its low on resources or it may not. However you should avoid this design. If you need to run something after your application has closed, then look at the Service class.
Take a look at http://www.compiletimeerror.com/2013/01/why-and-how-to-use-asynctask.html#.VNtB1LDF_38 it may help you.
I have three AsyncTasks in my Splash screen Activity that downloads app data for caching. I don't want to actually start the next Activity until all task's finish.
Is there an effective way of doing this?
Here is how I call my Tasks in the onCreate:
TrendingTask task = new TrendingTask(SplashActivity.this);
task.execute();
TrendingWatchingTask task2 = new TrendingWatchingTask(
SplashActivity.this);
task2.execute();
new MasterTask().execute();
They are all called, one after the other. I don't actually start the next Activity until it gets to the onPostExecute() in MasterTask. But I believe this is a gamble cause the other two may not be finished.
Note: MasterTask is an inner class while the other two are their own classes.
You can keep a global integer counter and increment it every time an AsyncTask starts(in its onPreExecute()) and decrement it in onPostExecute(). And when you launch your activity you can check if this counter is equal to zero. If it isn't - then you know you've got AsyncTasks running.
I'm doing an app which use the new Google Maps V2, when the Map Camera moves, I need to reload the points to the new camera position, I do it with a asynctask.
The problem is when I move the camera position multiple times, app loads the points multiples times. So, I cancel the async task when the camera is moved and I don't load new point until the task is cancelled. I have an empty while loop to do it, Is there a better solution to do it?
// LOAD NEW POIS ASYNC
private void updatePoisInMap( ){
....
if (refresh_pois_async != null) {
refresh_pois_async.cancel(true);
while (!refresh_pois_async.isCancelled()) {
}
}
refresh_pois_async = new RefreshPoisAsync( ).execute( lowerLeftCorner, topRightCorner);
}
Actually, I don't think your while loop will work, or at least not as you would expect it to. The AsyncTask.isCancelled() method is normally used inside the doInBackground() method, to support early cancellation. You have several options to do what you want:
override the onCancelled() method of your AsyncTask. This is guaranteed to be called on the main thread when your doInBackground() is finished and you called cancel() on your task beforehand. In your onCancelled() you can then start a new AsyncTask. Use the isCancelled() in your Task's doInBackground() for early exit, as described above.
don't cancel the current task, but let it run. In the meantime the user may move the map multiple times. As long as a task is still running, just set a flag that it should be rerun. At task completion (override onPostExecute() in your AsyncTask) check the flag and start over if it was set.
combine the above to find the right balance between responsiveness (start loading the right POIs quick enough) and over-processing (don't start every time, but wait for the user to stop scrolling).
In my app I have a background task (using AsyncTask) that downloads stuff from a web site.
This task can be called from two separate activities, and it is possible to call it twice. So in "activity1" the background task "update" is called, and runs for a while (it takes something like 5-10 seconds usually).
Then while it's running, user switches to "activity2" and runs "update" again.
This gives problems: either a crash when both try to clear the database (command: DELETE FROM table) at the same time, causing a "database locked" error. Or they try to put the same item in the database causing a duplicate.
I've tried to solve this by setting a static boolean flag to true when a task is active.
When the task is called, it will check for this flag, and if true (i.e. the same task running on another thread) it goes into a wait loop using a handler until this flag clears, and then returns. This to make sure that when the background task returns, the update has been done. I have to use a Looper for that: this sometimes fails with an error "can create only one looper per thread". And I really have it in a way that only one looper can be started, this is the offending code, which appears at the start of the background task:
if (active) {
Looper.prepare();
handler = new Handler();
handler.postDelayed(new Runnable() {
int count = 0;
#Override
public void run() {
if (active) {
count++;
if (count < 1000)
handler.postDelayed(this, 100);
}
}
}, 100);
Looper.loop();
active = false;
return "done";
}
And to make matters worse it often seems to hang in this loop, without returning.
How to solve such a situation?
Why don't use synchronization instead? It sounds like a concurrency issue. Why don't you make sure that if the first background task is running then the second background task is sleeping until the first one is finished.
Or ensure somehow, that if the user switches to Activity number 2, the background task from activity number 1 is cancelled.
Instead of the AsyncTask you can consider to use IntentService. Have a look at the Android Service concept. The IntentService class ensures that only one request will be processed at one time.
I found this answer very useful during implementing IntentService with Activity callback communication.
Database locking issues solved by wrapping it into a ContentProvider. Besides problems with a method being called again before the previous instance was finished, I had the issue of different methods running in different background threads clashing while trying to write to the database.
Officially designed to allow for sharing data between apps, it also works great for sharing data between threads in a single app. The ContentProvider will make sure that no locking issues occur.