I was testing this code to check if app crashes for changing ui component from background thread. but it didn't.
Here in the code added below. I started a new thread in onCreate()
method of MainActivity
and It should have crashed as per the android docs which says
In the class, the Runnable.run() method contains the code that's executed. Usually, anything is allowable in a Runnable. Remember, though, that the Runnable won't be running on the UI thread, so it can't directly modify UI objects such as View objects.
So I was expecting it to crash. Which it didn't. See code -
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
thread.start();
}
}
While if i try changing ui while starting thread from onClicklistener()
as below it does crash. which is expected.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
}
};
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
thread.start();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.setOnClickListener(listener);
}
}
Now, that the second code snippet crashes, which is expected and the first one doesn't.
Please explain why is this happening, as I'm creating a new worker thread each time but just at different places. Official docs reference will be appreciated.
I found the reason behind this behavior as pointed out by @krish in the comments on my question. The reason is that, the thread was able to make changes in the TextView
object only till it was not visible on UI screen i.e not rendered. It is only after the view rendering, that any thread except the Main thread may not make changes to any UI components. I Tried using view observer to see if the view was rendered before the changes or not. which showed that changes were made before the view rendering.
Here is the code that i tried.
public class MainActivity extends AppCompatActivity {
TextView txt;
Thread thread;
Runnable runnable = new Runnable() {
@Override
public void run() {
txt.setText("bro");
Log.d("ThreadTest", "The Text was changed.");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.name_txt);
thread = new Thread(runnable);
txt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.d("ThreadTest", "The TextView was rendered");
}
});
}
@Override
protected void onResume() {
super.onResume();
thread.start();
}
}
Using the code above. You'll see in the output:
The Text was changed.
The TextView was rendered
Which means text was changed before view rendering. if you try to start thread to makes changes in onGlobalLayout method. App crashes as it should.