Handler機制從入門到放棄(一)

閑來無事颂翼,準備好好梳理一下Handler機制,之前分析過沒有寫成博客慨灭,結(jié)果就是慢慢的淡忘了朦乏,這次趁著剛分析完,趕緊寫下來氧骤。

在開始分析之前先打打基礎(chǔ)呻疹,理解理解什么是線程以及什么是Handler,這里大部分內(nèi)容引用一篇來自伯樂在線的文章筹陵,因為看來看去關(guān)于基礎(chǔ)的部分這個人已經(jīng)說得很好了刽锤,我就負責(zé)把主要的部分抽取出來。

原文地址:Android線程和Handler基礎(chǔ)入門


現(xiàn)在大多數(shù)的移動設(shè)備已經(jīng)變得越來越快朦佩,但是它們其實也不算是非巢⑺迹快。如果你想讓你的APP既可以承受一些繁雜的工作而又不影響用戶體驗的話吕粗,那么必須把任務(wù)并行執(zhí)行纺荧。在Android上,我們使用線程颅筋。

什么是線程宙暇?

線程或者線程執(zhí)行本質(zhì)上就是一串命令(也是程序代碼),然后我們把它發(fā)送給操作系統(tǒng)執(zhí)行议泵。

線程

一般來說占贫,我們的CPU在任何時候一個核只能處理一個線程。多核處理器(目前大多數(shù)Android設(shè)備已經(jīng)都是多核)顧名思義先口,就是可以同時處理多線程(通俗地講就是可以同時處理多件事)型奥。

多核處理與單核多任務(wù)處理的實質(zhì)

上面我說的是一般情況,并不是所有的描述都是一定正確的碉京。因為單核也可以用多任務(wù)模擬出多線程厢汹。

每個運行在線程中的任務(wù)都可以分解成多條指令,而且這些指令不用同時執(zhí)行谐宙。所以烫葬,單核設(shè)備可以首先切換到線程1去執(zhí)行指令1A,然后切換到線程2去執(zhí)行指令2A,接著返回到線程1再去執(zhí)行1B搭综、1C垢箕、1D,然后繼續(xù)切換到線程2兑巾,執(zhí)行2B条获、2C等等,以此類推蒋歌。

這個線程之間的切換十分迅速帅掘,以至于在單核的設(shè)備中也會發(fā)生。幾乎所有的線程都在相同的時間內(nèi)進行任務(wù)處理奋姿。其實锄开,這都是因為速度太快造成的假象素标,就像電影《黑客帝國》里的特工Brown一樣称诗,可以變幻出很多的頭和手。

Java核心里的線程

在Java中头遭,如果要想做平行任務(wù)處理的話寓免,會在Runnable里面執(zhí)行你的代碼〖莆可以繼承Thread類袜香,或者實現(xiàn)Runnable接口:

// 1
public class IAmAThread extends Thread {
    public IAmAThread() {
        super("IAmAThread");
    }

    @Override
    public void run() {

// your code (sequence of instructions)
    }
}
// to execute this sequence of instructions in a separate thread.
new IAmAThread().start();

// 2
public class IAmARunnable implements Runnable {
    @Override
    public void run() {

// your code (sequence of instructions)
    }
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable = new IAmARunnable();
new Thread(myRunnable).start();

這兩個方法基本上是一樣的。第一個版本是創(chuàng)建一個Thread類鲫惶,第二個版本是需要創(chuàng)建一個Runnable對象蜈首,然后也需要一個Thread類來調(diào)用它。

Android上的線程

無論何時啟動APP欠母,所有的組件都會運行在一個單獨的線程中(默認的)——叫做主線程欢策。這個線程主要用于處理UI的操作并為視圖組件和小部件分發(fā)事件等,因此主線程也被稱作UI線程(Main Thread)赏淌。除了Main Thread之外的線程都可稱為Worker Thread踩寇。Main Thread主要負責(zé)控制UI頁面的顯示、更新六水、交互等俺孙。 因此所有在UI線程中的操作要求越短越好,只有這樣用戶才會覺得操作比較流暢掷贾。一個比較好的做法是把一些比較耗時的操作睛榄,例如網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作想帅、 復(fù)雜計算等邏輯都封裝到單獨的線程场靴,這樣就可以避免阻塞主線程,這樣就需要用到了Android的Handler機制博脑。

這里劃重點:Handler負責(zé)與子線程進行通訊憎乙,從而讓子線程與主線程之間建立起協(xié)作的橋梁票罐,使Android的UI更新的問題得到完美的解決

怎么創(chuàng)建Handler

既然Handler有這樣的好處,那么看Handler怎么用泞边,官方給出了兩種方式創(chuàng)建一個Handler:

1该押、使用默認的構(gòu)造方法:new Handler()。
2阵谚、使用帶參的構(gòu)造方法蚕礼,參數(shù)是一個Runnable對象或者回調(diào)對象。

//第一種方法
private Handler handler = new Handler();  
   private Runnable myRunnable= new Runnable() {    
        public void run() {  
             //一些耗時操作
        }  
    }; 
 //其他地方調(diào)用
 handler.post(xxx);
 這里就寫一個post方法梢什,實際上還有很多奠蹬,諸如postDelayed、postAtTime
//第二種方法
Handler myHandler = new Handler() {  
          public void handleMessage(Message msg) {   
               switch (msg.what) {   
                   //根據(jù)參數(shù)進行操作
                         break;   
               }   
               super.handleMessage(msg);   
          }   
     };  
  //其他地方調(diào)用
myHandler.sendMessage(xxx);

如何使用Handler

這里使用一個簡單的Demo來演示Handler的用法嗡午,界面偏簡單就不貼了囤躁,直接貼代碼,模擬的是點擊Button執(zhí)行下載荔睹,下載完成后更新UI狸演。

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("開始下載文件");
                //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                MainActivity.this.statusTextView.setText("文件下載完成");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

按照以前寫Java的思路的話可能會這么寫僻他,但是運行程序時候會發(fā)現(xiàn)控制臺報錯:

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

錯誤的意思是只有創(chuàng)建View的原始線程才能更新View宵距。出現(xiàn)這樣錯誤的原因是Android中的View不是線程安全的,下面給出合理的解釋:

因為UI訪問是沒有加鎖的吨拗,在多個線程中訪問UI是不安全的满哪,如果有多個子線程都去更新UI,會導(dǎo)致界面不斷改變而混亂不堪劝篷。所以最好的解決辦法就是只有一個線程有更新UI的權(quán)限哨鸭,所以這個時候就只能有一個線程振臂高呼:放開那女孩,讓我來携龟!那么最合適的人選只能是主線程兔跌。

來自---Android中線程那些事

那么為了規(guī)避Android的這種機制,我們這里分別采用Handler的兩種方式來實現(xiàn)上面的代碼:

A峡蟋、使用post方式

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主線程中創(chuàng)建坟桅,所以自動綁定主線程
    private Handler uiHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("開始下載文件");
                //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        MainActivity.this.statusTextView.setText("文件下載完成");
                    }
                };
                uiHandler.post(runnable);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}


