handler消息传递机制

不管是基于监听的事件处理机制还是基于回调的事件处理机制都属于是响应的事件,关于响应的事件响应就这两种。

本节给大家讲解的 是Activity中UI组件中的信息传递Handler,相信很多朋友都知道,Android为了线程安全,并不允许我们在UI线程外操作UI;很多时候我们做界面刷新都需要通过Handler来通知UI组件更新!除了用Handler完成界面更新外,还可以使用runOnUiThread()来更新,甚至更高级的事务总线,当然,这里我们只讲解Handler,什么是Handler,执行流程,相关方法,子线程与主线程中中使用Handler的区别等!

1.学习路线图:

2.Handler类的引入:

实际上wpf或者直接是桌面应用程序上都讲究的是跨线程访问的安全性,之前做的一个桌面应用程序里面实际上就使用了委托的方式来实现跨线程的访问。

3.Handler的执行流程图:

流程图解析: 相关名词

  • UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
  • Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
  • Message:Handler接收与处理的消息对象
  • MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
  • Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!

简单点说:

当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!

实际上对于InputDispatcher的消息分发机制是不是也是这样的呢?感觉很多相似之处,尤其是什么looper还有就是Messagequeue之类的。

4.Handler的相关方法:

  • void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
  • sendEmptyMessage(int what):发送空消息
  • sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
  • sendMessage(Message msg):立即发送信息
  • sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
  • final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息

5.Handler的使用示例:

1)Handler写在主线程中

在主线程中,因为系统已经初始化了一个Looper对象,所以我们直接创建Handler对象,就可以进行信息的发送与处理了!

代码示例: 简单的一个定时改变textview的信息,通过Timer定时器,定时修改textview的显示的内容,具体的代码如下所示:

xml文件:

   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="按钮"
       android:id="@+id/my_button1"
       />
   <TextView
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       android:id="@+id/this_textview"
       android:text="hello it is me"
       />
main_activity.java

public class MainActivity extends AppCompatActivity {
    private TextView my_view; // 不可以在此使用findviewbyid获取到xml上面的对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        my_view = (TextView)findViewById(R.id.this_textview) ;
        //使用定时器,每隔200毫秒让handler发送一个空信息
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                my_handler.sendEmptyMessage(0x1); // 定义在后面也可以被识别
            }
        },0,2000);
    }
    final Handler my_handler = new Handler() { // 必须是final对象
        //重写handleMessage方法,根据msg中what的值判断是否执行后续操作
        public void handleMessage(Message msg){
            if (msg.what==0x1){
                my_view.setText("hell0");
            }
        }
    };

}

2)Handler写在子线程中

如果是Handler写在了子线程中的话,我们就需要自己创建一个Looper对象了!创建的流程如下:

1 )直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue; 

2 )创建Handler对象,重写handleMessage( )方法就可以处理来自于其他线程的信息了! 3

 )调用Looper.loop()方法启动Looper

使用示例: 输入一个数,计算后通过Toast输出在这个范围内的所有质数

xml文件如下:

   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="按钮"
       android:id="@+id/my_button1"
       android:onClick="button_click"
       />
   <EditText
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       android:id="@+id/this_textview"
       android:inputType="number"
       />

java文件如下:

public class MainActivity extends AppCompatActivity {
    EditText my_edit;
    my_thread thread_object;
    static final String UPPER_NUM = "upper";
    class my_thread extends Thread{
         public Handler my_handler; // 一定是一个需要的内部成员,后面好使用.my_handler来指向他
        public void run(){
            Looper.prepare(); //  创建looper以及相应的消息队列
             my_handler = new Handler(){
              public void handleMessage(Message msg){// 子线程中处理消息
                  if (msg.what==0x12){
                     // int upper = msg.getData().getInt(UPPER_NUM);
                      int upper = msg.getData().getInt(UPPER_NUM);
                      // 是java的一个类。也就是int的包装类。int是基本数据类型,integer是引用类型,包含很多属性和方法,而int只是一个值,没有其他的任何方法和属性了
                      List<Integer> nums = new ArrayList<Integer>();
                      // 计算从2开始、到upper的所有质数
                      outer:
                      for (int i = 2 ; i <= upper ; i++)
                      {
                          // 用i处于从2开始、到i的平方根的所有数
                          for (int j = 2 ; j <= Math.sqrt(i) ; j++)
                          {
                              // 如果可以整除,表明这个数不是质数
                              if(i != 2 && i % j == 0)
                              {
                                  continue outer;
                              }
                          }
                          nums.add(i);
                      }
                      // 使用Toast显示统计出来的所有质数
                      Toast.makeText(MainActivity.this , nums.toString()
                              , Toast.LENGTH_LONG).show();
                  }
              }
            };
            Looper.loop(); // 需要调用该函数来启动looper,根据流程是先重写handleMessage函数,然后再开始使用Looper.loop函数来启动looper
        }
    }
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        my_edit = (EditText)findViewById(R.id.this_textview);
        thread_object = new my_thread();
        thread_object.start();
    }
    public void button_click(View view){
        Message message = new Message();
        message.what = 0x12;
        Bundle bundle = new Bundle();// bundle类主要用来携带数据用
        bundle.putInt(UPPER_NUM,Integer.parseInt(my_edit.getText().toString())); // 往bandule里面扔数据
        message.setData(bundle);// 将bundle扔进message里面
        thread_object.my_handler.sendMessage(message); // 使用handler将message扔出去
    }
}

