Android更新Ui進階精解(一)

《代碼里的世界》

用文字札記描繪自己 android學習之路

轉(zhuǎn)載請保留出處 by Qiao http://blog.csdn.net/qiaoidea/article/details/45115047

[Android更新Ui進階精解(一)][4] android ui線程檢查機制
[Android更新Ui進階精解(二)][5] android 線程更新UI機制


1.回顧

[前面一篇][1]簡單講了如何快速使用handler更新ui。稍微補充一些:

  1. 更新ui時可以直接使用這種方法,你不須非要再new一個子線程才使用,比如:
viewPostBtn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                viewPostBtn.post(new Runnable() {
                    
                    @Override
                    public void run() {
                        titleView.setText("viewPost——Result");
                    }
                });
            }
        });
  1. 這幾種方法可以同樣延伸到很多類似的反不同意義的用法,比如
    sendMessage()可以延伸的方法
  2. sendMessageAtTime(Message msg, long uptimeMillis)在指定時間uptimeMillis時發(fā)送消息msg;
  3. sendMessageDelayed(Message msg, long delayMillis)延遲delayMillis時間后發(fā)送消息msg
  4. sendEmptyMessage(int what) 發(fā)送一個指定類型what的空消息;
  5. sendEmptyMessageAtTime(int what, long uptimeMillis)在指定時間uptimeMillis時發(fā)送一條指定類型what的空消息;
  6. sendEmptyMessageDelayed(int what, long delayMillis)延遲delayMillis時間后發(fā)送一條指定類型what的空消息;
  7. sendMessageAtFrontOfQueue(Message msg)在消息隊列頭(優(yōu)先)發(fā)送這條消息msg;

同樣,post()可以延伸的方法

  1. postAtTime(Runnable r, long uptimeMillis)
  2. postAtTime(Runnable r, Object token, long uptimeMillis)
  3. postDelayed(Runnable r, long delayMillis)
  4. postAtFrontOfQueue(Runnable r)
    請自行查閱相應方法,這里不予一一列出。

2.原理--源碼分析

首先說[上篇][1]的第一個問題,android在生成頁面的同時生成一個ViewRootImpl的對象,這個對象負責檢查checkThread線程是否是在主ui線程,當我們嘗試使用非ui線程更新視圖時,checkThread則拋出異常。

1. 先看看負責檢查線程的ViewRootImpl這段邏輯

@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})  
public final class ViewRootImpl implements ViewParent,  
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {  
    // other code..
  
    void checkThread() {  
        if (mThread != Thread.currentThread()) {  
            throw new CalledFromWrongThreadException(  
                    "Only the original thread that created a view hierarchy can touch its views.");  
        }  
    }  
  
    // other code..
}  

好吧,為什么要這樣?  引用一段比較合理解釋:
 “那么為什么Android要求只能在UI主線程中更改View呢?這就要說到Android的單線程模型了,因為如果支持多線程修改View的話,由此產(chǎn)生的線程同步和線程安全問題將是非常繁瑣的,所以Android直接就定死了,View的操作必須在UI線程,從而簡化了系統(tǒng)設計?!?/strong>

2. 再看看視圖創(chuàng)建時候是何時添加了這個檢查對象的

我們從activity創(chuàng)建說起,首先獲取一個窗口管理器WindowManager,然后設置并初始化其container。接著通過activity得到根視圖DecorView(FramLayout),最后將DecorView添加到 activity的ViewManager 中去,而這個ViewManager 在addView時候就會生成一個ViewRootImpl對象。說這么多感覺表述不清楚,更容易犯糊涂了。 看代碼