B蕊蝗、使用sendMessage方式實現(xiàn)

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主線程中創(chuàng)建仅乓,所以自動綁定主線程
    private Handler uiHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    System.out.println("msg.arg1:" + msg.arg1);
                    System.out.println("msg.arg2:" + msg.arg2);
                    MainActivity.this.statusTextView.setText("文件下載完成");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
        System.out.println("Main thread id " + Thread.currentThread().getId());
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("開始下載文件");
                //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時過程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                Message msg = new Message();
                //雖然Message的構(gòu)造函數(shù)式public的蓬戚,我們也可以通過以下兩種方式通過循環(huán)對象獲取Message
                //msg = Message.obtain(uiHandler);
                //msg = uiHandler.obtainMessage();

                //what是我們自定義的一個Message的識別碼夸楣,以便于在Handler的handleMessage方法中根據(jù)what識別
                //出不同的Message,以便我們做出不同的處理操作
                msg.what = 1;

                //我們可以通過arg1和arg2給Message傳入簡單的數(shù)據(jù)
                msg.arg1 = 123;
                msg.arg2 = 321;
                //我們也可以通過給obj賦值Object類型傳遞向Message傳入任意數(shù)據(jù)
                //msg.obj = null;
                //我們還可以通過setData方法和getData方法向Message中寫入和讀取Bundle類型的數(shù)據(jù)
                //msg.setData(null);
                //Bundle data = msg.getData();
                //將該Message發(fā)送給對應(yīng)的Handler
                uiHandler.sendMessage(msg);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

以上代碼來自博客:Android中Handler的使用

上面這兩種形式都能達到我們的要求,在此不一一測驗豫喧,注釋寫的很詳細了石洗,看到這里應(yīng)該已經(jīng)大致知道了如何使用Handler,但是我想我們應(yīng)該遠遠不滿足于此紧显,下一篇博客將帶著大家從源碼一起看看Handler機制到底是怎么實現(xiàn)的讲衫。
Handler機制從入門到放棄(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市孵班,隨后出現(xiàn)的幾起案子涉兽,更是在濱河造成了極大的恐慌,老刑警劉巖篙程,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枷畏,死亡現(xiàn)場離奇詭異,居然都是意外死亡虱饿,警方通過查閱死者的電腦和手機拥诡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郭厌,“玉大人袋倔,你說我怎么就攤上這事≌勰” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵批狐,是天一觀的道長扇售。 經(jīng)常有香客問我,道長嚣艇,這世上最難降的妖魔是什么承冰? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮食零,結(jié)果婚禮上困乒,老公的妹妹穿的比我還像新娘。我一直安慰自己贰谣,他們只是感情好娜搂,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吱抚,像睡著了一般百宇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秘豹,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天携御,我揣著相機與錄音,去河邊找鬼。 笑死啄刹,一個胖子當著我的面吹牛涮坐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播誓军,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼膊升,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谭企?” 一聲冷哼從身側(cè)響起廓译,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎债查,沒想到半個月后非区,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡盹廷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年征绸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俄占。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡管怠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缸榄,到底是詐尸還是另有隱情渤弛,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布甚带,位于F島的核電站她肯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鹰贵。R本人自食惡果不足惜晴氨,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碉输。 院中可真熱鬧籽前,春花似錦、人聲如沸敷钾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闰非。三九已至膘格,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間财松,已是汗流浹背瘪贱。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工纱控, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菜秦。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓甜害,卻偏偏與公主長得像,于是被迫代替她去往敵國和親球昨。 傳聞我的和親對象是個殘疾皇子尔店,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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