欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Handler的使用詳解

 更新時(shí)間:2021年09月08日 09:47:19   作者:孫群  
這篇文章主要介紹了Android Handler的使用詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

在Android開發(fā)中,我們經(jīng)常會(huì)遇到這樣一種情況:在UI界面上進(jìn)行某項(xiàng)操作后要執(zhí)行一段很耗時(shí)的代碼,比如我們?cè)诮缑嫔宵c(diǎn)擊了一個(gè)”下載“按鈕,那么我們需要執(zhí)行網(wǎng)絡(luò)請(qǐng)求,這是一個(gè)耗時(shí)操作,因?yàn)椴恢朗裁磿r(shí)候才能完成。為了保證不影響UI線程,所以我們會(huì)創(chuàng)建一個(gè)新的線程去執(zhí)行我們的耗時(shí)的代碼。當(dāng)我們的耗時(shí)操作完成時(shí),我們需要更新UI界面以告知用戶操作完成了。所以我們可能會(huì)寫出如下的代碼:

package ispring.com.testhandler;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


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秒中,模擬文件的耗時(shí)過(guò)程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                MainActivity.this.statusTextView.setText("文件下載完成");
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

上面的代碼演示了單擊”下載“按鈕后會(huì)啟動(dòng)一個(gè)新的線程去執(zhí)行實(shí)際的下載操作,執(zhí)行完畢后更新UI界面。但是在實(shí)際運(yùn)行到代碼MainActivity.this.statusTextView.setText(“文件下載完成”)時(shí),會(huì)報(bào)錯(cuò)如下,系統(tǒng)崩潰退出:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
錯(cuò)誤的意思是只有創(chuàng)建View的原始線程才能更新View。出現(xiàn)這樣錯(cuò)誤的原因是Android中的View不是線程安全的,在Android應(yīng)用啟動(dòng)時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè)線程,即程序的主線程,主線程負(fù)責(zé)UI的展示、UI事件消息的派發(fā)處理等等,因此主線程也叫做UI線程,statusTextView是在UI線程中創(chuàng)建的,當(dāng)我們?cè)贒ownloadThread線程中去更新UI線程中創(chuàng)建的statusTextView時(shí)自然會(huì)報(bào)上面的錯(cuò)誤。Android的UI控件是非線程安全的,其實(shí)很多平臺(tái)的UI控件都是非線程安全的,比如C#的.Net Framework中的UI控件也是非線程安全的,所以不僅僅在Android平臺(tái)中存在從一個(gè)新線程中去更新UI線程中創(chuàng)建的UI控件的問(wèn)題。不同的平臺(tái)提供了不同的解決方案以實(shí)現(xiàn)跨線程跟新UI控件,Android為了解決這種問(wèn)題引入了Handler機(jī)制。

那么Handler到底是什么呢?Handler是Android中引入的一種讓開發(fā)者參與處理線程中消息循環(huán)的機(jī)制。每個(gè)Hanlder都關(guān)聯(lián)了一個(gè)線程,每個(gè)線程內(nèi)部都維護(hù)了一個(gè)消息隊(duì)列MessageQueue,這樣Handler實(shí)際上也就關(guān)聯(lián)了一個(gè)消息隊(duì)列??梢酝ㄟ^(guò)Handler將Message和Runnable對(duì)象發(fā)送到該Handler所關(guān)聯(lián)線程的MessageQueue(消息隊(duì)列)中,然后該消息隊(duì)列一直在循環(huán)拿出一個(gè)Message,對(duì)其進(jìn)行處理,處理完之后拿出下一個(gè)Message,繼續(xù)進(jìn)行處理,周而復(fù)始。當(dāng)創(chuàng)建一個(gè)Handler的時(shí)候,該Handler就綁定了當(dāng)前創(chuàng)建Hanlder的線程。從這時(shí)起,該Hanlder就可以發(fā)送Message和Runnable對(duì)象到該Handler對(duì)應(yīng)的消息隊(duì)列中,當(dāng)從MessageQueue取出某個(gè)Message時(shí),會(huì)讓Handler對(duì)其進(jìn)行處理。

Handler可以用來(lái)在多線程間進(jìn)行通信,在另一個(gè)線程中去更新UI線程中的UI控件只是Handler使用中的一種典型案例,除此之外,Handler可以做很多其他的事情。每個(gè)Handler都綁定了一個(gè)線程,假設(shè)存在兩個(gè)線程ThreadA和ThreadB,并且HandlerA綁定了 ThreadA,在ThreadB中的代碼執(zhí)行到某處時(shí),出于某些原因,我們需要讓ThreadA執(zhí)行某些代碼,此時(shí)我們就可以使用Handler,我們可以在ThreadB中向HandlerA中加入某些信息以告知ThreadA中該做某些處理了。由此可以看出,Handler是Thread的代言人,是多線程之間通信的橋梁,通過(guò)Handler,我們可以在一個(gè)線程中控制另一個(gè)線程去做某事。

Handler提供了兩種方式解決我們?cè)诒疚囊婚_始遇到的問(wèn)題(在一個(gè)新線程中更新主線程中的UI控件),一種是通過(guò)post方法,一種是調(diào)用sendMessage方法。

a. 使用post方法,代碼如下:

package ispring.com.testhandler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主線程中創(chuàng)建,所以自動(dò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);
        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 id " + Thread.currentThread().getId());
                System.out.println("開始下載文件");
                //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時(shí)過(guò)程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Runnable thread id " + Thread.currentThread().getId());
                        MainActivity.this.statusTextView.setText("文件下載完成");
                    }
                };
                uiHandler.post(runnable);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

