forceLayout(), requestLayout() - android

My reading of the android documentation finds the methods forceLayout() (which is to produce a layout display at the next layout request) and requestLayout() (which is supposed to post an immediate layout request), but I can not get them to behave as advertised. In particular, if I do one set text before a Thread.Sleep and one after, it waits for the Sleep to finish before setting both texts at once, whether or I call the forceLayout() and requestLayout() in between. Please do not respond with a lot of nonsense about how I should not call a Thread.Sleep in the UI thread. If I wrap the Thread.Sleep in a CountDownTimer it works perfectly well (as long as I have the tick time short enough to not interfere with sleep time, and the duration of the timer long enough to permit the Sleep to finish. The following is an example:
int i=0;
TextView tv2;
TextView tv1;
LinearLayout ll;
Button bt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ll=new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
tv1=new TextView(this);
tv2=new TextView(this);
bt=new Button(this);
bt.setText("Press to start");
ll.addView(bt);
ll.addView(tv1);
ll.addView(tv2);
tv2.setText("");
setContentView(ll);
bt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tv1.setText("starting sleep");
new CountDownTimer(6000,50){
public void onTick(long msuf)
{if(i==1)
{
try{
Thread.sleep(4000);
tv2.setText("waking up");
}
catch(InterruptedException e){};
}
i++;
}
public void onFinish(){}}.start();
}
});
}

[lots of nonsense about calling sleep() in UI thread]. If i get it right, you mean having something like:
//...inside onTick()
try {
tv2.setText("almost waking up"); // first setText()
Thread.sleep(4000);
tv2.setText("waking up"); // second seText()
}
If you make your main thread sleep, it will just stop processing anything: the current method, the thread loop and the message queue. Once awake again, it will finish executing the method, with the second setText() overriding the first one, and then leave the thread loop continue and do the UI refresh, showing only the second text.
Not requestLayout() nor forceLayout() can actually make the UI refresh immediately, they will both schedule a layout request in the thread loop. I'm not sure, but I think the difference between them is that requestLayout() is called by a view that has changed its size/position in its parent, and forceLayout() is called by a ViewGroup that needs its children to be re-laid out.
Therefore [more nonsense about calling sleep() in UI thread]. For such things calling postDelayed() on a main thread handler is the best solution probably, if you don't want to mess with multithreading.

Related

Modifying views in AsyncTask doInBackground() does not (always) throw exception

