Cut to the chase:

The myth is that child threads cannot update the UI and should be discussed categorically, not categorically.

Half an hour ago, my XRecyclerView group inside, a group of friends chat me privately, the question is:

Why did my child thread update the UI without error?

I asked him to send me the code and HERE it is, very simple code.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    title = (TextView) findViewById(R.id.title_tips);
    doGet("http; //www.baidu.com".new Callback() {
        @Override
        public void onFailure(Request request, IOException e) {}@Override
        public void onResponse(Response response) throws IOException {
            title.setText(response.body().string()); // Here we update the text in the child thread}}); }private void doGet(String url,Callback callback) {
    OkHttpClient client = new OkHttpClient();

    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url).get().build();

    client.newCall(request).enqueue(callback);
}Copy the code

So let’s do a simple analysis. He used OkHttp’s asynchronous enqueue request and updated the text of the textView after it succeeded.

To be clear:

  • Okhttp’s synchronous asynchronous callbacks are all in child threads.

In this case, as we’ve been taught to do: child threads can’t refresh the UI, this code is fine.

And here’s what I’m saying:

The code above doesn’t have to go wrong, and it runs smoothly.

Are you seriously suspicious?

You can try it. If you don’t mind, you can run the following transparent child thread to update the UI code

public class TestActivity extends Activity {
    private TextView title;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        title = (TextView) findViewById(R.id.title_tips);
        new Thread(
                new Runnable() {
                    @Override
                    public void run(a) {
                        // Child thread updates UI
                        title.setText("I did it with no fucking problem."); } } ).start(); }}Copy the code

Tried all know, true TM execution did not explode wrong.

Subversion?

why

After seeing the part of the code he sent me, onCreate, everything became clear. This is also the pit set by the person I interviewed several years ago. I’m going to go straight to the reason, source code analysis that you should look at, you should look at.

  • The limit that child threads cannot update the UI is the viewrootimpl.java internal limit
    void checkThread(a) {
      // This method is viewrootimpl.java internal code
      if(mThread ! = Thread.currentThread()) {throw new CalledFromWrongThreadException(
                  "Only the original thread that created a view hierarchy can touch its views."); }}Copy the code
  • For component activities, viewrotimpl is initialized after onCreate and after onResume.
  • If your child thread update code meets the following conditions, it will run smoothly:
    • Modify the application layer viewrootimpl. Java source code, remove restrictions
    • Write your update code before onResume, such as onCreate, and update it before viewrotimPL initializes.

Modify validation – throws an error

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    title = (TextView) findViewById(R.id.title_tips);
    new Thread(
            new Runnable() {
                @Override
                public void run(a) {
                    try {
                        // Wait for onResume to finish and let the viewrotimpl initialization complete
                        Thread.sleep(3000); // ---------- here, look here
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    title.setText("I can't do it.");
                }
            }
    ).start();
}Copy the code

After the