Android消息处理机制—— Looper, Handler, Message

Android消息处理有三大核心类:Looper, Handler和Message,下面就是详细的诠释这三者作用与关联。
Looper
Looper时常被称为消息泵,它是被设计用来使一个普通线程编程Looper线程,所谓的Looper线程就是循环工作的线程。这样的线程会不断循环,一旦有新任务则执行,执行完之后继续等待下一个任务。使用Looper类创建Looper线程只有两行核心代码:

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

        // ...其他处理,如实例化handler

        // 开始循环处理消息队列
        Looper.loop();
    }
}

这样就把该LooperThread线程转化为Looper线程了。

1.  Looper.prepare()

图1 普通线程转化为Looper线程
图1 普通线程转化为Looper线程

通过Looper类将一个普通线程转化为Looper线程,则在该Looper线程中,有一个Looper对象,它的内部维护了一个消息队列MessageQueue。其中,一个线程中至多只能有一个Looper对象,这一点在源码中可以看出:

public class Looper {
    // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    // Looper内的消息队列
    final MessageQueue mQueue;
    // 当前线程
    Thread mThread;
    // 。。。其他属性

    // 每个Looper对象中有它的消息队列,和它所属的线程
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

    // 我们调用该方法会在调用线程的TLS中创建Looper对象
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 试图在有Looper的线程中再次创建Looper将抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    // 其他方法
}

关于Looper的构造函数:
(1)创建并维护一个MessageQueue 的消息队列;
(2)将Looper对象指向它的实例所在线程(即Looper在创建的时候会关联一个线程);

关于Looper.prepare()函数:
**(1)**ThreadLocal对象用于保存Looper对象的引用,其本身是一个

public static final void loop() {
        Looper me = myLooper();  //得到当前线程Looper
        MessageQueue queue = me.mQueue;  //得到当前looper的MQ

        // 这两行没看懂= = 不过不影响理解
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        // 开始循环
        while (true) {
            Message msg = queue.next(); // 取出message
            if (msg != null) {
                if (msg.target == null) {
                    // message没有target为结束信号,退出循环
                    return;
                }
                // 日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
                msg.target.dispatchMessage(msg);
                // 还是日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);

                // 下面没看懂,同样不影响理解
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
                // 回收message资源
                msg.recycle();
            }
        }
    }

当然,除了prepare()和loop()函数,Looper类还提供了一些有用的方法,比如Looper.myLooper()得到当前线程的Looper对象:

public static final Looper myLooper() {
        // 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
        return (Looper)sThreadLocal.get();
    }

这边可以看到,就是通过ThreadLocal变量拿到Looper对象的。还有getThread()得到Looper对象所在的线程,quit()方法来结束Looper线程。
对于Looper有以下总结:
1).每个线程最多只能有一个Looper对象,并且此对象会存在ThreadLocal中;
2).每个Looper对象中会维护一个MessageQueue的消息队列,loop()方法调用后线程会不断地从队列中取出消息并交由handler去处理;
3).Looper类可以将一个普通线程转化为Looper线程。

Handler

下面问题是如何往MessageQueue上发送消息呢?这就要用到Handler!
Handler是什么呢?handler的主要作用是往MessageQueue上添加和处理消息,当然只会处理由自己发送的消息。Handler在创建的时候会关联一个looper,默认的构造方法将关联当前线程的looper,这个在源码中很清晰:

public class handler {

    final MessageQueue mQueue;  // 关联的MQ
    final Looper mLooper;  // 关联的looper
    final Callback mCallback; 
    // 其他属性

    public Handler() {
        // 没看懂,直接略过,,,
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            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
        mLooper = Looper.myLooper();
        // looper不能为空,即该默认的构造方法只能在looper线程中使用
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

    // 其他方法
}

下面如果要在LooperThread中加入Handler:

public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;

    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

        // 实例化两个handler
        handler1 = new Handler();
        handler2 = new Handler();

