Android消息机制---Handler工作原理

简述:

    子线程没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:CalledFromWrongThreadException为了实现子线程中操作UI界面,Android中引入了Handler消息传递机制。
    什么是Handler?
    handler通俗一点讲就是用来在各个线程之间发送数据的处理对象。在任何线程中,只要获得了另一个线程的handler,则可以通过  handler.sendMessage(message)方法向那个线程发送数据。基于这个机制,我们在处理多线程的时候可以新建一个thread,这个thread拥有UI线程中的一个handler。当thread处理完一些耗时的操作后通过传递过来的handler向UI线程发送数据,由UI线程去更新界面。 

    handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:

        (1)安排Message或Runnable 在某个主线程中某个地方执行;

        (2)安排一个动作在不同的线程中执行。

handler线程间通信示意图

   Android消息机制---Handler工作原理_第1张图片

基本使用:

    1、子线程向主线程发送消息Message

          Handler的一种经典的使用方法,也是更新UI最常用的方法。

                                                 Android消息机制---Handler工作原理_第2张图片

代码:

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private Button button;
    private TextView txt;
    private static final int WORKTHREAD = 1;

    private Handler mainThreadHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == WORKTHREAD) {
                final String data = (String) msg.obj;
                txt.setText(data);
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        }
    };


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

        button = (Button) findViewById(R.id.btn);
        txt = (TextView) findViewById(R.id.txt);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //开启一个工作线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = Message.obtain();
                        message.what = WORKTHREAD;
                        message.obj = "来自子线程数据!";
                        mainThreadHandler.sendMessage(message);
                    }
                }).start();

            }
        });
    }
}

2、主线程向子线程发送消息

      在平时的开发中有时会碰到主线程子线程发送消息,这时候来考虑这个问题。

                                             Android消息机制---Handler工作原理_第3张图片

代码:

public class MainActivity extends AppCompatActivity {
    private TextView txt;
    private Button btn_main_start;
    Handler workHandler;
   
    private static final int MAIN_THREAD = 2;


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

        intiViews();

        //开启子线程接收主线程数据
        final WorkThread workThread = new WorkThread();
        workThread.start();


        btn_main_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //主线程发送数据
                Message message = Message.obtain();
                message.what = MAIN_THREAD;
                message.obj = "来自主线程数据!";
                workHandler.sendMessage(message);
            }
        });


    }

    private void intiViews() {
        txt = (TextView) findViewById(R.id.txt);
        btn_main_start = (Button) findViewById(R.id.btn_main_start);
    }


    class WorkThread extends Thread {
        public void run() {
            Looper.prepare();
            workHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == MAIN_THREAD) {
                        final String data = (String) msg.obj;
                        txt.setText(data);
                        Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
                    }
                }
            };
            Looper.loop();
        }
    }

}

从上面的代码中=可能会有一些疑问,比如:

    (1)为什么主线程向子线程发送消息时要调用手动下列代码:  

            Looper.prepare();
            workHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                   //do something
                } 
            };
            Looper.loop();

       (2)为什么new Handler()构造方法中没有使用当前线程的Looper而是使用了主线程Looper?

           如果子线程涉及到更新UI的操作需要使用主线程的Looper,如果没有涉及到跟新UI的操作而只是简单的处理消息可以使用当前线程的Looper.

   为了对Handler作进一步的了解,接下来我们分析一下源码。

源码分析:

先来看一下Handler的构造方法:

 
  

Public constructors

Handler()

Default constructor associates this handler with the Looper for the current thread.

Handler(Handler.Callback callback)

Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you can handle messages.

Handler(Looper looper)

Use the provided Looper instead of the default one.

Handler(Looper looper, Handler.Callback callback)

Use the provided Looper instead of the default one and take a callback interface in which to handle messages.

 
    我们打开源码就会发现Handler(),Handler(Handler.Callback callback)以及Handler(boolean async)最终都会调用下面这个构造方法:
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class klass = getClass();
        //如果Handler为匿名内部类、局部内部类或者非静态成员内部类(即除了静态内部类外的所有内部类)立即警告“潜在的内存泄漏”
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //没有指定Looper,默认使用当前线程的Looper
    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 = callback;
    mAsynchronous = async;
}
  这个构造方法中做了两件事: ①检测“潜在的内存泄漏”(与该博文无太多关系,如果想了解更多请看: Handler引发内存泄漏)②关联当前线程的Looper.

   再看其他两个构造方法:Handler(Looper looper)和Handler(Looper looper, Callback callback)。这两个构造方法最终调用的是下面的构造方法:

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

从以上两段代码可以看出:Handler构造方法需要先关联一个Looper,

接下来再来探一探sendMessage(Message msg)方法:

public final boolean sendMessage(Message msg)              
{                                                          
    return sendMessageDelayed(msg, 0);                     
}                                                          

该方法最后调用了sendMessageDelayed()方法,我们找到该方法:

public final boolean sendMessageDelayed(Message msg, long delayMillis)      
{                                                                           
    if (delayMillis < 0) {                                                  
        delayMillis = 0;                                                    
    }                                                                       
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}                                                                           

该方法调用了sendMessageAtTime()方法,我们继续追踪下去:

 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);                 
 }                                                                    
     到这里就算是结束了,它首先获取当前Looper所关联的MessageQueue,然后将Message存储到相应的MessageQueue()中。关于enqueueMessaage()方法参见前篇博客: MessageQueue工作原理。

Handler发送消息已经清楚了,接下来再来看看Handler如何处理消息。

在使用Handler的时候我们每次都要调用(主线程除外)Looper.loop()方法,这个方法作用:当MQ中有消息要执行时调用该条Message所属Handler的dispatchMessage(msg)方法处理消息(详细见:Looper工作原理),ok,我们打开dispatchMessage(msg)源码:

public void dispatchMessage(Message msg) {          
    if (msg.callback != null) {                     
        handleCallback(msg);                        
    } else {                                        
        if (mCallback != null) {                    
            if (mCallback.handleMessage(msg)) {     
                return;                             
            }                                       
        }                                           
        handleMessage(msg);                         
    }                                               
}                                                   
                                                    
   该方法中首先判断Message是否存在其接口CallBack,如果有就调用handleCallback(msg),否则判断时候Handler接口Callback是否为null,如果不为null就调用CallBack.handleMessage(msg)方法,否则调用Handler的handleMessage( msg)方法。而 CallBack.handleMessage(msg)方法和Handler的handleMessage(msg)方法都需要开发者自己手动处理。





你可能感兴趣的:(Android)