我們?cè)贏ctivity中創(chuàng)建了一個(gè)Handler成員變量uiHandler,Handler有個(gè)特點(diǎn),在執(zhí)行new Handler()的時(shí)候,默認(rèn)情況下Handler會(huì)綁定當(dāng)前代碼執(zhí)行的線程,我們?cè)谥骶€程中實(shí)例化了uiHandler,所以u(píng)iHandler就自動(dòng)綁定了主線程,即UI線程。當(dāng)我們?cè)贒ownloadThread中執(zhí)行完耗時(shí)代碼后,我們將一個(gè)Runnable對(duì)象通過(guò)post方法傳入到了Handler中,Handler會(huì)在合適的時(shí)候讓主線程執(zhí)行Runnable中的代碼,這樣Runnable就在主線程中執(zhí)行了,從而正確更新了主線程中的UI。以下是輸出結(jié)果:

這里寫圖片描述

通過(guò)輸出結(jié)果可以看出,Runnable中的代碼所執(zhí)行的線程ID與DownloadThread的線程ID不同,而與主線程的線程ID相同,因此我們也由此看出在執(zhí)行了Handler.post(Runnable)這句代碼之后,運(yùn)行Runnable代碼的線程與Handler所綁定的線程是一致的,而與執(zhí)行Handler.post(Runnable)這句代碼的線程(DownloadThread)無(wú)關(guān)。

b. 使用sendMessage方法,代碼如下:

package ispring.com.testhandler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主線程中創(chuàng)建,所以自動(dòng)綁定主線程
    private Handler uiHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    System.out.println("handleMessage thread id " + Thread.currentThread().getId());
                    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 id " + Thread.currentThread().getId());
                System.out.println("開始下載文件");
                //此處讓線程DownloadThread休眠5秒中,模擬文件的耗時(shí)過(guò)程
                Thread.sleep(5000);
                System.out.println("文件下載完成");
                //文件下載完成后更新UI
                Message msg = new Message();
                //雖然Message的構(gòu)造函數(shù)式public的,我們也可以通過(guò)以下兩種方式通過(guò)循環(huán)對(duì)象獲取Message
                //msg = Message.obtain(uiHandler);
                //msg = uiHandler.obtainMessage();

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

                //我們可以通過(guò)arg1和arg2給Message傳入簡(jiǎn)單的數(shù)據(jù)
                msg.arg1 = 123;
                msg.arg2 = 321;
                //我們也可以通過(guò)給obj賦值Object類型傳遞向Message傳入任意數(shù)據(jù)
                //msg.obj = null;
                //我們還可以通過(guò)setData方法和getData方法向Message中寫入和讀取Bundle類型的數(shù)據(jù)
                //msg.setData(null);
                //Bundle data = msg.getData();

                //將該Message發(fā)送給對(duì)應(yīng)的Handler
                uiHandler.sendMessage(msg);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

通過(guò)Message與Handler進(jìn)行通信的步驟是:
1. 重寫Handler的handleMessage方法,根據(jù)Message的what值進(jìn)行不同的處理操作
2. 創(chuàng)建Message對(duì)象
雖然Message的構(gòu)造函數(shù)式public的,我們還可以通過(guò)Message.obtain()或Handler.obtainMessage()來(lái)獲得一個(gè)Message對(duì)象(Handler.obtainMessage()內(nèi)部其實(shí)調(diào)用了Message.obtain())。
3. 設(shè)置Message的what值
Message.what是我們自定義的一個(gè)Message的識(shí)別碼,以便于在Handler的handleMessage方法中根據(jù)what識(shí)別出不同的Message,以便我們做出不同的處理操作。
4. 設(shè)置Message的所攜帶的數(shù)據(jù),簡(jiǎn)單數(shù)據(jù)可以通過(guò)兩個(gè)int類型的field arg1和arg2來(lái)賦值,并可以在handleMessage中讀取。
5. 如果Message需要攜帶復(fù)雜的數(shù)據(jù),那么可以設(shè)置Message的obj字段,obj是Object類型,可以賦予任意類型的數(shù)據(jù)?;蛘呖梢酝ㄟ^(guò)調(diào)用Message的setData方法賦值Bundle類型的數(shù)據(jù),可以通過(guò)getData方法獲取該Bundle數(shù)據(jù)。
6. 我們通過(guò)Handler.sendMessage(Message)方法將Message傳入Handler中讓其在handleMessage中對(duì)其進(jìn)行處理。
需要說(shuō)明的是,如果在handleMessage中 不需要判斷Message類型,那么就無(wú)須設(shè)置Message的what值;而且讓Message攜帶數(shù)據(jù)也不是必須的,只有在需要的時(shí)候才需要讓其攜帶數(shù)據(jù);如果確實(shí)需要讓Message攜帶數(shù)據(jù),應(yīng)該盡量使用arg1或arg2或兩者,能用arg1和arg2解決的話就不要用obj,因?yàn)橛胊rg1和arg2更高效。
程序的運(yùn)行結(jié)果如下:

這里寫圖片描述

由上我們可以看出,執(zhí)行handleMessage的線程與創(chuàng)建Handler的線程是同一線程,在本示例中都是主線程。執(zhí)行handleMessage的線程與執(zhí)行uiHandler.sendMessage(msg)的線程沒(méi)有關(guān)系。

本文主要是對(duì)Android中Handler的作用于如何使用進(jìn)行了初步介紹,如果大家想了解Handler的內(nèi)部實(shí)現(xiàn)原理,可以參見下一篇博文《深入源碼解析Android中的Handler,Message,MessageQueue,Looper》。

到此這篇關(guān)于Android Handler的使用詳解的文章就介紹到這了,更多相關(guān)Android Handler的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論