I just came across some unexpected behaviour when playing around with some sample code.
As "everybody knows" you cannot modify UI elements from another thread, e.g. the doInBackground() of an AsyncTask.
For example:
public class MainActivity extends Activity {
private TextView tv;
public class MyAsyncTask extends AsyncTask<TextView, Void, Void> {
#Override
protected Void doInBackground(TextView... params) {
params[0].setText("Boom!");
return null;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
tv = new TextView(this);
tv.setText("Hello world!");
Button button = new Button(this);
button.setText("Click!");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new MyAsyncTask().execute(tv);
}
});
layout.addView(tv);
layout.addView(button);
setContentView(layout);
}
}
If you run this, and click the button, you're app will stop as expected and you'll find the following stack trace in logcat:
11:21:36.630: E/AndroidRuntime(23922): FATAL EXCEPTION: AsyncTask #1
...
11:21:36.630: E/AndroidRuntime(23922): java.lang.RuntimeException: An error occured while executing doInBackground()
...
11:21:36.630: E/AndroidRuntime(23922): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
11:21:36.630: E/AndroidRuntime(23922): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
So far so good.
Now I changed the onCreate() to execute the AsyncTask immediately, and not wait for the button click.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// same as above...
new MyAsyncTask().execute(tv);
}
The app doesn't close, nothing in the logs, TextView now displays "Boom!" on the screen. Wow. Wasn't expecting that.
Maybe too early in the Activity lifecycle? Let's move the execute to onResume().
#Override
protected void onResume() {
super.onResume();
new MyAsyncTask().execute(tv);
}
Same behaviour as above.
Ok, let's stick it on a Handler.
#Override
protected void onResume() {
super.onResume();
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
new MyAsyncTask().execute(tv);
}
});
}
Same behaviour again. I'm running out of ideas and try postDelayed() with a 1 second delay:
#Override
protected void onResume() {
super.onResume();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
new MyAsyncTask().execute(tv);
}
}, 1000);
}
Finally! The expected exception:
11:21:36.630: E/AndroidRuntime(23922): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Wow, this is timing related?
I try different delays and it appears that for this particular test run, on this particular device (Nexus 4, running 5.1) the magic number is 60ms, i.e. sometimes is throws the exception, sometimes it updates the TextView as if nothing had happened.
I'm assuming this happens when the view hierarchy has not been fully created at the point where it is modified by the AsyncTask. Is this correct? Is there a better explanation for it? Is there a callback on Activity that can be used to make sure the view hierachy has been fully created? Timing related issues are scary.
I found a similar question here Altering UI thread's Views in AsyncTask in doInBackground, CalledFromWrongThreadException not always thrown but there is no explanation.
Update:
Due to a request in comments and a proposed answer, I have added some debug logging to ascertain the chain of events...
public class MainActivity extends Activity {
private TextView tv;
public class MyAsyncTask extends AsyncTask<TextView, Void, Void> {
#Override
protected Void doInBackground(TextView... params) {
Log.d("MyAsyncTask", "before setText");
params[0].setText("Boom!");
Log.d("MyAsyncTask", "after setText");
return null;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
tv = new TextView(this);
tv.setText("Hello world!");
layout.addView(tv);
Log.d("MainActivity", "before setContentView");
setContentView(layout);
Log.d("MainActivity", "after setContentView, before execute");
new MyAsyncTask().execute(tv);
Log.d("MainActivity", "after execute");
}
}
Output:
10:01:33.126: D/MainActivity(18386): before setContentView
10:01:33.137: D/MainActivity(18386): after setContentView, before execute
10:01:33.148: D/MainActivity(18386): after execute
10:01:33.153: D/MyAsyncTask(18386): before setText
10:01:33.153: D/MyAsyncTask(18386): after setText
Everything as expected, nothing unusual here, setContentView() completed before execute() is called, which in turn completes before setText() is called from doInBackground(). So that's not it.
Update:
Another example:
public class MainActivity extends Activity {
private LinearLayout layout;
private TextView tv;
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
tv.setText("Boom!");
return null;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = new LinearLayout(this);
Button button = new Button(this);
button.setText("Click!");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tv = new TextView(MainActivity5.this);
tv.setText("Hello world!");
layout.addView(tv);
new MyAsyncTask().execute();
}
});
layout.addView(button);
setContentView(layout);
}
}
This time, I'm adding the TextView in the onClick() of the Button immediately before calling execute() on the AsyncTask. At this stage the initial Layout (without the TextView) has been displayed properly (i.e. I can see the button and click it). Again, no exception thrown.
And the counter example, if I add Thread.sleep(100); into the execute() before setText() in doInBackground() the usual exception is thrown.
One other thing I have just noticed now is, that just before the exception is thrown, the text of the TextView is actually updated and it displays properly, for just a split second, until the app closes automatically.
I guess something must be happening (asynchronously, i.e. detached from any lifecycle methods/callbacks) to my TextView that somehow "attaches" it to ViewRootImpl, which makes the latter throw the exception. Does anybody have an explanation or pointers to further documentation about what that "something" is?
The checkThread() method of ViewRootImpl.java is responsible for throwing this exception.
This check is suppressed using member mHandlingLayoutInLayoutRequest until performLayout() i.e all the initial drawing traversals are complete.
hence it throws exception only if we use delay.
Not sure if this is a bug in android or intentional :)
Based on RocketRandom's answer I've done some more digging and came up with a more comprehensive answer, which I feel is warranted here.
Responsible for the eventual exception is indeed ViewRootImpl.checkThread() which is called when performLayout() is called. performLayout() travels up the view hierarchy until it eventually ends up in ViewRootImpl, but it originates in TextView.checkForRelayout(), which is called by setText(). So far so good. So why does the exception sometimes not get thrown when we call setText()?
TextView.checkForRelayout() is only called if the TextView already has a Layout (mLayout != null). (This check is what inhibits the exception from being thrown in this case, not mHandlingLayoutInLayoutRequest in ViewRootImpl.)
So, again, why does the TextView sometimes not have a Layout? Or better, since obviously it starts out not having one, when and where does it get it from?
When the TextView is initially added to the LinearLayout using layout.addView(tv);, again, a chain of requestLayout() is called, travelling up the View hierarchy, ending up in ViewRootImpl, where this time, no exception is thrown, because we're still on the UI thread. Here, ViewRootImpl then calls scheduleTraversals().
The important part here is that this posts a callback/Runnable onto the Choreographer message queues, which is processed "asynchronously" to the main flow of execution:
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
The Choreographer will eventually process this using a Handler and run whatever Runnable ViewRootImpl has posted here, which will eventually call performTraversals(), measureHierarchy(), and performMeasure() (on ViewRootImpl), which will perform a further series of View.measure(), onMeasure() calls (and a few others), travelling down the View hierarchy until it finally reaches our TextView.onMeasure(), which calls makeNewLayout(), which calls makeSingleLayout(), which finally sets our mLayout member variable:
mLayout = makeSingleLayout(wantWidth, boring, ellipsisWidth, alignment, shouldEllipsize,
effectiveEllipsize, effectiveEllipsize == mEllipsize);
After this happens, mLayout isn't null any more, and any attempt to modify the TextView, i.e. calling setText() as in our example, will lead to the well known CalledFromWrongThreadException.
So what we have here is a nice little race condition, if our AsyncTask can get its hands on the TextView before the Choreographer traversals are complete, it can modify it without penalties. Of course this is still bad practice, and shouldn't be done (there are many other SO posts dealing with this), but if this is done accidentally or unknowingly, the CalledFromWrongThreadException is not a perfect protection.
This contrived example uses a TextView and the details may vary for other views, but the general principle remains the same. It remains to be seen if some other View implementation (perhaps a custom one) that doesn't call requestLayout() in every case may be modified without penalties, which might lead to bigger (hidden) issues.
You can write in doInBackground to a TextView if it is not part of the GUI yet.
It is only part of the GUI after statement setContentView(layout);.
Just my thought.

