android中子線程可以更新UI,是真的嗎?

看到這個問題,相信很多小夥伴認爲,android開啓子線程更新UI,是不對的,爲什麼呢?

因爲只有主線程(UI線程)纔可以進行UI的修改,如果在子線程進行UI更新,會拋出異常:

    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views

是的,上面的表述是對的,但是,爲什麼說子線程又可以更新UI呢?這種說法可靠嗎?

現在,我們用事實(代碼)說話,來一段,

場景1:在MainActivity中開啓子線程,設置TextView的內容,佈局文件中有個TextView,比較簡單,這裏忽略,如下:

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("MainActivity", "onCreate執行...");
        initView();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.v("MainActivity", "準備UI更新...");
                tv.setText("測試子線程更新UI");
                Log.v("MainActivity", "完成UI更新...");
            }
        }).start();

    }

    private void initView() {
        tv = (TextView) this.findViewById(R.id.tv);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "onResume執行...");
    }
}

可以在日誌看到:

    onCreate執行...
    onResume執行...
    準備UI更新...
    完成UI更新...
    I/ActivityManager:Displayed com.rockykou.debug/.MainActivity: +868ms

運行結果:正常,界面顯示“測試子線程更新UI

場景2:在子線程的更新UI代碼前,加上Thread.sleep(),其他與上面場景一致,如下:

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("MainActivity", "onCreate執行...");
        initView();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //添加部分
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.v("MainActivity", "準備UI更新...");
                tv.setText("測試子線程更新UI");
                Log.v("MainActivity", "完成UI更新...");
            }
        }).start();

    }

    private void initView() {
        tv = (TextView) this.findViewById(R.id.tv);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "onResume執行...");
    }
}

可以在日誌看到:

    onCreate執行...
    onResume執行...
    I/ActivityManager: Displayed com.rockykou.debug/.MainActivity: +1s33ms
    準備UI更新...
    AndroidRuntime: FATAL EXCEPTION: Thread-161
              android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
              at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4607)

              ...

運行結果:異常,app崩潰

比較下,場景1情況,子線程更新UI正常,場景2情況,加上Thread.sleep(5000),子線程更新UI異常,而且注意場景2中有個ViewRootImpl.checkThread方法,

怎麼解釋上述問題呢?

首先,這是兩個線程,

場景1,主線程執行onCreate,開啓子線程,主線程繼續執行onResume,接下來,子線程執行,這裏明確一點,ViewRootImpl類l對象沒有被創建,checkThread沒有被執行,直到子線程執行完,UI更新正常完成

場景2,主線程執行onCreate, 開啓子線程,主線程繼續執行onResume,子線程處於睡眠5秒狀態,當繼續執行子線程代碼時,ViewRootImpl對象已經被創建,checkThead方法被調用,檢查出當前線程(也就是子線程)與原來的線程(主線程)不一致,拋出異常

實際上,子線程執行更新UI代碼時,如果ViewRootImpl對象如果已經創建,UI更新異常,未被創建,UI更新正常,


最終,根本在cpu對時間片的分配上,

當主線程有足夠的時間執行時,ViewRootImpl對象被創建,子線程更新UI異常

當主線程沒有足夠時間(此時子線程在佔有時間片)執行時,ViewRootImpl對象未創建,子線程更新UI正常



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章