3)Looper类分析

之前单纯的在应用上来看了Looper,现在更加深入一步来看Looper。

我们以Looper使用的一个常见例子来分析这个Looper类。
[-->例子1]
//定义一个my_thread。
class my_thread extends Thread{
public Handler mHandler;
public void run(){
//①调用prepare。
Looper.prepare();
……
//②进入消息循环。
Looper.loop();
}
}
//应用程序使用LooperThread。
{
……
new my_thread().start();//启动新线程,线程函数是run
}
上面的代码一共有两个关键调用(即①和②),我们对其逐一进行分析。

1.准备好了吗
第一个调用函数是Looper的prepare函数。它会做什么工作
呢?其代码如下所示:
[-->Looper.java]
public static final void prepare(){
//一个Looper只能调用一次prepare。
if(sThreadLocal.get()!=null){
throw new RuntimeException("Only one Looper may be created per
thread");
}
//构造一个Looper对象,设置到调用线程的局部变量中。
sThreadLocal.set(new Looper());//构造对象就需要跑到他的构造函数里面去
}
//sThreadLocal定义
private static final ThreadLocal sThreadLocal=new ThreadLocal();

ThreadLocal是Java中的线程局部变量类,全名应该是Thread Local Variable。我觉得它的实现和操作系统提供的线程本地存储
(TLS:Thread Local Storage)有关系。总之,该类有两个关键函数:
set:设置调用线程的局部变量。
get:获取调用线程的局部变量。
注意 set/get的结果都和调用这个函数的线程有关。ThreadLocal类可参考JDK API文档或Android API文档。

根据上面的分析可知,prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是my_thread的run线程。先看看Looper对象的构造,其代码如下所示:
[-->Looper.java]
private Looper(){
//构造一个消息队列。
mQueue=new MessageQueue();
mRun=true;
//得到当前线程的Thread对象。
mThread=Thread.currentThread();
}

prepare函数很简单,它主要干了一件事:
在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV(实际上是数据存储的一种相关的协议)中。而Looper对象内部封装了一个消息队列。
也就是说,prepare函数通过ThreadLocal机制,巧妙地把Looper和调用线程关联在一起了。要了解这样做的目的是什么,
需要再看第二个重要函数。

2.Looper循环
代码如下所示:
[-->Looper.java]
public static final void loop(){
Looper me=myLooper();//myLooper返回保存在调用线程TLV中的Looper对象。
//取出这个Looper的消息队列。
MessageQueue queue=me.mQueue;
while(true){
Message msg=queue.next();
//处理消息,Message对象中有一个target,它是Handler类型。
//如果target为空,则表示需要退出消息循环。
if(msg!=null){
if(msg.target==null){
return;
}
//调用该消息的Handler,交给它的dispatchMessage函数处理。
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
//myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象。
public static final Looper myLooper(){
return(Looper)sThreadLocal.get();
}
通过上面的分析会发现,Looper的作用是:
封装了一个消息队列。
Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了。
处理线程调用loop函数,处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。

可事件源又是怎么向Looper消息队列添加消息的呢?来看下一节。

.Looper、Message和Handler的关系
Looper、Message和Handler之间也存在暧昧关系,不过要比RefBase那三个简单得多,用两句话就可以说清楚:
Looper中有一个Message队列,里面存储的是一个个待处理的Message。
Message中有一个Handler,这个Handler是用来处理Message的。
其中,Handler类封装了很多琐碎的工作。先来认识一下这个

4、Handler

1.初识Handler
Handler中所包括的成员:
[-->Handler.java]
final MessageQueue mQueue;//Handler中也有一个消息队列,实际上Handler中的消息队列是指向Looper的,从而实现了操作Looper中的消息队列。
final Looper mLooper;//也有一个Looper。
final Callback mCallback;//有一个回调用的类。
这几个成员变量是怎么使用的呢?这首先得分析Handler的构造函数。Handler一共有四个构造函数,它们主要的区别是在对上面三个重要成员变量的初始化上。我们试对其进行逐一的分析。
[-->Handler.java]
//构造函数1
public Handler(){
//获得调用线程的Looper。
mLooper=Looper.myLooper();
if(mLooper==null){
throw new RuntimeException(……);
}
//得到Looper的消息队列。
mQueue=mLooper.mQueue;
//无callback设置。
mCallback=null;
}
//构造函数2
public Handler(Callback callback){
mLooper=Looper.myLooper();
if(mLooper==null){
throw new RuntimeException(……);
}
//和构造函数1类似,只不过多了一个设置callback。
mQueue=mLooper.mQueue;
mCallback=callback;
}
//构造函数3
public Handler(Looper looper){
mLooper=looper;//looper由外部传入,是哪个线程的Looper不确定。
mQueue=looper.mQueue;
mCallback=null;
}
//构造函数4,和构造函数3类似,只不过多了callback设置。
public Handler(Looper looper,Callback callback){
mLooper=looper;
mQueue=looper.mQueue;
mCallback=callback;
}
在上述构造函数中,Handler中的消息队列变量最终都会指向Looper的消息队列,Handler为何要如此做?

实际上在Android N上面貌似有很多的改变了,来一起来看看呢!

    public Handler() {
	    //获得调用线程的Looper
        this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
具体的四个构造函数如上面所示:

2.Handler的真面目
根据前面的分析可知,Handler中的消息队列实际就是某个Looper的消息队列,那么,Handler如此安排的目的何在?
在回答这个问题之前,我先来问一个问题:
怎么往Looper的消息队列插入消息?如果不知道Handler,这里有一个很原始的方法可解决上面这
个问题:调用Looper的myQueue,它将返回消息队列对象MessageQueue。
构造一个Message,填充它的成员,尤其是target变量。调用MessageQueue的enqueueMessage,将消息插入消息队列。
这种原始方法的确很麻烦,且极容易出错。但有了Handler后,我们的工作就变得异常简单了。Handler更像一个辅助类,帮
助我们简化编程的工作。
(1)Handler和Message
Handler提供了一系列函数,帮助我们完成创建消息和插入消息队列的工作。这里只列举其中一二。要掌握详细的API,则需要
查看相关的文档。


//查看消息队列中是否有消息码是what的消息。
final boolean hasMessages(int what)
//从Handler中创建一个消息码是what的消息。
final Message obtainMessage(int what)
//从消息队列中移除消息码是what的消息。
final void removeMessages(int what)
//发送一个只填充了消息码的消息。
final boolean sendEmptyMessage(int what)
//发送一个消息,该消息添加到队列尾。
final boolean sendMessage(Message msg)
//发送一个消息,该消息添加到队列头,所以优先级很高。
final boolean sendMessageAtFrontOfQueue(Message msg)


只需对上面这些函数稍作分析,就能明白其他的函数。现以
sendMessage为例,其代码如下所示:
[-->Handler.java]
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg,0);//调用sendMessageDelayed
}
[-->Handler.java]
//delayMillis是以当前调用时间为基础的相对时间
public final boolean sendMessageDelayed(Message msg,long
delayMillis)
{
if(delayMillis<0){
delayMillis=0;
}
//调用sendMessageAtTime,把当前时间算上return
sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
}
[-->Handler.java]
//uptimeMillis是绝对时间,即sendMessageAtTime函数处理的是绝对时间
public boolean sendMessageAtTime(Message msg,long uptimeMillis){
boolean sent=false;
MessageQueue queue=mQueue;
if(queue!=null){
//把Message的target设置为自己,然后加入到消息队列中msg.target=this;
sent=queue.enqueueMessage(msg,uptimeMillis);
}
return sent;
}
看到上面这些函数我们可以预见,如果没有Handler的辅助,当我们自己操作MessageQueue的enqueueMessage时,得花费多
大工夫!
Handler把Message的target设为自己,是因为Handler除了封装消息添加等功能外还封装了消息处理的接口。


(2)Handler的消息处理
刚才,我们往Looper的消息队列中加入了一个消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage函数,再把这个消息派发给Handler处理。
Handler在这块是如何处理消息的呢?
[-->Handler.java]
public void dispatchMessage(Message msg){
//如果Message本身有callback,则直接交给Message的callback处理
if(msg.callback!=null){
handleCallback(msg);
}else{
//如果本Handler设置了mCallback,则交给mCallback处理
if(mCallback!=null){
if(mCallback.handleMessage(msg)){
return;
}
}
//最后才是交给子类处理
handleMessage(msg);
}
}
dispatchMessage定义了一套消息处理的优先级机制,它们分别是:
Message如果自带了callback处理,则交给callback处理。
Handler如果设置了全局的mCallback,则交给mCallback处理。
如果上述都没有,该消息则会被交给Handler子类实现的
handleMessage来处理。当然,这需要从Handler派生并重载handleMessage函数。

在通常情况下,我们一般都是采用第三种方法,即在子类中通过重载handleMessage来完成处理工作的。


本节小结

本节对Android中的Handler事件传递进行了简单的分析,要分清楚Handler,Message,MessageQueue, Loop的概念,以及Handler写在主线程中以及子线程中的区别!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值