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

Android Handler的使用詳解

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

在Android開發(fā)中,我們經(jīng)常會遇到這樣一種情況:在UI界面上進行某項操作后要執(zhí)行一段很耗時的代碼,比如我們在界面上點擊了一個”下載“按鈕,那么我們需要執(zhí)行網(wǎng)絡(luò)請求,這是一個耗時操作,因為不知道什么時候才能完成。為了保證不影響UI線程,所以我們會創(chuàng)建一個新的線程去執(zhí)行我們的耗時的代碼。當(dāng)我們的耗時操作完成時,我們需要更新UI界面以告知用戶操作完成了。所以我們可能會寫出如下的代碼:

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

上面的代碼演示了單擊”下載“按鈕后會啟動一個新的線程去執(zhí)行實際的下載操作,執(zhí)行完畢后更新UI界面。但是在實際運行到代碼MainActivity.this.statusTextView.setText(“文件下載完成”)時,會報錯如下,系統(tǒng)崩潰退出:
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不是線程安全的,在Android應(yīng)用啟動時,會自動創(chuàng)建一個線程,即程序的主線程,主線程負(fù)責(zé)UI的展示、UI事件消息的派發(fā)處理等等,因此主線程也叫做UI線程,statusTextView是在UI線程中創(chuàng)建的,當(dāng)我們在DownloadThread線程中去更新UI線程中創(chuàng)建的statusTextView時自然會報上面的錯誤。Android的UI控件是非線程安全的,其實很多平臺的UI控件都是非線程安全的,比如C#的.Net Framework中的UI控件也是非線程安全的,所以不僅僅在Android平臺中存在從一個新線程中去更新UI線程中創(chuàng)建的UI控件的問題。不同的平臺提供了不同的解決方案以實現(xiàn)跨線程跟新UI控件,Android為了解決這種問題引入了Handler機制。

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

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

Handler提供了兩種方式解決我們在本文一開始遇到的問題(在一個新線程中更新主線程中的UI控件),一種是通過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)建,所以自動綁定主線程
    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秒中,模擬文件的耗時過程
                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();
            }
        }
    }
}

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

這里寫圖片描述

通過輸出結(jié)果可以看出,Runnable中的代碼所執(zhí)行的線程ID與DownloadThread的線程ID不同,而與主線程的線程ID相同,因此我們也由此看出在執(zhí)行了Handler.post(Runnable)這句代碼之后,運行Runnable代碼的線程與Handler所綁定的線程是一致的,而與執(zhí)行Handler.post(Runnable)這句代碼的線程(DownloadThread)無關(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)建,所以自動綁定主線程
    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秒中,模擬文件的耗時過程
                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();
            }
        }
    }
}

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

這里寫圖片描述

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

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

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

相關(guān)文章

最新評論