Android postDelayed does not delay

I have the Problem that my Android app does not delay a second (or 10 seconds), if I use the postDelayed method..
Basically I would like my program to wait one second after I clicked the button, then update the text on my textview ("READY"), wait another 2 seconds, then update the textview again ("SET") and then it should start another activity (not yet implemented :-) ).
With my code, the programm starts and after I click the button the textview shows the last text ("SET") immediately.. It just does not wait.
What am i doing wrong?
Here is my code:
public class MyCounterActivity extends Activity {
private long mInternval = 100000;
private Handler mHandler;
private Runnable mStatusChecker = new Runnable() {
#Override
public void run() {
//updateInterval(); //change interval
startRepeatingTask();
}
};
void startRepeatingTask(){
mHandler.postDelayed(mStatusChecker, mInternval);
//mStatusChecker.run();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gym_counter);
final TextView tv1 = (TextView) findViewById(R.id.fullscreen_content);
final Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final long up;
EditText textUp = (EditText) findViewById(R.id.editTextUp);
up = Integer.parseInt(textUp.getText().toString());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//
}
},1000);
Log.d("after 1 runnable", "whaaat");
tv1.setText("Ready");
handler.postDelayed(new Runnable() {
#Override
public void run() {
//
}
}, 2000);
Log.d("after 2nd runnable", "whaaat 2");
//startRepeatingTask();
tv1.setText("SET");
}
});
}
I also tried to run it with the runOnUiThread() (within the onClick(View v) but with with the same result). I expected it to wait 1 second (startRepeatingTask()) and then runs the loop and waits several seconds...
runOnUiThread(new Runnable() {
#Override
public void run() {
startRepeatingTask();
for (int u = 0; u < up; u++){
startRepeatingTask();
}
}
}
});
Hope my description makes sense :-).
Thank you for your help!
EDIT:
I was now able to find a solution for my first problem. The answer from #mad in this post helpded me: How to start a different activity with some delay after pressing a button in android?
(Thats probably the same thing that #laalto tried to tell me. Thanks for the hint!)
In the onClick()
tv1.setText("READY");
mHandler.postDelayed(mDelay1, 2000);
And then the Runnable
private Runnable mDelay1 = new Runnable() {
#Override
public void run() {
if (tv1.getText()=="READY")
tv1.setText("SET");
}
};
BUT:
If i want to refresh the text on my Textview after every second, how do i do that? I cant just call mHandler.postDelayed() several times.. Any help is appreciated.
When you call postDelayed(), it just places the Runnable in a queue and returns immediately. It does not wait for the runnable to be executed.
If you need something to happen after a delay, put the code in the run() method of the runnable.
Whenever you call something like Thread.start(), handler.postDelayed, view.postDelayed, AsynchTask, TimerTask .. you enter the world of threading or you might call it parallel computing.
So there can be multiple threads ("codes") running at the same time.
When you are inside your Activity it is running in a Thread that is calld UI-thread or main thread. All graphics is handled in that thread and that thread alone.
Do NEVER wait in the UI-thread!
Example: you have a button that switches color from say gray to yellow on pressing it. Now you enter a Thread.sleep(10000); - waiting 10 seconds at the start of your onClick.
You will then see that the button stays yellow (=pressed) for 10 seconds even if you only pressed very shortly. Also: if you overdo it android os will become angry and post the user if he wants to force-close your app.
So what happens on handler.postDelayed?
Android will very quickly open a thread that runs in the background parallel to your UI thread. So in some nanoseconds it has done that and will execute the next command in UI thread (in the example above it is Log.d). In the background it will wait and count the millis until time is up. Then any code that is inside the runnable.run method will again be executed in the ui-thread after the wait.
Note also: postDelayed will not be super precise with the wait time as usually the ui-thread is quite buisy and when the wait time is up it may have something else to do. Your runnable code will be added to a queue and executed when ui-thread is ready again. All this happens without you having anything to do about it.
Also:
Remember to work with try/catch inside the runnable.run as many things can happen while waiting - for example user could press Home button closing your app - so the ui-element you wanted to change after the wait could already been destroyed.

Android ProgressBar not updating as expected?

I am dynamically populating a TableLayout with rows using an AsyncTask and am not getting the expected ProgressBar behavior.
I initially set the max value of the ProgressBar to three times the number of items I'm processing to accommodate three operations. Here's my code:
onPreExecute:
progress.setMax(items.size() * 3); // We need to create the rows and add listeners to them
Async Class
public class TableLoader extends AsyncTask<Object, String, Boolean>{
#Override
protected Boolean doInBackground(Object... params) {
for (int i = 0; i < items.size(); i++) {
// doStuffToCreateRow
rows.add(row);
progress.publishProgress();
}
}
Then I have:
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (!isCancelled()) {
for (int i = 0; i < rows.size(); i++) {
table.addView(rows.get(i));
progress.incrementProgressBy(1);
}
table.requestLayout();
listener.onTaskCompleted();
}
}
Where publishProgress is simply:
protected void onProgressUpdate(String... values) {
progress.incrementProgressBy(1);
}
Finally, in my onPostExecute method, I have a callback to the activity that uses the AsyncTask, which looks like this in the main activity:
public void onTaskCompleted() { // TableRows have been generated
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
for (int i = 0; i < itemTable.getChildCount(); i++) {
TableRow row = (TableRow) itemTable.getChildAt(i);
// Create and add a listener to that row's checkbox
// progress.incrementProgressBy(1);
}
progress.setVisibility(View.GONE);
}
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion (generation of TableRows). Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
Why is my progressBar not updating as expected?
I've looked the issue up but have found people incrementing the bar by < 1 using division or not using the UI thread. To my knowledge, I'm updating using the UI thread and incrementing by 1/number of items * 3 each time.
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion
It means the Async part is working properly, your first third is the one involving doInBackground / ProgressUpdate, that is, heavy stuff is done outside of the UI thread and just signals progress update. The UI thread is sleeping and happily waiting for update requests.
Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
The other two operations take place in the UI thread. Both for in onPostExecuted and onTaskCompleted run on the UI Thread and are tight loops. The UI thread is blocked in your loops. So while you are inside those tight loops no matter how many thousand times you mingle with any UI component: setprogress, settext, etc.... It will not update until the for loop ends, your routine finishes, and the system takes control again of the UI thread.
What you can do is, as it looks like there's a big number of rows, to add them in chunks then update the progress. This will give some air. Take a look at the following prototype
Handler handler=new Handler();
interface AddRowListener {
public void onRowsAdded();
}
private void addRowChunk(final int startRow, final int totalRows, final AddRowListener listener) {
for (int i=startRow; i<startRow+CHUNK_SIZE; i++) {
if (i>=totalRows) {
// we have finished! Just call the listener
listener.onRowsAdded();
return;
}
addView....
}
// After adding some rows, we end here, and schedule a call to this function again
// to continue. The 25ms delay is optional, but it will give some air to Android for sure
handler.postDelayed(new Runnable() {
addRowChunk(startRow+CHUNK_SIZE, totalRows, listener);
}, 25);
}
private void addRows(AddRowListener listener) {
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
// we start the process, it will create row chunks and call the listener when done
addRowChunk (0, itemTable.getChildCount(), listener);
}
So to add the rows you now can do:
addRows (new AddRowListener() {
public void onRowsAdded() {
// the rows have been added.
}
});
By the way, what's your row size? it has to be huge !
PD_ You have to select a CHUNK_SIZE. Just experiment with values until the UI is snappy. I'd say 40-50 is OK, but it's just a wild guess and depends on the phone hardware.
Don't set your progress.setMax() inside doInBackground(), do it inside onPreExecute() instead. You should not modify the UI thread(Main Thread) from a worker/background thread, and there is exactly where doInBackground() runs. onPreExecute(), onProgressUpdate(), and onPostExecute() run on the UI Thread.
Why you are not using a thread to update the progress bar. This approach is perfect to update the progress........but if you want to set manually then first set the max for progress bar just call below method in your onPreExecute and then onPostExecute
progressBar.setProgress(value);
Where Value should be half of the max when you are calling from onPreExecute and then cent percent from onPostExecute......but I recommend you to use a thread to update the correct progress.
Do not use publishProgress in onPostExecute. Just call incrementProgressBy.

In Android, how do I change the color of a button, pause for a few seconds, then change it again?

Here is my code:
public class TestActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
start();
}
private void start() {
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.linearLayout);
Button button = new Button(this);
layout.addView(button);
button.getBackground().setColorFilter(new LightingColorFilter(0x00000000, 0x0000FF00)); // green
button.invalidate();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
button.getBackground().setColorFilter(new LightingColorFilter(0x00000000, 0x000000FF)); // blue
button.invalidate();
}
}
This just displays a blue button after 3 seconds; it never shows it as green.
I think that if my brain worked properly, I could figure out the answer from one of these posts:
Why isn't view.invalidate immediately redrawing the screen in my android game
How to force a view to redraw immediately before the next line of code is executed
How to force an entire layout View refresh?
You should not sleep on the UI thread, as this will hang your interface. Instead, after initially setting the colour to green, you could create a Handler (on the UI thread, so it will use that thread to handle messages), then use Handler.postDelayed to send a message that will be handled in 3 seconds. In the Runnable you pass to postDelayed you can set the colour to blue.
You're sleeping the main UI thread virtually instantly after setting the colour. This means that the thread is locked up before it gets a chance to do redrawing.
One solution to this is to use threads. Set the background colour, then use another thread to sleep which on completion calls the main (UI) thread to change the colour again.
You can try looking up AsyncTask or Threading.

