Reputation: 483
Considering a simple code like this:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInastanceState){
super.onCreate(savedInastanceState);
setContentView(R.layout.main);
final ProgressDialog pdUpdate = new ProgressDialog(this);
final Button btn = (Button)findViewById(R.id.btnUpdate);
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
btn.setText(Calendar.getInstance().getTime().toString());
int i=0;
while (i<100){
i++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("test","i = " + i);
}
}
});
}
}
By click on btn
two things should happen: changing the text of the button and counting i
to 100 with delays of 100 msec (it is just an example to simulate a heavy process like reading file, downloading etc).
I know the correct way of implementing such codes is to use AsyncTask
but my question is about how this code is compiled. It is a single thread app. So the compiler reads btn.setText(Calendar.getInstance().getTime().toString());
first and goes to the next lines of code only after this line is executed (please correct me if I am wrong), but it does not happen. Why?
In C#
there is a Refresh()
method which solves this problem (just call it after UI changes and changes are applied). Is there any similar method in java?
I appreciate any help.
EDIT 1
there question is about the order of the following processes:
Process 1:
btn.setText(Calendar.getInstance().getTime().toString());
Process 2:
i++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("test","i = " + i);
}
No matter which one comes first ( I mean either btn.onClick(){process1;process2;}
or btn.onClick(){process2;process1;}
), always the second process is executed first. I mean first I see the counting of i
in Logcat and then see the changing of the button text.
Upvotes: 0
Views: 96
Reputation: 3847
You do not need to call any equivalent of refresh()
in this case. However Button.setText()
do not redraw the view hierarchy automatically. Instead it passes up the view hierarchy the information that the text changed and that it needs to be redrawn. Ultimately this information reaches the root of the view hierarchy which in turn informs the Choreographer
. The Choreographer
schedules the drawing for the next frame. This in turn gets stored in your UI thread's message queue.
So when you put your UI thread to sleep, the layout is not redrawn, but is scheduled to be redrawn. As soon as your thread becomes idle, a messages from message queue start being executed. At some point the Choreographer
is called with the redraw message and orders the view hierarchy to redraw itself.
Also consider that methods like Handler.post(Runnable)
, View.post(Runnable)
and their postDelayed
counterparts can serve as an alternative to AsyncTask
if do not need to do a heavy computation, but instead schedule some operation (for example a view update) for later. These use the same mechanism as described above - put a Runnable
into the thread's message queue, which in turn gets picked up and executed when the thread is idle.
Upvotes: 3
Reputation: 67
As many people pointed out in the comments, since you are doing your while
loop within the onClickListener
the UI thread never gets the chance to invalidate
your button, and thus redraw said button.
I would think if you wanted to get around this you could simply have another listener related to the redraw process finishing. Take a look at the following from this answer:
public class DrawListenerView extends View{
private Callback callback;
public DrawListenerView(Callback callback){
this.callback = callback;
}
@Override
protected void onDraw (Canvas canvas){
super.onDraw(canvas);
//add your method here you want to call
//Or use a Callback-pattern
callback.finish();
}
}
public interface Callback(){
public void finish();
}
You could do your looped sleeping once the draw of your button has finished. This would give you the new text drawn, and then sleep after.
I'm not sure what the application for this would be, however you might consider just disabling the button for some time after they press it if you don't want the user to click it again for whatever amount of time.
Upvotes: 0
Reputation: 114
nonono,only your method run finish can it work(exp:just sleep 100 msc without while loop).you can find in google how the java method works.(my english is very poor)
Upvotes: 0
Reputation: 6968
Android is managing its own drawing cycles. But you're sharing the main thread with it. You can call invalidate()
on a view, which is planning a redrawing of the view. But you'll have to free some time on the main thread fro Android to refresh the UI anyway (setText()
is probably calling invalidate()
anyway).
Upvotes: -1