I have a splash screen that I'm trying to run some AsyncTasks on. It appears the AsyncTasks are running before it shows the splash screen. I need the splash screen to show (logo and each process shows a different message) and the start running the asynctasks. I'm using books in the blow code only as an example of what I have:
SplashScreen.java:
// Splash screen timer
private static int SPLASH_TIME_OUT = 3000;
TextView statusMessage;
BookClubDB bookClubDB = new BookClubDB(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
statusMessage = (TextView) findViewById(R.id.statusMessage);
statusMessage.setText("Checking For Connection");
if(isNetworkAvailable()){
statusMessage.setText("Connection Found");
String appUpdateDate = null;
appUpdateDate = lastUpdate();
update1(appUpdateDate);
update2(appUpdateDate);
update3(appUpdateDate);
update4(appUpdateDate);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
endSplash();
}
}, SPLASH_TIME_OUT);
}
else {
statusMessage.setText("No Connection Found");
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
endSplash();
}
}, SPLASH_TIME_OUT);
}
}
public void endSplash(){
Intent i = new Intent(SplashScreen.this, MainActivity.class);
startActivity(i);
// close this activity
finish();
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
private String lastUpdate(){
String updateDate = null;
updateDate = bookClubDB.getUpdateDate();
return updateDate;
}
private void update1(String appUpdateDate){
statusMessage.setText("Updating Regions");
bookClubDB.getAPI1(appUpdateDate);
}
private void update2(String appUpdateDate){
statusMessage.setText("Updating Vehicles");
bookClubDB.getAPI2(appUpdateDate);
}
private void update3(String appUpdateDate){
statusMessage.setText("Updating Products");
bookClubDB.getAPI3(appUpdateDate);
}
private void update4(String appUpdateDate){
statusMessage.setText("Updating Documents, Images, and Video");
bookClubDB.getAPI4(appUpdateDate);
}
The functions being called (updated1...4) call another function (getAPI1...4) in a file called BookClubDB.java. The second set is what runs the asynctasts in a file named JASONParser.java. The functions and parser work. Only the asynctasks run before the splash screen shows.
I want the splash screen to pop up with "checking for connection" -> "connection found" -> each updated from the functions as they are ran.
The JSONParser.java is a simple json parser that returns the json results back to the BookClubDB.java:
JSONObject output = new JSONParser().execute(new String[] {bookClubURL,consumerKey,sharedSecret}).get();
The function takes the json, converts it into a jsonArray and I populate a database with it. Again everything is working as needed, only the asynctasks are running too soon.
How can I start the thread for the splash screen before the asynctask thread? I've looked at other SO answers but they never really answered it for me.
You are running all of your code in the OnCreate method of your activity. The UI is not yet visible at this point.
Check out the activity lifecycle in the Android docs and rewrite according to that.
I guess this is happening because you are firing asynctasks in activity's onCreate, when the activity is still being created.
If I understood correctly, you want them to be triggered only after the splash screen was displayed.
onResume is a good callback for that.
Moreover, notice that you are firing all the updatexx methods at the same time, and the status message will be overridden every time.
It is probably because you are setting the message text on main thread.
If you run some changes on main thread too quickly, they don't have to be performed or may result in lags in the UI.
Basically your app is doing something like this:
-> OnCreate
-> setText("Checking For Connection")
-> setText("OnConnectionFound")
-> setText("Updating Regions")
-> schedule new AsyncTask to update regions
-> setText("Updating Vehicles")
-> schedule new AsyncTask to update vehicles
..... update3,4......
and only now the system starts to execute the async tasks.
(It does not have to be this strict, especially on multicore processors. It is possible that f.e. the regions asyncTask starts before the update3 setText, but it certainly does not finish before that)
But there is one thing you need to realize: all these calls are quite fast, probably no more then few miliseconds. And the UI is not updated that often (nor would you notice it).
Therefore the text updates too quickly/does not update at all.
You mentioned you are using AsyncTask, that's good. You will probably just have to place your setText calls into onPreExecute/onPostExecute/onProgressChanged methods (based on when you want the text to be changed) and it will work fine. These methods are designed to be called on UI thread in the right time, therefore ensure all your UI actions will be rendered :)
Related
Scenario:
The user has a list of items, let's say 10 items. Each item has an Operation button, which calls an AsyncTask which makes a web call. When a call is made, the item displays a spinner during the execution of the task
Problem:
Some of the users abuse this, and press quickly more Operation buttons, quicklt one after another, executing the web calls too often. So I want to be able to somehow, execute each of the AsyncTasks one after another with a delay of 2 seconds between executions. I do not want to switch to something else from AsyncTask if possible. So basically if there are 3 Operation buttons pressed, the execution should be:
-> Operation 1
-> 2 seconds delay
-> Operation 2
-> 2 seconds delay
-> Operation 3
-> ....
What would be the best way to do this in Android?
LE:
I have just realized something, for executing my task I ran the following code:
myTask = new MyTask();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
myTask.execute();
}
Well, I've been using this code for a lot of time now, knowing that after honeycomb the tasks were not executed in parallel anymore without using an Executor. So it seems that only doing a simple myTask.execute() and adding a Thread.sleep() makes my AsyncTasks execute, one after another just as expected.
You will need to maintain a list of the operations that needs to be performed.
on click of the button add the task in the list, call a method which check the list for the task and executes it if there is no other task is running..
in onPostExecute method call the same method to check if there is any other task / operation that needs to be performed..
It may not be the full code you require... but may give you some idea..
public class TestActivity extends AppCompatActivity {
private static boolean isTaskRunning =false;
static ArrayList<CustomTask> customTaskList = new ArrayList();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
public void onBtnClick(View view)
{
// create custom task with required values and actions
CustomTask customTask = new CustomTask();
customTaskList.add(customTask);
checkAndExecuteTask();
}
private static void checkAndExecuteTask()
{
//checks if there is any task in the list and is there any other running task
if(customTaskList.size()>0 && !isTaskRunning) {
new MyAsync(customTaskList.get(0)).execute();
}
}
static class MyAsync extends AsyncTask<Void,Void,Void>
{
CustomTask currentCustomTask;
public MyAsync(CustomTask customTask)
{
currentCustomTask = customTask;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
isTaskRunning= true;
}
#Override
protected Void doInBackground(Void... voids) {
// do your stuff
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
customTaskList.remove(currentCustomTask);
isTaskRunning =false;
checkAndExecuteTask(); // task is completed so check for another task and execute (if any).
}
}
class CustomTask
{
// create class with required fields and method
}
}
There are a number of ways you can do this in android.
One way is to use a handler.
What you need to do is to, create a seperate thread and run handler.postDelayed in it.
private void startWebCall() {
Thread thread = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Do your web calls here
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, 2000);
Looper.loop();
}
};
thread.start();
}
You should call above method whenever user clicks a item.
Another way that I can think of is using an IntentService
An IntentService is a service that is used for doing asynchronous tasks in background. It maintains a queue of the tasks it needs to do. It is different from the above approach in the sense that it executes these tasks in a sequential order. So when you make requests to it to make web calls it will queue them, make the first call and then after it finishes it will make the second call. So the different web calls will not execute in parallel. They will execute in a sequential order but in a different thread. Also it is a service so it can run even in the background, i.e if user closes the app.
This is a good tutorial to get start with IntentService.
AsyncTaks should be generally avoided unless the work one needs to do is quite trivial. This blog explains its pitfalls.
Under some conditions, when my app starts, it displays an AlertDialog. However, the alert never gets displayed. I discovered that if I add a delay, it works (i.e. gets displayed).
More specifically: on app startup, it executes the main activity onCreate() which under a certain condition starts a 2nd activity. In the 2nd activity, through a separate thread, it makes a check for some web server status. If the Android device doesn't have Internet connectivity, HttpURLConnection returns an error instantly and my enclosing function executes a callback to the 2nd activity. My code then uses post() to attempt to display an alert to the user (using post allows displaying the alert on the UI thread, which is required).
Apparently it tries to display the alert before any of the either activity's UI has been created. If I use postDelayed() in the 2nd activity, the problem still persists. However, if I use the following block of code in the main activity, the alert shows properly:
new Handler().postDelayed (new Runnable ()
{
#Override public void run()
{
Intent intent = new Intent (app, MyClass.class);
app.startActivityForResult (intent, requestCode);
}
}, 3000);
My solution is a hack that happens to work at the moment. I don't mind having a little delay on start-up for this particular situation but I don't want a delay that's longer than necessary or one that may sometimes fail.
What is the proper solution?
Ok, here's a workaround. First, I'll speculate that the problem is that the attempt to display the alert is happening before the looper for the UI thread has been started. Just a speculation.
To work around the problem I added a recursive post which gets called from onResume(), like this:
private boolean paused = true;
#Override public void onResume ()
{
super.onResume();
paused = false;
checkForAlert();
}
#Override public void onPause ()
{
super.onPause();
paused = true;
}
And here's the function that does the post:
private AlertInfo alertInfo = null;
private void checkForAlert()
{
if (alertInfo != null)
{
...code to build alert goes here...
alertInfo = null;
}
if (!paused)
contentView.postDelayed (new Runnable()
{
#Override public void run() { checkForAlert(); }
}, 200);
}
AlertInfo is a simple class where the thread needing the alert can put the relevant info, e.g. title, message.
So, how does this work? checkForAlert() gets called during onResume() and will continue to get called every 200ms until "paused" is false, which happens in onPause(). It's guaranteed to be recurring whenever the activity is displayed. The alert will get built and displayed if alertInfo is not null. In the secondary thread, I simply create an AlertInfo instance and then within 200ms the alert gets displayed. 200ms is short enough that most people won't notice the delay. It could be shorter but then battery use goes up.
Why did I start checkForAlert() in onResume instead of onCreate()? Simply because there's no need for it to run unless the activity is currently "on top". This also helps with battery life.
I've been writing android apps for some months now, and I'm at the point where I'm building an actual needed app.
As I want that to work nice and fast, I made a Workerthread to do all kinds of tasks in the background while the UI can...build up and work and stuff.
It's based on the Android Studio Drawer app blueprint.
In Main.onCreate I got my operator=new Operator(), which extends Thread.
Now, when loading a new Fragment, it sometimes calls MainActivity.operator.someMethod() (I made operator static so I can use it from anywhere), and after some time I realized, the only tasks actually running in background are those in the operators run() method and an Asynctask my login Fragment runs. Everything else the UI waits for to complete and therefore gets executed by the UI thread.
So I thought: no problem! My operator gets a handler which is built in run(), and I change those tasks:
public void run() {
Looper.prepare(); //Android crashed and said I had to call this
OpHandler = new Handler();
LoadLoginData();
[...Load up some Arrays with hardcoded stuff and compute for later use...]
}
public void LoadLoginData() {
OpHandler.post(LoadLoginDataRunnable);
}
private Runnable LoadLoginDataRunnable = new Runnable() {
#Override
public void run() {
if(sharedPreferences==null)
sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
sessionID=sharedPreferences.getString("sessionID", null);
if(sessionID!=null) {
postenID = sharedPreferences.getString("postenID", PID_STANDARD);
postenName = sharedPreferences.getString("postenName", PID_STANDARD);
context.QuickToast(sessionID, postenName, postenID);
}
}
};
context is my MainActivity, I gave the operator a reference so I could send Toasts for Debugging.
But now, the Runnables seem to not run or complete, any Log.e or Log.d stuff doesn't arrive in the console.
After some googeling and stackoverflowing, everyone is just always explaining what the difference is between Handlers, Asynctask, and Threads. And the multitask examples always only show something like new Thread(new Runnable{run(task1)}).start times 3 with different tasks.
And so became my big question:
How to correctly, over a longer time (~lifecycle of the MainActivity), with different tasks, use a background thread?
Edit: to clarify, I would also like a direct solution to my special problem.
Edit 2: after reading nikis comment (thank you), the simple answer seems to be "use HandlerThread instead of thread". Will try that as soon as I get home.
Trying a HandlerThread now. It seems my OpHandler, initialized in run(), gets destroyed or something after run() has finished, not sure whats up here (this is btw another mystery of the kind I hoped would get answered here). I get a NullpointerException as soon as I try to use it after run() has finished.
Make your worker thread own a queue of tasks. In the run() method, just pop a task from the queue and execute it. If the queue is empty, wait for it to fill.
class Operator extends Thread
{
private Deque<Runnable> tasks;
private boolean hasToStop=false;
void run()
{
boolean stop=false;
while(!stop)
{
sychronized(this)
{
stop=hasToStop;
}
Runnable task=null;
synchronized(tasks)
{
if(!tasks.isEmpty())
task=tasks.poll();
}
if(task!=null)
task.run();
}
}
void addTask(Runnable task)
{
synchronized(tasks)
{
tasks.add(task);
}
}
public synchronized void stop()
{
hasToStop=true;
}
}
I have a download process that runs in the background, and updates the UI with progress (a ListView adapter). It works fine until I leave the activity and come back. After loading the activity again there is a "new" ListView object that is not the same one that is bound to the BG download process. How can I structure my code so that the background process can always talk to the ListView in my activity?
The specific line that does this is:
adapter.notifyDataSetChanged();
Here is the shell of the Download class:
public class Download
{
}
protected void start()
{
TransferManager tx = new TransferManager(credentials);
this.download = tx.download(s3_bucket, s3_dir + arr_videos.get(position), new_video_file);
download.addProgressListener(new ProgressListener()
{
public void progressChanged(final ProgressEvent pe)
{
handler.post( new Runnable()
{
#Override
public void run()
{
if ( pe.getEventCode() == ProgressEvent.COMPLETED_EVENT_CODE )
{
Download.this.onComplete();
}
else
{
Download.this.onProgressUpdate();
}
}
});
}
});
}
protected void onProgressUpdate()
{
this.download_status = "downloading";
Double progress = this.download.getProgress().getPercentTransfered();
Integer percent = progress.intValue();
//Log.v("runnable", percent + "");
downloaded_data.edit().putInt(position+"", percent).commit();
adapter.notifyDataSetChanged();
}
}
The short answer is simply "no". There's no simple way to find/keep the reference to a ListView in a destroyed/recreated Activity.
One way you can get around this is to use BroadcastReceiver. You can broadcast progress intents, and have the Activity register/deregister from those intents in onCreate() and onPause().
Another (arguably easier) hack you can do it is to persist the state (along the lines of what you're doing with downloaded_data.edit()), and have a thread in your Activity that regularly polls this state and updates the ListView accordingly.
You can save data of listview in file, then in function onCreate callback to take it. Using File may be a solution. Once your Activity is destroyed, all datas are lost
Make tasks detachable, like an Executor service placed in a component that is always there between Activity changes:
Use a Service: clients can connect to it and request what tasks are running etc.
Implement Application class and let it hold references to tasks that are running, exposed via a static field.
I need my Android app to periodically fetch data from a server using AJAX calls, and update the UI accordingly (just a bunch of TextViews that need to be updated with setText()). Note that this involves 2 tasks:
Making an AJAX call, and updating the UI once I receive a response - I use a simple AsyncTask for this.
Doing the above repeatedly, at regular intervals.
I haven't figured out an elegant way to achieve Point 2 above. Currently, I am simply executing the task itself from OnPostExecute(). I read on this thread at SO that I need not worry about garbage collection as far as the AsyncTask objects are concerned.
But I'm still unsure as to how I set up a timer that will fire my AsyncTask after it expires. Any pointers will be appreciated. Here is my code:
public class MyActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new AjaxRequestTask().execute(MY_REST_API_URL);
}
private void updateReadings(String newReadings) {
//Update the UI
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX Request
}
#Override
protected void onPostExecute(String result) {
updateReadings(result);
/*Is there a more elegant way to achieve this than create a new AsyncTask object every 10 seconds? Also, How can I update the UI if I create a timer here? */
new AjaxRequestTask().execute(MY_REST_API_URL);
}
}
}
Thanks in advance
EDIT:
I tried posting an answer but couldn't do it since I don't have the reputation to answer within 8 hours.
Well, so I found a solution. I'm not convinced however.
protected void onPostExecute(String result) {
updateReadings(result);
// super.onPostExecute(result);
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
new AjaxRequestTask().execute(MY_REST_API_URL);
}
},
TIMER_ONE_TIME_EXECUTION_DELAY
);
}
Are there any flip sides that I should be aware of when I use this? In particular, I am seeing lots of GCs happening in the LogCat. Also, I am wondering how an AsyncTask can be candidate for GC unless the onPostExecute() completes?
How can I "stop" the updates? One way I thought of was to make the very first AsyncTask instance as a member variable of the Activity. That way, I can invoke cancel(true) on it and hope that this will "stop" the tasks.
SOLUTION:
In case anyone is looking for something similar - none of the solutions I mentioned here work satisfactorily. They all suffer from OutOfMemory issues. I did not debug into the details of the OOM, but I suspect it could either be because of the recursion, or because of having HTTP-related objects as member variables in the AsyncTask rather than as members of the Activity (basically because of NOT reusing HTTP and other objects).
I discarded this approach for a different one - making my Ajax Calls endlessly in the doInBackground() of my AsyncTask; and updating the UI in onProgressUpdate(). That way I also avoid the overhead of maintaining too many threads or Handlers for updating the UI (remember UI can be updated in onProgressUpdate() ).
This approach also eliminates the need for Timers and TimerTasks, favoring the use of Thread.sleep() instead. This thread on SO has more details and a code snippet too.
Call postDelayed() on any View to schedule a hunk of code to be run on the main application thread after a certain delay. Do this in onPostExecute() of the AsyncTask to create and execute another AsyncTask.
You could use AlarmManager, as others have cited, but I would agree with you that it feels a bit like overkill for timing that occurs purely within an activity.
That being said, if the AJAX calls should be occurring regardless of whether the activity exists, definitely consider switching to AlarmManager and an IntentService.
I think the android way to do this is using AlarmManager. Or you can user a basic java Timer as well. I'd recommend AlarmManager.
Set it up to send some intent with a custom Action, and register a broadcastreceiver for it.
If the ajax calls are only executed in the activity you can just use a timer in the activity which starts the tasks.
Otherwise use a service which uses the AlarmManager and which connects to the gui via a broadcast.
The recommended way to do a repeated task, is via AlarmManager, as alluded to by Scythe. Basically it involves setting up a broadcast listener, and having AlarmManager fire off an intent to that listener at whatever interval you choose. You then would have your broadcast listener call out to the activity to run the AsyncTask. If you need a very tight timer (less than 5s calls I'd say), then you're better off using a Timer within a Service, and using AIDL to call back to the activity.
Instead of talking directly from the broadcast intent, you could also setup an IntentService which you can poke, and use AIDL to update the activity.
This is how I achieved it finally. Note that the AsyncTask cancel(true) method is useless in my scenario because of the recursion. I used what #CommonsWare suggested - used a flag to indicate whether any more tasks should be executed.
public class MyActivity extends Activity {
/*Flag which indicates whether the execution should be halted or not.*/
private boolean mCancelFlag = false;
private AjaxRequestTask mAjaxTask;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if(mAjaxTask == null){
mAjaxTask = new AjaxRequestTask();
}
mAjaxTask.execute(MY_REST_API_URL);
}
#Override
protected void onResume() {
super.onResume();
mCancelFlag = false; /*when we resume, we want the tasks to restart. Unset cancel flag*/
/* If the main task is Finished, create a new task and execute it.*/
if(mAjaxTask == null || mAjaxTask.getStatus().equals(AsyncTask.Status.FINISHED)){
new AjaxRequestTask().execute(TLS_REST_API_URL);
}
}
#Override
protected void onPause() {
mCancelFlag = true; /*We want the execution to stop on pause. Set the cancel flag to true*/
super.onPause();
}
#Override
protected void onDestroy() {
mCancelFlag = true;/*We want the execution to stop on destroy. Set the cancel flag to true*/
super.onDestroy();
}
private void updateReadings(String result) {
//Update the UI using the new readings.
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
private AjaxRequestTask mChainAjaxRequest;
private Timer mTimer;
private TimerTask mTimerTask;
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX call and get the response
return ajaxResponse;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "Updating readings");
updateReadings(result);
// super.onPostExecute(result);
if(mTimer == null){
mTimer = new Timer();
}
if(!mCancelFlag){/*Check if the task has been cancelled prior to creating a new TimerTask*/
if(mTimerTask == null){
mTimerTask = new TimerTask() {
#Override
public void run() {
if(!mCancelFlag){/*One additional level of checking*/
if(mChainAjaxRequest == null){
mChainAjaxRequest = new AjaxRequestTask();
}
mChainAjaxRequest.execute(MY_REST_API_URL);
}
}
};
}
mTimer.schedule(mTimerTask,TIMER_ONE_TIME_EXECUTION_DELAY);
}
}
}
}