Android应用开发之Android Handler消息机制解析
凌雪 2018-10-24 来源 :网络 阅读 1156 评论 0

摘要:本文将带你了解Android应用开发之Android Handler消息机制解析,希望本文对大家学Android有所帮助。

本文将带你了解Android应用开发之Android Handler消息机制解析,希望本文对大家学Android有所帮助。


Handler
在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work   thread)中去执行,然后将执行的结果告诉UI线程(main thread),熟悉Android的朋友都知道,UI的更新只能通过Main   thread来进行。那么这里就涉及到了如何将子线程的数据传递给main thread呢?
Android已经为我们提供了一个消息传递的机制——Handler,来帮助我们将子线程的数据传递给主线程,其实,当熟悉了Handler的原理之后
我们知道,Handler不仅仅能将子线程的数据传递给主线程,它能实现任意两个线程的数据传递。
接下来,我们便详细的了解下Handler的原理及其使用。
首先看一下Handler最常规的使用方式:
private Handler mHandler = new   Handler(){        @Override        public   void handleMessage(Message msg)   {            super.handleMessage(msg);            switch   (msg.what)   {                case   MESSAGE_WHAT:                    Log.d(TAG,   "main thread receiver message: " + ((String)   msg.obj));                    break;            }        }    };     private   void sendMessageToMainThreadByWorkThread()   {        new   Thread(){            @Override            public   void run()   {                Message   message =   mHandler.obtainMessage(MESSAGE_WHAT);                message.obj   = "I am message from work   thread";                mHandler.sendMessage(message);            }        }.start();    }    /*    *   通常我们在主线程中创建一个Handler,    * 然后重写该Handler的handlerMessage方法,可以看到该方法传入了一个参数Message,    * 该参数就是我们从其他线程传递过来的信息。    *    *   我们在来看下子线程中如何传递的信息,子线程通过Handler的obtainMessage()方法获取到一个Message实例,    * 我们来看看Message的几个属性:    *   Message.what------------------>用来标识信息的int值,通过该值主线程能判断出来自不同地方的信息来源    *   Message.arg1/Message.arg2----->Message初始定义的用来传递int类型值的两个变量    *   Message.obj------------------->用来传递任何实例化对象    * 最后通过sendMessage将Message发送出去。    *    * Handler所在的线程通过handlerMessage方法就能收到具体的信息了,如何判断信息的来源呢?当然是通过what值啦。    * 怎么样很简单吧    */
   
    文章的开头说过,Handler不仅仅是能过将子线程的数据发送给主线程,它适用于任意两个线程之间的通信。
