1. 程式人生 > >Android技術----Activity原始碼

Android技術----Activity原始碼

一、Activity相關

為什麼下邊不睡眠可以設定setText(),新增 睡眠1秒就丟擲異常?看下面程式碼:

public class MainActivity extends AppCompatActivity {

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

        final TextView textview = (TextView) findViewById(R.id.textview);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                textview.setText("1111");

            }
        }).start();
    }
}

如上程式碼:
如果不新增 睡眠,直接線上程中設定 setText("1111")不會報錯;
如果添加了 睡眠,然後設定 setText("1111")就會在 1秒 後報錯:Only the original thread that created a view hierarchy can touch its views.
這個錯意思是:不能在原始執行緒以外的任何執行緒去更新UI

前提知識:在ViewRootImpl中,首先呼叫 requestLayout(),然後在 requestLayout() 中呼叫 checkThread()方法,這個方法會判斷當前執行緒是否等於建立的執行緒,如果不等於,直接丟擲異常;

原因如下:
1>:不新增睡眠:不報錯,是因為 onCreate()是在 MainActivity建立之後就會執行,而 checkThread()是在 ViewRootImpl的 requestLayout()中執行的,而佈局的 繪製流程是在 onResume()之後開始的。所以說:在 onCreate()中 new Thread(new Runnable()).start() 更新UI是可以的,因為它不會呼叫 onResume(),不會去繪製佈局,所以就不會呼叫 checkThread()方法,所以不會丟擲異常;

2>:新增睡眠:但是當睡眠1秒之後,這個時候 onResume()已經執行完了,就開始繪製佈局,如果你在去更新UI,就會呼叫 requestLayout(),就會呼叫checkThread()方法,所以就會丟擲異常;

二. 什麼是主執行緒(UI執行緒)、什麼是子執行緒?

不能在子執行緒中更新UI,這個觀點不太對;
假設子執行緒可以更新UI,會出現錯亂的問題,因為會涉及到 同步問題,假設執行緒1和執行緒2同時去更新UI,無法確保到底更新成執行緒1的樣子還是執行緒2的樣子,加鎖?無法確定加鎖的位置。所以谷歌就只能允許一個執行緒去更新UI,就把這個執行緒叫做UI執行緒,也稱為主執行緒。

所以,谷歌這樣做如果直接在 子執行緒更新UI,就會報錯 Only the original thread that created a view hierarchy can touch its views.,不能在原始執行緒以外的任何執行緒去更新UI,而不是不能在子執行緒中更新UI。

三. toast在 new Thread中的問題:

new Thread(new Runnable() {
            @Override
            public void run() {
               
                Looper.prepare();
                Toast.makeText(MainActivity.this , "1111" , Toast.LENGTH_SHORT).show();
                Looper.loop();

            }
        }).start();

如果直接讓 toast 在 new Thread()中彈出,就會報錯,報這個錯:Can't create handler inside thread that has not called Looper.prepare()

如果添加了Looper.prepare()和 Looper.loop(),就可以彈出 toast,這個還是在子執行緒中 彈出的 toast;

在子執行緒可以彈出 toast,原因是:
子執行緒更新 Toast是載入在 WindowManager上邊,它不是Activity,所以就不會來到 ViewRootImpl中,所以就不會執行 checkThread(),所以就不會丟擲異常



感謝:

https://www.jianshu.com/p/0da229c00f0d