android 子線程中更新界面?被ProgressBar給迷惑了

在看apidemos的例子RetainedFragement時,看到在Thread中執行了 這麼一句

mProgressBar.setProgress(progress);
且執行正常,progressbar確實一直在更新。

頓覺疑惑,View在更新時,會檢查當前線程是否是創建view所在的線程(即UI線程),若不一致,則會拋出異常的。
in ViewRootImpl.java 中:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}


後來查看了setProgress()的源碼後,才恍然大悟,這個方法內已經處理了子線程裏調用的情況了。

@android.view.RemotableViewMethod
    public synchronized void setProgress(int progress) {
        setProgress(progress, false);
    }

    @android.view.RemotableViewMethod
    synchronized void setProgress(int progress, boolean fromUser) {
        if (mIndeterminate) {
            return;
        }

        if (progress < 0) {
            progress = 0;
        }

        if (progress > mMax) {
            progress = mMax;
        }

        if (progress != mProgress) {
            mProgress = progress;
            refreshProgress(R.id.progress, mProgress, fromUser);
        }
    }

    private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
        if (mUiThreadId == Thread.currentThread().getId()) {
            doRefreshProgress(id, progress, fromUser, true);
        } else {
            if (mRefreshProgressRunnable == null) {
                mRefreshProgressRunnable = new RefreshProgressRunnable();
            }

            final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
            mRefreshData.add(rd);
            if (mAttached && !mRefreshIsPosted) {
                post(mRefreshProgressRunnable);
                mRefreshIsPosted = true;
            }
        }
    }


關鍵就是refreshProgress()了,這裏處理了若當前線程不是ui thread,則將更新消息post到ui thread中去執行了。而且可以發現這幾個方法都是加了同步控制的,是線程安全的,保證了多線程調用也是正常的了。

真的是“源碼之下無祕密“啊。

p.s:
之前一次面試的時候,對方有問到能否在子線程裏更新界面?
我回到不能,因爲會拋出異常。對方接着問,爲什麼?
我就答不上來了,現在的理解是,因爲android下的界面更新不是線程安全的,所以要保證在單一線程中同步執行。其他線程要更新UI的話,需要通過handler,Looper消息機制把更新事情pass到UI thread的消息隊列中,由UI thread來完成界面的更新繪製。
面試官又繼續問道,能否在子線程裏去更新progressbar的進度呢?
我當時回答的是不能,必須通過handler去發消息。但現在看來progressbar的代碼後,這個問題的答案其實應該是可以的,因爲progressbar自己內部就處理了子線程的更新問題。

學東西,要能做到知其然,知其所以然。我對知識的掌握層次還太淺了,僅僅停留在會使用工具的層面。平時要多思考,自己給自己提出一些問題(what,why),才能加深對知識的理解。


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