处女男学Android(五)---Handler完结篇之Handler的post方法详解



前言



上一篇blog(处女男学Android(四)---Handler进阶篇之Handler、Looper、MessageQueue工作机制)介绍了关于Handler、Looper、MessageQueue的工作原理和源码分析,已经对Handler的整体工作过程有了一个较为深刻的了解,那么关于Handler还有一些重要的知识点,本篇就具体记录一下关于Handler的post方法的用法和工作原理。



一、boolean android.os.Handler.post(Runnable r)方法简介


google对post方法的介绍是这样的:



红色方框中的解释大致是这样的:
将Runnable对象添加到消息队列中,这个Runnable对象将运行在一个线程中,即Handler所依附的线程中。

关于参数的解释很简单:参数r将会被执行。

关于返回值的解释大致是这样的:
如果返回true,就说明了Runnable对象成功放进了消息队列中。如果返回false则表示失败,通常是因为该消息队列正在被处理。

看了上面的解释相信大家都会有疑问,我看完之后列出了以下几个问题:
1.上面说执行post方法是将Runnable对象放到消息队列,但消息队列放的都应该是消息对象,即:Message对象,这    里怎么成Runnable对象了?
2.参数传来的Runnable对象将会被执行,究竟是什么时候执行呢?怎么执行?
3.Looper从消息队列中取出放进去的Runnable对象之后,会做什么?

下面通过一个小例子先看一下post方法的用法和效果。这个例子和第二篇blog中的例子一模一样,即通过点击按钮来改变UI,而且是通过post方法去发送消息。

Layout代码(third.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >

    <TextView
        android:id="@+id/tv_textView1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:gravity="center_horizontal|center_vertical"
        android:text="Text Example"
        android:textSize="15sp" />

    <Button
        android:id="@+id/btn_button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Change Text" />

</LinearLayout>


Activity代码:

package com.example.handlertest;

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

public class ThirdActivity extends Activity {

	private Button button;
	private TextView textView;

	private Handler handler = new Handler();  //在主线程定义一个handler

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.third);

		textView = (TextView) findViewById(R.id.tv_textView1);

		button = (Button) findViewById(R.id.btn_button1);
		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				MyThread mThread = new MyThread();
				mThread.start();  //启动MyThread
			}
		});
	}

	class MyThread extends Thread {
		@Override
		public void run() {
			handler.post(new Runnable() {  //通过post方法发送消息
				@Override
				public void run() {
					String currentThreadName = Thread.currentThread().getName();  
					System.out.println("当前线程名称为--->" + currentThreadName);  //打印线程名

					try {
						Thread.sleep(1000 * 2); // 模拟一个2s的耗时任务
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					String s = "\n has been changed !";
					textView.setText(textView.getText() + s);
					textView.setTextColor(Color.RED); // 更改UI
				}
			});
		}
	}

}


下面看一下运行效果:



通过上面的效果图我们不难发现:
Runnable中的代码运行在了主线程,所以在主线程中修改UI也自然没问题了,但这其中的原因以及上面的三个问题我们有必要搞清楚,下面我们结合源码简单分析一下。


二、 源码分析


首先,我们自然应该先看看post方法到底都干了些什么:
public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }


这里我们需要再看看getPostMessage这个方法:
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //新建了一个Message对象
        m.callback = r;  //将传进来的Runable对象赋给Message对象的callback属性,Message的callback属性本身就是Runnable对象
        return m;  //返回赋值之后的Message对象
    }

对比一下上一篇blog中提到的handler的sendMessage源码:
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }


到这里我们可以发现post方法和sendMessage方法差不多,都是给消息队列中发送一条消息而已,只不过sendMessage方法中,Message对象是我们自己创建好传进去的,而post方法则是在内部创建一个Message对象,只是将我们传进去的Runnable对象赋值给Message对象的callback属性之后之后再返回而已。下面问题来了:挖掘机技术到底哪家强?开个玩笑。问题应该是这样的,Looper取出了Message对象之后又做了什么?


回顾上一篇blog,其实是通过在loop()方法中,循环到当前的Message对象时,调用msg.target.dispatchMessage(msg)方法,target也就是一个handler对象,其实就是调用handler的dispatchMessage(msg)这个方法来处理取出的消息对象的,那么这里我们再贴一遍这个方法的源代码:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);  //当Message对象的callback属性不为空时则调用handleCallback(msg)方法
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


现在看到这段应该大致明白怎么回事了,上一篇我们没有给callback属性赋值,所以会执行handleMessage方法,但现在我们调用post方法的同时就将一个Runnable对象赋值给了Message的callback属性,所以这里我们自然会走if,下面看看handleCallback(msg)这个方法都做了什么:
private static void handleCallback(Message message) {
        message.callback.run();  
    }


到了这里想必大家都已经明白了,直接调用了Runnable的run()方法来运行。有线程基础的话应该很清楚,run()方法和start()方法的区别就在于run()方法不会开启新线程,而是在原有的线程中运行。下面针对开头提出的问题作出解释和更正:
1.上面说执行post方法是将Runnable对象放到消息队列,但消息队列放的都应该是消息对象,即:Message对象,这    里怎么成Runnable对象了?
其实最终放到消息队列的还是Message对象,只是将传进去的Runnable对象赋值给了Message对象的callback属性之后再放到消息队列的。

2.参数的Runnable对象将会被执行,究竟是什么时候执行呢?
看完了最后的一段源码,很明显是Looper取出Message对象之后交给了Handler的handleCallback(msg)方法去处理,也就是在这个方法中通过直接调用run()方法执行了Runnable中的代码。

3.Looper从消息队列中取出放进去的Runnable对象之后,会做什么?
这里需要更正的是放进去的不是Runnable对象,而是Message对象,原因见1。至于做了什么,原因见2。

解释完了之前的问题,也了解了post方法,我们发现这是发送消息的另一种方式,比通过handler的sendMessage要简单一些,并不用继续在主线程中去重写handleMessage(msg)方法,因为Runnable的代码块本身就运行在主线程中。


三、 总结


本篇blog主要记录了与handler相关的post方法的使用方法和工作原理,对通过handler发送消息也多了一种选择,关于handler的所有内容到这里基本已经记录完毕,虽然很多地方我个人的理解还不是很到位,但通过阅读源码确实感觉到有很大的收获,剩下的就是时间和努力的问题(鄙人较笨)。后续的blog准备学习记录一些UI方面的知识,比如自定义View、适配器等等,我会继续努力的,争取早日拿下Android!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值