下面我们来看下两个子线程之间如何进行通信的。
很简单啊,在一个线程创建Handler,另外一个线程通过持有该Handler的引用调用sendMessage发送消息啊!
写程序可不能关说不练啊,我们把代码敲出来看一下!
?12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455private   Handler handler;    private void   handlerDemoByTwoWorkThread()   {        Thread hanMeiMeiThread = new   Thread()   {            @Override            public   void run() {//                  Looper.prepare();                handler   = new Handler()   {                    @Override                    public   void handleMessage(Message msg) {                        Log.d(TAG,   "hanMeiMei receiver message: " + ((String)   msg.obj));                        Toast.makeText(MainActivity.this,   ((String) msg.obj),   Toast.LENGTH_SHORT).show();                    }                };//                  Looper.loop();            }        };        Thread   liLeiThread = new Thread()   {            @Override            public   void run()   {                try   {                    Thread.sleep(2000);                }   catch (InterruptedException e) {                    e.printStackTrace();                }                Message   message =   handler.obtainMessage();                message.obj   = "Hi MeiMei";                handler.sendMessage(message);            }        };        hanMeiMeiThread.setName("韩梅梅   Thread");        hanMeiMeiThread.start();        liLeiThread.setName("李雷   Thread");        liLeiThread.start();         /*        *   搞定,我们创建了两个Thread,liLeiThread和hanMeiMeiThread两个线程,很熟悉的名字啊!        *   跟之前的代码没太大区别hanMeiMeiThread创建了Handler,liLeiThread通过Handler发送了消息。        * 只不过此处我们只发送一个消息,所以没有使用what来进行标记        *   运行看看,我们的李雷能拨通梅梅吗?        *   啊哦,出错了        *   05-13 17:08:17.709 20673-20739/ E/AndroidRuntime: FATAL EXCEPTION: 韩梅梅   Thread                                                   Process:   design.wang.com.designpatterns, PID:   20673                                                   java.lang.RuntimeException:   Can't create handler inside thread that has not called Looper.prepare()                                                       at   android.os.Handler.(Handler.java:200)                                                       at   android.os.Handler.(Handler.java:114)        *Can't   create handler inside thread that has not called   Looper.prepare()        * -----------》它说我们创建的handler没有调用Looper.prepare();        *   好的,我们在实例化Handler之前调用下该方法,看一下。加上是不是没有报错了呢。        *   等等,虽然没有报错,但是hanMeiMeiThread也没有接到消息啊,消息呢?别急。        *   我们在Handler实例化之后加上Looper.loop();看一看,运行一下,是不是收到消息了呢。        *   只是为什么呢?        *   接下来我们就去看看Handler是怎么实现的发消息呢,弄清楚了原理,这里的原因也就明了了。        */     }
   
    好了,卖了半天的关子,终于要开始真正的主题了。
首先我们来看下,为什么在子线程里实例化的时候不调用Looper.prepare()就会报错呢?
?12345678910111213141516171819202122232425262728//我们先来看看new Handler();时出错的原因。后续讲解源码分析只贴出关键部分。//如下是Handler构造函数里抛出上文异常的地方,可以看到,由于mLooper对象为空才抛出的该异常。mLooper =   Looper.myLooper();        if (mLooper   == null)   {            throw   new RuntimeException(                "Can't   create handler inside thread that has not called   Looper.prepare()");        }/*  异常的原因看到了,接下来我们看看Looper.prepare()方法都干了些什么?*/public static void   prepare() {    prepare(true);} private static void   prepare(boolean quitAllowed) {    if (sThreadLocal.get()   != null) {        throw new   RuntimeException("Only one Looper may be created per   thread");    }    sThreadLocal.set(new   Looper(quitAllowed));}/* 可以看到,该方法在当前thread创建了一个Looper(),   ThreadLocal主要用于维护线程的本地变量,    */ private Looper(boolean quitAllowed) {    mQueue =   new MessageQueue(quitAllowed);    mThread =   Thread.currentThread();}//而Looper的构造函数里面又为我们创建了一个MessageQueue()对象。
了解到此,我们已经成功引出了Handler机制几个关键的对象了,Looper、MessageQueue、Message。
那么,肯定也有人又产生新的疑问了——为什么在主线程中创建Handler不需要要用Looper.prepare()和Looper.loop();
方法呢?
其实不是这样的,App初始化的时候都会执行ActivityThread的main方法,我们可以看看ActivityThread的main()方法都做了什么?
       Looper.prepareMainLooper();        ActivityThread   thread = new   ActivityThread();        thread.attach(false);        if   (sMainThreadHandler == null)   {            sMainThreadHandler   = thread.getHandler();        }        if   (false)   {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG,   "ActivityThread"));        }        //   End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();/* 真相只有一个,是的在创建主线程的时候Android已经帮我们调用了Looper.prepareMainLooper() 和Looper.loop()方法,所以我们在主线程能直接创建Handler使用。*/
   
    我们接着来看Handler发送消息的过程:
 //调用Handler不同参数方法发送Message最终都会调用到该方法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);    }
sendMessage的关键在于enqueueMessage(),其内部调用了messageQueue的enqueueMessage方法
 boolean   enqueueMessage(Message msg, long when)   {        if (msg.target == null)   {            throw   new IllegalArgumentException("Message must have a   target.");        }        if   (msg.isInUse()) {            throw   new IllegalStateException(msg + " This message is already in   use.");        }         synchronized   (this)   {            if   (mQuitting)   {                IllegalStateException   e = new IllegalStateException(                        msg.target   + " sending message to a Handler on a dead   thread");                Log.w(TAG,   e.getMessage(),   e);                msg.recycle();                return   false;            }             msg.markInUse();            msg.when   =   when;            Message   p =   mMessages;            boolean   needWake;            if   (p == null || when == 0 || when < p.when)   {                //   New head, wake up the event queue if blocked.                msg.next   =   p;                mMessages   = msg;                needWake   =   mBlocked;            }   else   {                //   Inserted within the middle of the queue.  Usually we don't have to   wake                //   up the event queue unless there is a barrier at the head of the queue                //   and the message is the earliest asynchronous message in the   queue.                needWake   = mBlocked && p.target == null &&   msg.isAsynchronous();                Message   prev;                for   (;;)   {                    prev   = p;                    p   =   p.next;                    if   (p == null || when < p.when) {                        break;                    }                    if   (needWake && p.isAsynchronous())   {                        needWake   =   false;                    }                }                msg.next   = p; // invariant: p ==   prev.next                prev.next   =   msg;            }             //   We can assume mPtr != 0 because mQuitting is false.            if   (needWake)   {                nativeWake(mPtr);            }        }        return   true;    }    /*从代码可以看出Message被存入MessageQueue时是将Message存到了上一个Message.next上,       形成了一个链式的列表,同时也保证了Message列表的时序性。    */
Message的发送实际是放入到了Handler对应线程的MessageQueue中,那么,Message又是如何被取出来的呢?
细心的朋友可能早早就发现了,之前抛出异常的地方讲解了半天的Loop.prepare()方法,一直没有说到Loop.loop()方法。
同时,在之前的例子中也看到了,如果不调用Looper.loop()方法,Handler是接受不到消息的,所以我们可以大胆的猜测,
消息的获取肯定和它脱不了关系!当然关怀疑还不行,我们还必须找出真相来证明我们的猜想?那还等什么,先看看loop()方法吧。
 public   static void loop() {//可以看到,在调用Looper.prepare()之前是不能调用该方法的,不然又得抛出异常了        final Looper me   = myLooper();        if (me == null)   {            throw   new RuntimeException("No Looper; Looper.prepare() wasn't called on this   thread.");        }        final   MessageQueue queue =   me.mQueue;         // Make sure   the identity of this thread is that of the local   process,        // and keep track of   what that identity token actually   is.        Binder.clearCallingIdentity();        final   long ident =   Binder.clearCallingIdentity();         for   (;;)   {            Message   msg = queue.next(); // might   block            if   (msg == null)   {                //   No message indicates that the message queue is quitting.                return;            }             //   This must be in a local variable, in case a UI event sets the   logger            final   Printer logging =   me.mLogging;            if   (logging != null)   {                logging.println(">>>>>   Dispatching to " + msg.target + " "   +                        msg.callback   + ": " +   msg.what);            }             final   long traceTag =   me.mTraceTag;            if   (traceTag != 0)   {                Trace.traceBegin(traceTag,   msg.target.getTraceName(msg));            }            try   {                msg.target.dispatchMessage(msg);            }   finally {                if   (traceTag != 0)   {                    Trace.traceEnd(traceTag);                }            }             if   (logging != null) {                logging.println("<<<<<   Finished to " + msg.target + " " +   msg.callback);            }             //   Make sure that during the course of dispatching   the            //   identity of the thread wasn't corrupted.            final   long newIdent =   Binder.clearCallingIdentity();            if   (ident != newIdent) {                Log.wtf(TAG,   "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);            }             msg.recycleUnchecked();        }    }/*这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环 不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们在继续往下分析Message的分发*/public void   dispatchMessage(Message msg) {    if (msg.callback !=   null)   {        handleCallback(msg);    }   else {        if (mCallback != null)   {            if   (mCallback.handleMessage(msg))   {                return;            }        }        handleMessage(msg);    }}/*好了,到这里已经能看清晰了可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法*/private static void handleCallback(Message message)   {        message.callback.run();    }//即,如果我们在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。比如我们经常写的runOnUiThread方法:runOnUiThread(new Runnable()   {            @Override            public   void run() {             }        });public   final void runOnUiThread(Runnable action)   {      if (Thread.currentThread() != mUiThread)   {          mHandler.post(action);      }   else   {          action.run();      }  }  /*而如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的,所以handler的handlerMessage方法的执行也会在主线程中。  */
   
    到这里,想必你应该清楚如何在不同的线程之间来使用Handler了吧。
最后总结一下:
在使用handler的时候,在handler所创建的线程需要维护一个唯一的Looper对象,
Looper对象的内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,
但是只能有一个Looper和一个MessageQueue。 Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message存入到了上一个
Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message。   Looper对象通过loop()方法开启了一个死循环,不断地从looper内的MessageQueue中取出Message,
然后通过handler将消息分发传回handler所在的线程。
   
    最后附上一张自己理解画出来的流程图:    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved