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

来源:互联网 发布:淘宝 作弊 编辑:程序博客网 时间:2024/05/23 22:54

   一.场景

           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() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}Message message = Message.obtain();message.arg1 = ++i;TASK_HANDLER.sendMessage(message);}}).start();}static final class TaskHandler extends Handler {@Overridepublic void handleMessage(Message msg) {Log.i("Info", "message:"+msg.arg1);}}

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

new Thread(new Runnable() {@Overridepublic 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable() {@Overridepublic void run() {Looper.prepare();handler = new Handler(Looper.myLooper()) {@Overridepublic 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
原创粉丝点击