Android Handler的基本使用

轉載請註明出處:http://blog.csdn.net/vnanyesheshou/article/details/72677227

本文已授權微信公衆號 fanfan程序媛 獨家發佈 掃一掃文章底部的二維碼或在微信搜索 fanfan程序媛 即可關注

最近看了一篇關於Handler的博客,感覺寫的很不錯,涉及了Handler的基本使用方法,及Handler、Looper、MessageQueue的深入分析。
原文地址:http://blog.csdn.net/zrf1335348191/article/details/50716983
雖然平常用Handler比較多,但是並沒有深刻理解其內部原理,知其然而不知其所以然,爲了知其所以然,還是應該好好研究下Handler的。先整理一篇Handler的基本使用。
Andorid是單線程模型的,當一個程序第一次啓動時,Android會同時啓動一個主線程(Main Thread),主線程主要負責處理與UI相關的事件。Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
在開發中,我們經常會需要做一些耗時的操作:比如下載圖片、打開網頁、下載視頻等。如果將這些耗時的操作放在主線程(UI線程),長時間的阻塞導致應用ANR。必然應該將這些操作放在子線程中處理,這些操作處理過程中,我們需要更新UI界面以告知用戶現在具體的進度、狀態等信息。
Handler提供了三種方式解決我們這個問題(在一個新線程中更新主線程中的UI控件),一種是調用sendMessage方法,一種是通過post方法,另一種是obtainMessage。


1 sendMessage
package cn.vn.hand;

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

public class MainActivity extends Activity implements OnClickListener {
    private final String TAG = "MainActivity";
    private TextView mTipTv;
    private Button mDownloadBt;
    private boolean isDownloading = false;
    public final int MSG_DOWN_FAIL = 1;
    public final int MSG_DOWN_SUCCESS = 2;
    public final int MSG_DOWN_START = 3;
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch(msg.what){
            case MSG_DOWN_FAIL:
                mTipTv.setText("download fial");
                break;
            case MSG_DOWN_SUCCESS:
                mTipTv.setText("download success");
                break;
            case MSG_DOWN_START:
                mTipTv.setText("download start");
                break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mTipTv = (TextView) findViewById(R.id.tv_tip);
        mDownloadBt = (Button) findViewById(R.id.bt_start);
        mDownloadBt.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.bt_start:
            if(!isDownloading){
                new MyThread().start();
            }
            break;
        default:
            break;
        }
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            isDownloading = true;
            Log.d(TAG,"MyThread start run");
            //發送消息給mHander
            mHandler.sendEmptyMessage(MSG_DOWN_START);
            try { //讓線程睡眠3s。
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message msg = new Message();
            msg.what = MSG_DOWN_SUCCESS;
            //msg.arg1 = 111;  可以設置arg1、arg2、obj等參數,傳遞這些數據
            //msg.arg2 = 222; msg.obj = obj;
            mHandler.sendMessage(msg);
            isDownloading = false;
            Log.d(TAG,"MyThread stop run");
        }
    }
}

使用步驟:

  1. 在UI線程中創建handler對象mHandler,並實現handleMessage方法,根據Message的what值進行不同的處理操作。
  2. 創建Message對象
  3. 根據需要設置Message的參數,Message.what一般都是必要的,用來區分不同的Message,做出不同的操作。還可以設置Message兩個int型字段arg1、arg2。當然除了這簡單的數據外,還可以設置攜帶複雜數據,其obj字段類型爲Object類型,可以爲任意類類型的數據。也可以通過Message的setData方法設置Bundle類型的數據,可以通過getData方法獲取該Bundle數據。
  4. mHandler.sendMessage(Message)方法將Message傳入Handler中的消息隊列中,然後handleMessage中對消息進行處理。

創建Handler的線程和其handleMessage運行的線程是同一線程,mHandler是在主線程中創建的,所以其handleMessage方法也是在主線程中運行。mHandler.sendMessage(Message)可以在主線程中也可以在子線程中,發送消息的線程與其執行的線程沒有聯繫,最終都會在其創建的線程中處理這些消息。
sendMessage還有許多變形,可以發送空message(只攜帶what參數)、延時消息、定時消息等。使用方式很簡單。
這裏寫圖片描述
對於延時、定時消息,有時我們可能會想取消消息,這就可以通過removeMessages(int what)、或removeMessages(int what, Object object)、removeCallbacksAndMessages(Object token)將指定消息移除。

2 post
package cn.vn.hand;

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

public class MainActivity extends Activity implements OnClickListener {
    private final String TAG = "MainActivity";
    private TextView mTipTv;
    private Button mDownloadBt;
    private boolean isDownloading = false;
    public final int MSG_DOWN_FAIL = 1;
    public final int MSG_DOWN_SUCCESS = 2;
    public final int MSG_DOWN_START = 3;
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            Log.d(TAG, "handlemessage what="+msg.what);
            switch(msg.what){
            case MSG_DOWN_FAIL:
                mTipTv.setText("download fial");
                break;
            case MSG_DOWN_SUCCESS:
                mTipTv.setText("download success");
                break;
            case MSG_DOWN_START:
                mTipTv.setText("download start");
                break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mTipTv = (TextView) findViewById(R.id.tv_tip);
        mDownloadBt = (Button) findViewById(R.id.bt_start);
        mDownloadBt.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.bt_start:
            if(!isDownloading){
                //new MyThread().start();
                new postThread().start();
            }
            break;
        default:
            break;
        }
    }

    class postThread extends Thread{
        @Override
        public void run() {
            isDownloading = true;
            Log.d(TAG,"run threadid="+Thread.currentThread().getId()+
                    ",name="+Thread.currentThread().getName());
            try { //讓線程睡眠3s。
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG, "Runnable threadid="+Thread.currentThread().getId()
                            +",name="+Thread.currentThread().getName());
                    //更新ui
                    mTipTv.setText("download success");
                }
            });
            isDownloading = false;
        }
    }
}

Handler的post方法參數爲Runnable對象,mHandler是在主線程中創建的,所以Runnalbe會在主線中運行(與Runnable創建的線程無關、與mHandler.post方法調用的線程無關)。

05-26 03:49:20.877: D/MainActivity(1297): run threadid=99,name=Thread-99
05-26 03:49:23.877: D/MainActivity(1297): Runnable threadid=1,name=main

有打印可確定Runnable的run方法確實是在主線程中運行的,可以更新UI。
post方法與sendMessage類似也有多個相似方法:
這裏寫圖片描述
post延時、定時處理Runnable也可以進行取消,可以通過removeCallbacks(Runnable r)、removeCallbacks(Runnable r, Object token)、removeCallbacksAndMessages(Object token)方法進行取消。


3 obtainMessage

obtainMessage與sendmessage類似,也可以看成一種。

class obtainThread extends Thread{
    @Override
    public void run() {
        isDownloading = true;
        mHandler.obtainMessage(MSG_DOWN_START).sendToTarget();
        try { //讓線程睡眠3s。
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mHandler.obtainMessage(MSG_DOWN_SUCCESS).sendToTarget();
        isDownloading = false;
    }
}

mHandler.obtainMessage()生成Message對象,此對象攜帶其target對象,直接調用sendToTarget方法就可以將該消息發送到mHandler對應的消息隊列中,然後在mHandler的handleMessage中進行處理。使用和sendMessage類型,都是發送Message對象。

這就差不多說完了,handler的一些基本使用方法,下篇分析下其原理。

歡迎掃一掃關注我的微信公衆號,定期推送優質技術文章:

這裏寫圖片描述

發佈了129 篇原創文章 · 獲贊 270 · 訪問量 97萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章