        // 开始循环处理消息队列
        Looper.loop();
    }
}

加入Handler实例的效果图如下所示:
图3 加入handler对象的Looper线程
图3 加入handler对象的Looper线程

因此可以看得出来:一个LooperThread只能有一个Looper对象,但是可以用多个Handler。

Handler发送消息
有了handler就可以实用post(Runnable),postAtTime(Runnable,long),sendEmptyMessage(int),sendMessage(Message)等,看到这些API可以看出,发送两种类型的消息:Message 和 Runnable,但是其实Runnable也是被封装成Message的。
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行

 public final boolean post(Runnable r)
    {
       // 注意getPostMessage(r)将runnable封装成message
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //得到空的message
        m.callback = r;  //将runnable设为message的callback,
        return m;
    }

通过handler发送Message有如下特点:
1.message.target即为该handler对象,这样可以确保looper执行到该Message时能够找到处理它的handler,即在loop()函数中的关键代码:

msg.target.dispatchMessage(msg);

2.post发出的消息,其callback为runnable对象;

Handler如何处理消息
消息的处理是通过核心方法dispatchMessage(Message msg)和钩子函数handleMessage(Message msg)完成的,源码如下:

// 处理消息,该方法由looper调用
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);
        } else {
            // 如果handler本身设置了callback,则执行callback
            if (mCallback != null) {
                 /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);
        }
    }

    // 处理runnable消息
    private final void handleCallback(Message message) {
        message.callback.run();  //直接调用run方法!
    }
    // 由子类实现的钩子方法
    public void handleMessage(Message msg) {
    }

看到这里,开发者可以实现handleMessage函数和Runnable对象的run()函数,以此来完成整个Handler机制!

Handler用处的总结:
1. handler可以在任意线程发送消息,并且这些消息会被添加到关联的MessageQueue上。
图4 handler发送消息到MessageQueue
图4 handler发送消息到MessageQueue

2. handler是在它关联的Looper线程中来处理消息的。
图5 handler处理Message
图5 handler处理Message

这样就解决了Android最经典的不能在其他非主线程中更新UI的问题:Android的主线程必是一个Looper线程,我们在其中创建的handler对象将默认关联主线程的Looper对象(该Looper对象是由系统自动创建的),也就自然关联到该Looper对象锁维护的MessageQueue。其中一个最常见的实用案例就是,创建handler并将其引用传给某个thread,当该thread完成耗时操作后,将实用handler将消息回发到消息队列,再由loop()函数将消息弹出,交由handler的handleMessage(Message msg)的用户自实现的钩子函数来更新UI。
具体实现如图6所示:
图6 整体过程

给一个实例代码:

public class TestDriverActivity extends Activity {
    private TextView textview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textview = (TextView) findViewById(R.id.textview);
        // 创建并启动工作线程
        Thread workerThread = new Thread(new SampleTask(new MyHandler()));
        workerThread.start();
    }

    public void appendText(String msg) {
        textview.setText(textview.getText() + "\n" + msg);
    }

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            String result = msg.getData().getString("message");
            // 更新UI
            appendText(result);
        }
    }
}
public class SampleTask implements Runnable {
    private static final String TAG = SampleTask.class.getSimpleName();
    Handler handler;

    public SampleTask(Handler handler) {
        super();
        this.handler = handler;
    }

    @Override
    public void run() {
        try {  // 模拟执行某项任务,下载等
            Thread.sleep(5000);
            // 任务完成后通知activity更新UI
            Message msg = prepareMessage("task completed!");
            // message将被添加到主线程的MQ中
            handler.sendMessage(msg);
        } catch (InterruptedException e) {
            Log.d(TAG, "interrupted!");
        }

    }

    private Message prepareMessage(String str) {
        Message result = handler.obtainMessage();
        Bundle data = new Bundle();
        data.putString("message", str);
        result.setData(data);
        return result;
    }![这里写图片描述](http://img.blog.csdn.net/20150720232945900)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值