In the code section given below, I have run a thread on a button click. At first, the thread will set text to the textView, then it will sleep for 6 seconds. But, in reality on the button click, at first, the thread is sleeping for 6 seconds and then it is setting text to the textView. Now, why is this mismatch of statement execution flow happening?
public class MainActivity extends AppCompatActivity { EditText editText; Button button; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = findViewById(R.id.editText); button = findViewById(R.id.button); textView = findViewById(R.id.textView); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { button.setEnabled(false); runthread(); } }); } private void runthread() { final String s1 = editText.getText().toString(); Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { textView.setText(s1); button.setEnabled(true); try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }); } }
Advertisement
Answer
Android main thread is built upon two core concepts Looper and Handler. It translates to a message loop related to the thread, and messages interactor. All the ui staff benefit on that. For instance used by you setText
is nothing more then sending a new message to the looper, after some time the message is resolved by some handler. As you may guess firstly each message is enqueued.
If you look at your example through the prism of above, you see steps listed below.(All steps are taken on main thread / main looper)
Handler handler = new Handler(); <-- this handler is related to main looper handler.post(new Runnable() { <-- enqueue new message to main looper @Override public void run() { runOnUiThread(new Runnable() { <-- enqueue new message @Override public void run() { textView.setText(s1); <-- enqueue new message of setting text button.setEnabled(true); <-- enqueue new message of enabling button try { Thread.sleep(6000); <-- suspend thread for 6 seconds } catch (InterruptedException e) { e.printStackTrace(); } } }); } });
According to above, you suspend thread before all the messages including setting text and enabling button are handled. The consequence of that is reversed order.
#UPDATE Then, how can I enqueue this sleep() method also?
val handler = Handler(); handler.post { runOnUiThread { textView.text = "ABC"; button.isEnabled = true; } } handler.postDelayed({ try { Thread.sleep(6000); } catch (e: InterruptedException) { e.printStackTrace(); } }, 16) <-- 16 milliseconds is a time between frames