Implementing a while loop in android

I can't understand the implementation of a while loop in android.
Whenever I implement a while loop inside the onCreate() bundle, (code shown below)
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView=(TextView)findViewById(R.id.TextView);
while (testByte == 0)
updateAuto();
}
nothing boots up, and the program enters a "hanging" state after a while and I can't understand why. Testbyte is as follows:
byte testByte == 0;
and updateAuto() is supposed to update the code per 1 second and display inside the textView portion. I've been using setText inside updateAuto() as shown below and everything works fine, but once i implement the while loop all i see is a black screen and then an option to force close after a few seconds due to it "not responding".
TextView.setText(updateWords);
I've changed it to a button format (meaning i have to click on the button to update itself for now), but i want it to update itself instead of manually clicking it.
Am i implementing the while loop in a wrong way?
I've also tried calling the while loop in a seperate function but it still gives me the black screen of nothingness.
I've been reading something about a Handler service... what does it do? Can the Handler service update my TextView in a safer or memory efficient way?
Many thanks if anyone would give some pointers on what i should do on this.
Brace yourself. And try to follow closely, this will be invaluable as a dev.
While loops really should only be implemented in a separate Thread. A separate thread is like a second process running in your app. The reason why it force closed is because you ran the loop in the UI thread, making the UI unable to do anything except for going through that loop. You have to place that loop into the second Thread so the UI Thread can be free to run. When threading, you can't update the GUI unless you are in the UI Thread. Here is how it would be done in this case.
First, you create a Runnable, which will contain the code that loops in it's run method. In that Runnable, you will have to make a second Runnable that posts to the UI thread. For example:
TextView myTextView = (TextView) findViewById(R.id.myTextView); //grab your tv
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (testByte == 0) {
Thread.sleep(1000); // Waits for 1 second (1000 milliseconds)
String updateWords = updateAuto(); // make updateAuto() return a string
myTextView.post(new Runnable() {
#Override
public void run() {
myTextView.setText(updateWords);
});
}
}
};
Next just create your thread using the Runnable and start it.
Thread myThread = new Thread(myRunnable);
myThread.start();
You should now see your app looping with no force closes.
You can create a new Thread for a while loop.
This code will create a new thread to wait for a boolean value to change its state.
private volatile boolean isClickable = false;
new Thread() {
#Override
public void run() {
super.run();
while (!isClickable) {
// boolean is still false, thread is still running
}
// do your stuff here after the loop is finished
}
}.start();

Categories

Resources