我是靠谱客的博主 善良台灯,最近开发中收集的这篇文章主要介绍Handler消息(Message)传递机制详解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


一、线程Thread与AsyncTask、Handler关系

    我们创建的ServiceActivity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程google为了保障用户体验,规定主线程(UI界面)5s没响应就报错—ANR异常(Applicationnot Responding),所以较为耗时的操作一般需要开启子线程完成,即Thread 

   对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可,或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,因为Google做了限定,只有主线程才可以与UI界面进行交互只有主(进度条除外),即刷新UI元素,所以仅仅使用Thread无法将运行结果刷新到UI界面,所以android搞了一个消息机制Handler,利用Handler建立子线程与主线程UI之间的交互联系。

    Handler工作模式大致是:

    子线程:在一个线程的run方法中,将运行结果存放到Message,然后调用handler对象的 postMessagesendMessage方法来实现将Message发送到主线程即UI线程,每一个消息都有一个标示Message.what以方便辨识。

    UI线程(主线程):主线程通过Handle处理发送到主线程的Message,即重写Handle的HandleMessage函数,在其中刷新UI。

    所以,但仅通过Thread+Handler实现子线程与主线程之间的交互还不够,如何确保消息队列源源不断的被取出?这里需要 Looper循环去取消息,其实Android中每一个Thread都跟LooperLooper可以帮助Thread维护一个消息队列。因此,准确说,应该是Thread+Handler+Looper,三者结合共同实现消息传递机制。

    另外,Android为了方便开发,将三者的实现过程整合到AsyncTask中,故AsyncTask可以简单的开启线程并与主线程进行交互。


二、Handler消息(Message)传递机制

    Handler具有唯一性,即一旦创建Handler,Handler将与该线程进行绑定,管理该线程的消息队列,因此,Handler主要有两个功能,以主线程中的Handler为例:

    1)传递其他线程中的消息进队列。利用handler.sendMessage()或handler. postMessage()管理消息在队列中的位置

    2)队列中的消息与UI交互。利用Handler.HandleMessage()函数与UI交互。

    一个完整的消息传递机制如下图,有三个要素,分别是Message消息,MessageQueue消息队列,Looper循环泵。           Handler负责将子线程的Message进入队列排序确定位置,并将Looper循环泵出来的队首消息与主线程交互。具体过程见下面的源码过程分析。


三、Handler消息传递机制过程源码分析(以Activity主线程为例)

1)创建 new Handler()

Handler()的构造函数如下:

public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

所以,创建Handler,即自动绑定了一个Looper以及一个消息队列mQueue,以及一个回调接口Callback(如下)handleMessage()是一个空函数,承担与UI交互功能,所以需要在主线程中重写。

public interface Callback {
        public boolean handleMessage(Message msg);
    }

2)获取Message(Message是一个final类),Android推荐用Message.obtain()或者Handler.obtainMessage()获取Message

public final Message obtainMessage()
    {
        return Message.obtain(this);
}

obtain(this),this即为Handler,Message中对应的obtain()函数如下:

public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

关键是这句,m.target = h,赋值消息传送目标即为该Handler,而obtain()函数如下:如果消息池中不为空,则直接拿来使用,否则创建Message。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

3)sendMessage或postMessage将Message插入队列恰当位置,但二者最后都是调用函数sendMessageAtTime,并通过enqueueMessage函数将信息MessagemQueue中确定位置、排队。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

继续寻找源码,发现enqueueMessage最终会调用MessageQueue中的enqueueMessage,即

boolean enqueueMessage(Message msg, long when) {
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

依据处理时间when进行排队,when越小,Message在队列中越靠前。


4Looper利用循环泵loop()循环抽取队列中的Message并处理界面交互

public static void loop() {

        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
        }
    }

分析代码,很容易发现Loop是一个循环,直到消息队列为空退出。若存在Message,则通过msg.target.dispatchMessage(msg)分发消息,看Message源码发现,msg.target即为Handler,所以查看Handler. dispatchMessage(msg)源码如下:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

最终出现handleMessage(msg),处理与主线程的交互,这需要在主线程中重写。

    整个源码过程示意图如下:

四、例子如下

很简单的例子,模拟下载并刷新下载进度及结果

package com.tz.anr;
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.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	protected static final int UPDATE_PROGRESS = 0;
	protected static final int UPDATE_FINISH = 1;
	private static final int UPDATE_ERROR = 2;
	private ProgressBar pb;
	private TextView tv_progress;
	
	Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			//2.收到子线程发过来的消息
			//该方法是执行在主线程里面,所以可以直接操作UI
			switch (msg.what) {
			case UPDATE_PROGRESS:
				int progress = msg.arg1;
				//更新进度条
				pb.setProgress(progress);
				tv_progress.setText(progress+"%");
				break;
			case UPDATE_FINISH:
				Toast.makeText(MainActivity.this, "下载完毕!", 0).show();
				break;
			case UPDATE_ERROR:
				Toast.makeText(MainActivity.this, "下载出错!", 0).show();
				break;
			default:
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		pb = (ProgressBar)findViewById(R.id.pb);
		pb.setMax(100);
		tv_progress = (TextView)findViewById(R.id.tv_porgress);
	}

	public void click(View v){
		/**
		 * 开子线程:AsyncTask、Thread
		 * 模拟下载。
		 */
		try {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int progress = 0;
					while(progress<100){
						try {
							Thread.sleep(500);
							progress += 10;
							//1.子线程里面发送消息给主线程。
//							Message msg = new Message();
							Message msg = handler.obtainMessage();
							//what:代表是谁发出的消息--标识符id
							msg.what = UPDATE_PROGRESS;
							//存放数据arg1,arg2,obj
//							msg.arg2
//							msg.obj = new Student();
							msg.arg1 = progress;
							handler.sendMessage(msg);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
//					Message msg = handler.obtainMessage();
//					//what:代表是谁发出的消息--标识符id
//					msg.what = UPDATE_FINISH;
//					handler.sendMessage(msg);
					
					Message msg = handler.obtainMessage();
					msg.sendToTarget();//这种方法只能使用handler.obtainMessage();
//					handler.sendEmptyMessage(UPDATE_FINISH);
//					handler.sendMessageDelayed(msg, delayMillis)//delayMillis延迟时间,多久以后发送该消息
//					handler.sendMessageAtTime(msg, uptimeMillis)//uptimeMillis:可以在某个时间点发送该消息
				}
			}).start();
		} catch (Exception e) {
			e.printStackTrace();
//			Message msg = handler.obtainMessage();
//			//what:代表是谁发出的消息--标识符id
//			msg.what = UPDATE_ERROR;
//			handler.sendMessage(msg);
			handler.sendEmptyMessage(UPDATE_ERROR);
		}
	}
	
}





最后

以上就是善良台灯为你收集整理的Handler消息(Message)传递机制详解的全部内容,希望文章能够帮你解决Handler消息(Message)传递机制详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部