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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

使用步骤:

  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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

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
  • 1
  • 2
  • 3

有打印可确定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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

mHandler.obtainMessage()生成Message对象,此对象携带其target对象,直接调用sendToTarget方法就可以将该消息发送到mHandler对应的消息队列中,然后在mHandler的handleMessage中进行处理。使用和sendMessage类型,都是发送Message对象。

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

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/80690820
今日推荐