onCreate(Bundle savedInstanceState){
// show dialog A if something is not correct
new Thread(){
public void run(){
if(something is wrong) {
runOnUIThread(new Runnable(){
public void run(){
showDialog(A);
}
});
}
}
}.start();
// show dialog B
showDialog(B);
}
I want to know
which dialog will be shown first, and is the order indeterminate? why?
if the order is indeterminate, how can i reproduce the case that A is shown before B?
Thanks!
Which dialog will be shown first is not defined and you should not rely on one occurring before the other as above. The thread scheduler is not identically deterministic in all situations.
You need to lock on a mutex (or any other locking device) to make sure one is shown before the other.
Your question about which dialog will show first is indeterminate. There are cases where the order will flip flop. But generally B would be shown first since 9/10 it will get to place it's event on the UI thread before your thread could detect there was a problem.
I'd suggest using AsyncTask to perform whatever mechanisms are needed to startup, then in the onPostExecute() allow your program to resume starting up so it can showDialog(B) for whatever it needs. That way if dialog A is showing you can stop the startup process there and not show b.
public class MyAsyncStartup extends AsyncTask<Integer,Integer,MyResult> {
MyActivity activity;
public MyResult handleBackground() {
if( somethingWentWrong ) return null;
}
public onPostExecute( MyResult result ) {
if( result == null ) {
showDialog(B);
} else {
activity.resumeStartupAndShowA();
}
}
}
I don't think it is possible that A is shown before B... this is because runOnUIThread adds the event TO THE END of the event queue. The code in that event (showing dialog A) is not going to get executed until after the onCreate() finishes (which means that dialog B gets shown first).
What cannot be guaranteed is the order between showing dialog B and calling runOnUIThread, but that doesn't matter. Here is a fragment from the official docs:
[runOnUIThread] Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
N/A
You can't show B until you know whether or not A will be shown. So you have to wait for the worker thread no matter what. Would it be possible to put showDialog(B) in your other thread like this?
onCreate(Bundle savedInstanceState){
// show dialog A if something is not correct
new Thread(){
public void run(){
runOnUiThread(new Runnable(){
public void run(){
if(something is wrong) {
showDialog(A);
}
showDialog(B);
}
});
}
}
}.start();
}
Related
I am writing an Android application that interfaces with the Motorola EMDK, and I am running into an issue with timing/threading. I have an activity that adds a fragment to perform a very specific function using the EMDK, displays a screen that tells the user what is happening and then is cleaned up by the activity after about 15 seconds.
I am noticing a 1-2 second delay between when the EMDK action occurs, in this case the device cradle is being unlocked, and when the GUI is displayed that says "The cradle is now unlocked."
I have done some research about how Android handles drawing to the screen for fragments, and everything I can find says that onResume is called "when the fragment becomes visible." This does not match my experience, however. According to how I understand the code below should work, the screen should be drawn and then the EMDKManager.getEMDKManager() method is called, which constructs a pointer to the EMDK service and creates a new thread to perform the unlock:
#Override
public void onResume() {
super.onResume();
EMDKManager.getEMDKManager(getActivity().getApplicationContext(), this);
}
It looks more like the screen is drawn to only once onResume() completes in entirety, ie EMDKManager.getEMDKManager() finishes its call as well.
As the fragment is the EMDKListener object that is required for the second parameter for the method, I am struggling finding a way to thread this correctly. I need the GUI to be drawn first or at the same time that the cradle unlock occurs.
Are there any other methods that can be overridden or interfaced with to get the equivalent to an onViewDrawn() event for the fragment?
Thank you very much.
All the lifecycle method onCreate(), onResume() onStop() etc. are called by the main thread, which is also responsible for drawing the UI.
By preforming a long operation in those method, you block the UI thread from handling touch input as well as drawing the app
you can start your long operation on another thread by doing so:
new Thread(new Runnable() {
#Override
public void run() {
// do long operations here
}
}.start();
note that if that operation wants to update the UI components it MUST be done on the UI thread, you can do so by passing a runnable to the activity
activity.runOnUiThread(new Runnable() {
public void run() {
// do UI updating but, do not block it here
}
});
(or you can create an handler if it's a Service or you want to delay those runnables)
Although I have huge concerns about memory use/leaks, I did this in order to get the timing right:
private EMDKManager.EMDKListener getThis() {
return this;
}
private Runnable initEMDK = new Runnable() {
#Override
public void run() {
EMDKManager.getEMDKManager(getActivity().getApplicationContext(), getThis());
}
};
#Override
public void onResume() {
super.onResume();
Log.v(LOGTAG, "Starting");
new Thread(initEMDK).start();
}
I feel like there is a standard way of doing the getThis() method. If you know it, I would love to know.
Thank you.
I am a beginner to Android and I have some confusions regarding Android UI Thread. Now, I know that no thread apart from the one that created the UI can modify it.
Great.
Here is the Activity from my first Android app which slightly confuses me.
public class NasaDailyImage extends Activity{
public ProgressDialog modalDialog = null;
//------------------------------------------------------------------------------
#Override
protected void onCreate(Bundle savedInstanceState){
//Instantiate progress dialog, skipping details.
Button b = //get reference to button
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
modalDialog.show(); // show modal
Toast.makeText(getApplicationContext(), "Getting feeds", 500).show();
new AsyncRetriever().execute(new IotdHandler()); // Get the feeds !!
}
});
}
//------------------------------------------------------------------------------
public synchronized void resetDisplay(boolean parseErrorOccured,
boolean imageErrorOccured,
IotdHandler newFeeds){
if(parseErrorOccured || imageErrorOccured){
// make a Toast
// do not update display
}else{
// make a Toast
// update display
// based on new feed
}
}
//------------------------------------------------------------------------------
class AsyncRetriever extends AsyncTask<IotdHandler,Void,IotdHandler>{
#Override
protected IotdHandler doInBackground(IotdHandler... arg0) {
IotdHandler handler = arg0[0];
handler.processFeed(); // get the RSS feed data !
return handler;
}
//------------------------------------------------------------------------------
#Override
protected void onPostExecute(IotdHandler fromInBackground){
resetDisplay( // call to update the display
fromInBackground.errorOccured,
fromInBackground.imageError,
fromInBackground);
}
//------------------------------------------------------------------------------
}
1. onCreate is on the UI thread so I can do whatever I want but onClick is not. Why can I make a ProgressDialog and a Toast in that method? Why no error there?
2. The AsyncTask is subclass of the the NasaDailyImage. This means it can access all the methods of NasaDailyImage including resetDisplay() which updates the display. resetDisplay() is called in the onPostExecute which runs on a different thread from UI. So, why can I update the display there and yet get no errors ?
onClick() is indeed on the UI thread. Most of what happens in an Activity happens on the UI thread.
onPostExecte() (and its counterpart onPreExecute()) runs on the UI thread as well. The AsyncTask.onPostExecte() documentation clearly states this. AsyncTask was deliberately designed such that developers could update the UI before and after they do background work.
In general, your code will be running on the UI thread unless you explicitly tell it otherwise. Once you create AsyncTasks, Runnables, or Threads, you need to ensure you understand where your code is executing. In an Activity, it is typically safe to assume you are on the UI thread.
You are extending AsyncTask class , where async task class is calling its sequential method automatically. First onPreExecute then doBackground and finally onPost. If you want to change any ui change you can use onProgressUpdate method.
To use your activity class simple call activityclass.this.resetDisplay(). Because inner class scope sometimes failed to integrate except global varible.
Thanks
I've encountered a very weird feature.
When I'm trying to run an animation on the main thread, it does not start.
When I run said animation using
getView().post(new Runnable() {
#Override
public void run() {
getView().startAnimation(a);
}
});
It does start.
I've printed the CurrentThread before starting the animation and both print main.
Obviously, I am missing something here, as both should start the animation on the main thread...
My guess is that as post adds the task to the queue, it starts at a more "correct time", but I would love to know what happens here at more depth.
EDIT:
Let me clear things up - my question is, why starting the animation on post causes it to start, when starting the animation on the main thread does not.
post :post causes the Runnable to be added to the message queue,
Runnable : Represents a command that can be executed. Often used to run code in a different Thread.
run () : Starts executing the active part of the class' code. This method is called when a thread is started that has been created with a class which implements Runnable.
getView().post(new Runnable() {
#Override
public void run() {
getView().startAnimation(a);
}
});
code : getView().startAnimation(a);
in your code,
post causes the Runnable (the code will be run a in different thread) to add the message queue.
So startAnimation will be fired in a new thread when it is fetched from the messageQueue
[EDIT 1]
Why do we use a new thread instead of UI thread (main thread)?
UI Thread :
When application is started, Ui Thread is created automatically
it is in charge of dispatching the events to the appropriate widgets
and this includes the drawing events.
It is also the thread you interact with Android widgets with
For instance, if you touch the a button on screen, the UI thread
dispatches the touch event to the widget which in turn sets its
pressed state and posts an invalidate request to the event queue. The
UI thread dequeues the request and notifies the widget to redraw
itself.
What happens if a user press a button which will do longOperation ?
((Button)findViewById(R.id.Button1)).setOnClickListener(
new OnClickListener() {
#Override
public void onClick(View v) {
final Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
});
The UI freezes. The program may even crash.
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
}).start();
}
It breaks the android rule that never update UI directly from worker thread
Android offers several ways to access the UI thread from other threads.
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
Like below,
View.post(Runnable)
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}
Handler
final Handler myHandler = new Handler(Looper.getMainLooper());
(new Thread(new Runnable() {
#Override
public void run() {
final Bitmap b = loadImageFromNetwork();
myHandler.post(new Runnable() {
#Override
public void run() {
mImageView.setImageBitmap(b);
}
});
}
})).start();
}
For more info
http://android-developers.blogspot.com/2009/05/painless-threading.html
http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/
Is this being done on onCreate or onCreateView? If so, the app might not be in a state where the View is attached to the window. A lot of algorithms based on View metrics may not work since things like the View's measurements and position may have not been calculated. Android animations typically require them to run through UI math
View.post actually queues the animation on the View's message loop, so once the view gets attached to the window, it executes the animation instead of having it execute manually.
You are actually running things on the UI thread, but at a different time
Have a look here for a good answer. view.post() is the same as handler.post() pretty much. It goes into the main thread queue and gets executed after the other pending tasks are finished. If you call activity.runOnUiThread() it will be called immediately on the UI thread.
The problem I think could be the life-cycle method where you are calling the post() method. Are you doing it in onCreate()? if so look at what I found in the activity's onResume() documentation:
onResume()
Added in API level 1 void onResume () Called after
onRestoreInstanceState(Bundle), onRestart(), or onPause(), for your
activity to start interacting with the user. This is a good place to
begin animations, open exclusive-access devices (such as the
camera), etc.
https://developer.android.com/reference/android/app/Activity.html#onResume()
So, as Joe Plante said, maybe the view is not ready to start animations at the moment you call post(), so try moving it to onResume().
PD: Actually if you do move the code to onResume() then I think you can remove the post() call since you are already in the ui-thread and the view should be ready to start animations.
I am now working on an android app in which I need to display a text after some processing is done.
I'm using a Thread to run a process in the back while my progress dialog is being displayed to the user. The Thread works properly and I've followed it step by step and, apparently, it also ends fine; however, the method in which I call it does not seem to come to an end (at least, during a normal cycle) because the text I am setting afterward does display immediately, I have to wait and do some other action (like in order for it to display
Below is the piece of code I'm having trouble with:
private OnClickListener saldoDisp = new OnClickListener(){
public void onClick(View v){
int x = s2.getSelectedItemPosition();
branchSel = arrSucsId[x];
mainProc();
saldoAdminTex.setText(strSaldo); //The late one
}
};
public void mainProc(){
chekP = new Thread (null,doProc,"Background");
chekP.start();
mProgress =ProgressDialog.show(SivetaAsaldo.this, "","Obteniendo saldo...",true, false);
}
private Runnable doProc = new Runnable(){
public void run(){
if(getSaldoAdmin(levelSel,branchSel))
{
mProgress.dismis();
Log.i(TAG,"Task completed properly");
}else
handler.post(tosti);
}
};
So I do get the "Task completed properly" but seems like it still waits for something else, any clues guys?
Thanks for taking a bit of your time to check it out =).
saldoAdminTex.setText(strSaldo); //The late one
is going to get called immediately. It doesn't wait until after the Thread started in mainProc ends. You also cannot dismiss the Progress Dialog in your runnable. You can only do UI related things on the main UI thread.
It would help you to read the article on Painless Threading on the Android Dev site.
About your ProgressDialog, please see this answer about how to use a AsyncTask with a ProgressDialog.
Looking at your code, this:
saldoAdminTex.setText(strSaldo);
would potentially be executed before your thread finishes as the thread will be running in parallel to that line.
An alternative way would be to do this:
public void mainProc(){
mProgress =ProgressDialog.show(SivetaAsaldo.this, "","Obteniendo saldo...",true,false);
handler.post(new Runable(){
public void run(){
if(getSaldoAdmin(levelSel,branchSel))
{
mProgress.dismis();
saldoAdminTex.setText(strSaldo);
Log.i(TAG,"Task completed properly");
}else
handler.post(tosti);
}
});
}
I have a problem with progress dialog on opening an activity (called activity 2 in example).
The activity 2 has a lot of code to execute in this OnCreate event.
final ProgressDialog myProgressDialog = ProgressDialog.show(MyApp.this,getString(R.string.lstAppWait), getString(R.string.lstAppLoading), true);
new Thread() {
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
showApps();
}
});
myProgressDialog.dismiss();
}
}.start();
The showApps function launch activity 2.
If I execute this code on my button click event on activity 1, I see the progress, but she doesn't move and afeter I have a black screen during 2 or 3 seconds the time for android to show the activity.
If I execute this code in the OnCreate of Activity2 and if I replace the showApps by the code on OnCreate, Activity1 freeze 2 seconds, I don't see the progress dialog, and freeze again 2 seconds on activity 2 before seeing the result.
I had the same issue and using an AsyncTask is working for me.
There are 3 important methods to override in AsyncTask.
doInBackground : this is where the meat of your background
processing will occur.
onPreExecute : show your ProgressDialog here ( showDialog )
onPostExecute : hide your ProgressDialog here ( removeDialog or dismissDialog
)
If you make your AsyncTask subclass as an inner class of your activity, then you can call the framework methods showDialog, dismissDialog, and removeDialog from within your AsyncActivity.
Here's a sample implementation of AsyncTask:
class LoginProgressTask extends AsyncTask<String, Integer, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
try {
Thread.sleep(4000); // Do your real work here
} catch (InterruptedException e) {
e.printStackTrace();
}
return Boolean.TRUE; // Return your real result here
}
#Override
protected void onPreExecute() {
showDialog(AUTHORIZING_DIALOG);
}
#Override
protected void onPostExecute(Boolean result) {
// result is the value returned from doInBackground
removeDialog(AUTHORIZING_DIALOG);
Intent i = new Intent(HelloAndroid.this, LandingActivity.class);
startActivity(i);
}
}
AFAIK you cannot preload any activity with progress dialog displayed. Are you testing on a real device or in emulator?
I've seen workarounds that opened an activity with a ViewFlipper having a progress animation in the center, and in the next View, it was loaded an activity, but it's not something is recommended and hard to implement to work as you wish.
GeeXor
I would suggest you to avoid performing lots of operations in Activity 2's OnCreate.Writing lots of operations in OnCreate is a reason for the black screen between activities.So perform those operations asynchronously using AsyncTask or in a Thread (or write them in onStart if they are unavoidable).
The other suggestion is to start another progressDialog in activity 2's onCreate which will run until all of your data is loaded & user will know that something is happening in background.
this is what i would do. create a handler on the ui thread, start the background processing thread and then show the progressdialog. when the background thread has finished it's work get it to post a runnable on the ui thread via the handler to dismiss the dialog.