Android更新Ui進(jìn)階精解(一)

《代碼里的世界》

用文字札記描繪自己 android學(xué)習(xí)之路

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

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


1.回顧

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

  1. 更新ui時(shí)可以直接使用這種方法启盛,你不須非要再new一個(gè)子線程才使用潘拨,比如:
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)在指定時(shí)間uptimeMillis時(shí)發(fā)送消息msg加匈;
  3. sendMessageDelayed(Message msg, long delayMillis)延遲delayMillis時(shí)間后發(fā)送消息msg
  4. sendEmptyMessage(int what) 發(fā)送一個(gè)指定類型what的空消息攘轩;
  5. sendEmptyMessageAtTime(int what, long uptimeMillis)在指定時(shí)間uptimeMillis時(shí)發(fā)送一條指定類型what的空消息;
  6. sendEmptyMessageDelayed(int what, long delayMillis)延遲delayMillis時(shí)間后發(fā)送一條指定類型what的空消息率碾;
  7. sendMessageAtFrontOfQueue(Message msg)在消息隊(duì)列頭(優(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)
    請(qǐng)自行查閱相應(yīng)方法所宰,這里不予一一列出。

2.原理--源碼分析

首先說(shuō)[上篇][1]的第一個(gè)問(wèn)題畜挥,android在生成頁(yè)面的同時(shí)生成一個(gè)ViewRootImpl的對(duì)象仔粥,這個(gè)對(duì)象負(fù)責(zé)檢查checkThread線程是否是在主ui線程,當(dāng)我們嘗試使用非ui線程更新視圖時(shí)蟹但,checkThread則拋出異常躯泰。

1. 先看看負(fù)責(zé)檢查線程的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呢麦向?這就要說(shuō)到Android的單線程模型了,因?yàn)槿绻С侄嗑€程修改View的話客叉,由此產(chǎn)生的線程同步和線程安全問(wèn)題將是非常繁瑣的诵竭,所以Android直接就定死了兼搏,View的操作必須在UI線程卵慰,從而簡(jiǎn)化了系統(tǒng)設(shè)計(jì)》鹕耄”

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

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

/**
* 以下方法是在調(diào)用activity Resume時(shí)候執(zhí)行
* @ActivityClientRecord r 記錄activity相關(guān)狀態(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); 方法紊撕,略過(guò)罢荡。。。 
                //最后顯示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);  
    } 

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

//主要展示添加ViewRootImpl的過(guò)程漱受,其他代碼略
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線程是在何時(shí)對(duì)更新過(guò)程和加以控制檢查骡送,并了解了檢查的內(nèi)部原理昂羡。
  看到這里很多小伙伴們肯定會(huì)不滿了,你他瞄不是說(shuō)講解handler更新Ui的原理嗎摔踱,凈扯這些有屁用呢虐先!額,由于篇幅限制派敷,這段放在下一篇[Android更新Ui進(jìn)階精解(二)][5] 講解蛹批。咱線繼續(xù)上一話題:
  那么,我們真的就不能在子線程里更新Ui了嗎篮愉?顯然不是腐芍,基于前面講解的部分,既然是在onResume時(shí)候生成檢查ViewRootImpl對(duì)象试躏,所以我們其實(shí)可以在oncreate里更新Ui猪勇,比如

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

當(dāng)然,你是不能這樣用的

   new Thread(new Runnable() {  
            @Override  
            public void run() {  
                try {  
                    Thread.sleep(200);  //睡了就再起不來(lái)了
                } 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();  
    }  

愛哥說(shuō)是因?yàn)橄㈥?duì)列Message Queue在接收和處理過(guò)程并非立即的泣刹,需要一個(gè)過(guò)程。(這一部分我大愛哥 [愛哥 --非UI線程更新UI][3] 其實(shí)有精講裁替,大家不妨看一下项玛。)其實(shí)我覺(jué)得不妨可以大膽猜想,只要view是在渲染到視圖之前弱判,我們都是可以通過(guò)其他線程來(lái)更改的襟沮。大家有空可以研究下。

--
[1]: http://www.reibang.com/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://www.reibang.com/p/6de0a42a44d6
[5]: http://##

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昌腰,一起剝皮案震驚了整個(gè)濱河市开伏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遭商,老刑警劉巖固灵,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劫流,居然都是意外死亡巫玻,警方通過(guò)查閱死者的電腦和手機(jī)丛忆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)仍秤,“玉大人熄诡,你說(shuō)我怎么就攤上這事∈Γ” “怎么了凰浮?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)苇本。 經(jīng)常有香客問(wèn)我袜茧,道長(zhǎng),這世上最難降的妖魔是什么瓣窄? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任笛厦,我火速辦了婚禮,結(jié)果婚禮上俺夕,老公的妹妹穿的比我還像新娘递递。我一直安慰自己,他們只是感情好啥么,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贰逾,像睡著了一般悬荣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疙剑,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天氯迂,我揣著相機(jī)與錄音,去河邊找鬼言缤。 笑死嚼蚀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的管挟。 我是一名探鬼主播轿曙,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼僻孝!你這毒婦竟也來(lái)了导帝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤穿铆,失蹤者是張志新(化名)和其女友劉穎您单,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荞雏,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虐秦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年平酿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悦陋。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜈彼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叨恨,到底是詐尸還是另有隱情柳刮,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布痒钝,位于F島的核電站秉颗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏送矩。R本人自食惡果不足惜蚕甥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望栋荸。 院中可真熱鬧菇怀,春花似錦、人聲如沸晌块。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)匆背。三九已至呼伸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钝尸,已是汗流浹背括享。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留珍促,地道東北人铃辖。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猪叙,于是被迫代替她去往敵國(guó)和親娇斩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容