Handler机制源码分析(异步一)

   一.场景

           Handler 机制是Android异步消息的核心(线程间通信), 其实Handler不局限于子线程与主线程(UI线程) ,我们完全可以创建一个子线程,然后初始化Looper,Handler,我们可以通过Handler在其他线程(包括主线程)往该子线程发送消息。例如系统帮我们封装好的--> HandlerThread (一个封装好Looper,MessageQueueu的线程(不死线程)),发送消息给子线程有什么好处呢, 这里拿HandlerThread说明,现在有个场景, 我们都知道UI线程不能做耗时的操作, 会影响程序的性能,用户体验,所以耗时(IO,网络,数据库操作)操作全部往子线程堆。怎么解决呢, 我们可以创建一个子线程HandlerThread, 拿到该线程的Looper--HandlerThread.getLooper(),然后可以new Handler(Looper).post(Runnable r)往该线程发送消息, 然后就会在该子线程执行消息;

  二. 重要的概念(Handler , Looper,MessageQueue,Message)

          Handler   看看官方是怎么说的吧  A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue ,简单说Handler主要的作用就是往MessageQueue插入一个消息

     Looper --Looper作用是从MessageQueue读取Message,然后交给Handler处理消息,另外Looper还可以保证线程不死

    MessageQueue 消息队列, 链表插入和删除方便, 主要用于存放消息(Message) 

    Message 消息(消息会包装发送Message的Handler,详细见下面 一一源码验证)

 三.源码解析(UI线程应用Handler机制)

<span style="color:#3333ff;">	</span>private static final TaskHandler TASK_HANDLER = new TaskHandler();
	public void doClick(View v) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Message message = Message.obtain();
				message.arg1 = ++i;
				TASK_HANDLER.sendMessage(message);
			}
		}).start();
		
	}

	static final class TaskHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			Log.i("Info", "message:"+msg.arg1);
		}
	}

     上面代码很简单, 估计大家都会, 在子线程中睡三秒, 然后往UI线程发送一个消息。很简单就完成了一个子线程和UI线程的通信,并且没有任务问题, 其实,Android在背后为我们做了很多封装和处理了,才可以往UI线程发送消息, 不信见如下:

	new Thread(new Runnable() {


			@Override
			public void run() {
				handler = new Handler();
				handler.sendEmptyMessage(0x33);
			}
		}).start();

	}

   在子线程初始化一个Handler,然后发送一个消息。结果:

07-10 15:12:53.251: E/AndroidRuntime(5296): FATAL EXCEPTION: Thread-158
07-10 15:12:53.251: E/AndroidRuntime(5296): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-10 15:12:53.251: E/AndroidRuntime(5296): at android.os.Handler.<init>(Handler.java:121)
07-10 15:12:53.251: E/AndroidRuntime(5296): at com.example.just_intentservice.MainActivity$1.run(MainActivity.java:25)
07-10 15:12:53.251: E/AndroidRuntime(5296): at java.lang.Thread.run(Thread.java:856)

抛出异常了,大概意思是说没有,不能在没有初始化Looper的线程去创建Handler;

 点进Handler源码查看下,

 public Handler() {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    }
  先会到当前线程查找Looper ,如果没有Looper就会抛出上面的异常。所以不能在没有初始化Looper的线程初始化Handler,UI线程之所以可以初始化Handler,我们见以下,Android UI线程是如何初始化Looper吧...

    Java应用程序入口都是从mian函数, Android也不例外,只不过被SDK用hide标识着,我们无法查看,看下Android,UI线程是如何玩转Handler机制的,在ActivityThread里面main函数代码如下

public static void main(String[] args) {
    //******省略若干代码****//
        Looper.prepareMainLooper();//初始一个Looper
        Looper.loop();//开始无限循环读取消息
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
   我们代码追踪看Android是如何初始换Looper,在Looper类里面

 public static void prepareMainLooper() {//初始化MainLooper也是UI线程的Looper
        prepare();
        setMainLooper(myLooper());
    }
 public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
private Looper() {
        mQueue = new MessageQueue();//初始化一个MessageQueue
        mThread = Thread.currentThread();
    }

  

  先判断下当前线程是否存在Looper,存在抛出异常, 也就是说一个线程自能有一个Looper, 如果没有,则new出一个Looer(在构造方法里面初始化了一个对应的MessageQueue), 然后通过sThreadLocal保存起来, ThreadLocal用于线程保存对象 。然后来看下

  setMainLooper(myLooper()); 
public static Looper myLooper() {
        return sThreadLocal.get();
    }
 private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
可以看出myLooper就是从当前线程中拿出Looper,然后返回,setMainLooper(),就更简单了,其实就是保存个全局looper。
  就这样UI线程就吧Looper初始化完成了,所以Handler可以正常的在主线程初始化;

下面我们来走一把完整的Handler机制通信流程     

handler.sendEmptyMessage(0x33);      <span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">----子线程</span>
 public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);// 包装下时间调用两个参数<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">sendEmptyMessageDelayed方法</span>

    }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();//获取一个Message
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;//如果时间小于0则赋值为0
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {//对消息队列判空
            msg.target = this;//this表示当前handler
            sent = queue.enqueueMessage(msg, uptimeMillis);//进入队列
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }
     子线程的handler发送一个简单的空消息到UI线程, 要经过不断的包装, 可以看出,最后都是包装Message然后进入队列(MessageQueue)其实post(Runnable r)最后也是经过包装成Message对象,发送到队列, 这里就不做说明了, 详细见源码。 我们知道Looper在UI线程初始化完成就开始无限死循环读取MessageQueue里面消息,我们看Looper是如何读取和处理消息的:

public static void loop() {
        Looper me = myLooper();//获取当前线程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;   //获取Looper里面的消息队列     
        while (true) { //死循环读取
            Message msg = queue.next(); // might block 从队列里面拿到消息
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }      
              //省略一些代码
                msg.target.dispatchMessage(msg);//从message中拿到handler,然后调用<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">dispatchMessage</span>
                msg.recycle();//最终销毁消息
            }
        }
    }
  Handler类里面的dispatchMessage如下:

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//callback其实是一个Runnable对象, 一般post(Runnable r)是执行
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//回调handlerMessage函数--一般该方法由我们继承去实现的
        }
    }
private final void handleCallback(Message message) {
        message.callback.run();//执行run方法体
    }
   说明下, 子线程handler.sendEmptyMessage()当Message到达MessageQueue,这里就是子线程切换到UI线程了, 因为MessageQueue跑在UI线程,上面代码可以看出,Looper会不断的在MessageQueue里面读取消息,然后读到的message为空的化return掉, 其实里面还有一些唤醒机制(保证UI线程的不阻塞,当消息过来了才唤醒,这里不细说)如果message不为空,则从message中取出handler,然后调用handler的dispathMessage(Message message)方法来方法消息, 如果消息为post(Runnable r)则走handlerCallback,否则走handleMessage(msg),整个消息通信结束。


   通过源码分析, 我们完全可以创建一个不死子线程,然后在主线程中往子线程发送消息:

private Handler handler;

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

		new Thread(new Runnable() {

			@Override
			public void run() {
				Looper.prepare();
				handler = new Handler(Looper.myLooper()) {
					@Override
					public void handleMessage(Message msg) {
						if (msg.what == 0x88) {
							Log.i("Info", "i am sub");
						}

					}
				};
				Looper.loop();
				
			}
		}).start();

	}
public void doClick(View v) {

		handler.sendEmptyMessage(0x88);

	}

最后总结:Handler往MessageQueue插入Message,Looper往messageQueue里面读取message,最后都是交给Handler分发处理。













      


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值