/**
* 以下方法是在調(diào)用activity Resume時候執(zhí)行
* @ActivityClientRecord r 記錄activity相關狀態(tài)及參數(shù)
*/
 if (r.window == null && !a.mFinished && willBeVisible) {  
                 //獲取根視圖DecorView
                r.window = r.activity.getWindow();  
                View decor = r.window.getDecorView();  
                decor.setVisibility(View.INVISIBLE);
                //獲取并添加至ViewManager   
                ViewManager wm = a.getWindowManager();  
                WindowManager.LayoutParams l = r.window.getAttributes();  
                a.mDecor = decor;  
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  
                l.softInputMode |= forwardBit;  
                if (a.mVisibleFromClient) {  
                    a.mWindowAdded = true;  
                    wm.addView(decor, l);  
                }  
  
            }

然后更新和顯示activity ,調(diào)用了r.activity.makeVisible()方法

 if (!r.activity.mFinished && willBeVisible  
                    && r.activity.mDecor != null && !r.hideForNow) {  
                //判斷配置等是否需要更新view最后調(diào)用wm.updateViewLayout(decor, l); 方法,略過。。。 
                //最后顯示activity
                r.activity.mVisibleFromServer = true;  
                mNumVisibleActivities++;  
                if (r.activity.mVisibleFromClient) {  
                    r.activity.makeVisible();  //顯示
                }  
            }  

再看activity的makeVisible()方法

    void makeVisible() {  
        if (!mWindowAdded) {  
            ViewManager wm = getWindowManager();  
            wm.addView(mDecor, getWindow().getAttributes());  //注意這里。。
            mWindowAdded = true;  
        }  
        mDecor.setVisibility(View.VISIBLE);  
    } 

這個 ViewManager 的addview方法正是關鍵,它將添加我們提到的ViewRootImpl,其具體實現(xiàn)可以看WindowManagerGlobal:

//主要展示添加ViewRootImpl的過程,其他代碼略
public final class WindowManagerGlobal {  
    public void addView(View view, ViewGroup.LayoutParams params,  
            Display display, Window parentWindow) { 
        ViewRootImpl root;  
  
        synchronized (mLock) { 
  
            root = new ViewRootImpl(view.getContext(), display);  
  
            mRoots.add(root);  
        }  
    }  
}  

至此,我們大略知道了ui線程是在何時對更新過程和加以控制檢查,并了解了檢查的內(nèi)部原理。
  看到這里很多小伙伴們肯定會不滿了,你他瞄不是說講解handler更新Ui的原理嗎,凈扯這些有屁用呢!額,由于篇幅限制,這段放在下一篇[Android更新Ui進階精解(二)][5] 講解。咱線繼續(xù)上一話題:
  那么,我們真的就不能在子線程里更新Ui了嗎?顯然不是,基于前面講解的部分,既然是在onResume時候生成檢查ViewRootImpl對象,所以我們其實可以在oncreate里更新Ui,比如

         //onCreate調(diào)用這段
         new Thread(new Runnable() {  
            @Override  
            public void run() {  
                titleView.setText("OtherThread");  
            }  
        }).start();  

當然,你是不能這樣用的

   new Thread(new Runnable() {  
            @Override  
            public void run() {  
                try {  
                    Thread.sleep(200);  //睡了就再起不來了
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                titleView.setText("OtherThread");  
            }  
        }).start();  

但是為什么我我們又能在OnResume里這么用?

@Override  
    protected void onResume() {  
        super.onResume();  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                titleView.setText("OtherThread");  
            }  
        }).start();  
    }  

愛哥說是因為消息隊列Message Queue在接收和處理過程并非立即的,需要一個過程。(這一部分我大愛哥 [愛哥 --非UI線程更新UI][3] 其實有精講,大家不妨看一下。)其實我覺得不妨可以大膽猜想,只要view是在渲染到視圖之前,我們都是可以通過其他線程來更改的。大家有空可以研究下。

--
[1]: http://m.itdecent.cn/p/4c60506c3ae1
[2]: http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html
[3]: http://blog.csdn.net/aigestudio/article/details/43449123
[4]: http://m.itdecent.cn/p/6de0a42a44d6
[5]: http://##

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容