精华内容
下载资源
问答
  • 本文实例讲述了Android编程实现异步消息处理机制的几种方法。分享给大家供大家参考,具体如下: 1、概述 Android需要更新ui的话就必须在ui线程上进行操作。否则就会抛异常。 假如有耗时操作,比如:在子线程中下载...
  • Android有一套异步消息处理机制,可以解决子线程进行UI操作的问题 (不光可以主线程和子线程通信,也可以子线程和子线程进行通信)     示例代码 public class MainActivity extends AppCompatActivity { p...

    Android的主线程也叫UI线程,进行耗时操作会堵塞,所以开启子线程进行耗时操作,而更新UI元素必须在UI线程

    Android有一套异步消息处理机制,可以解决子线程进行UI操作的问题

    (不光可以主线程和子线程通信,也可以子线程和子线程进行通信)

     

     

    示例代码

    public class MainActivity extends AppCompatActivity {
    
        private final int flag=1;
    
        private TextView textView;
    
        @SuppressLint("HandlerLeak")
        private Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case flag:
                        textView.setText("异步消息处理");
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView=findViewById(R.id.textView);
            Button button=findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Message message=new Message();
                            message.what=flag;
                            handler.sendMessage(message);
                        }
                    }).start();
                }
            });
        }
    }

    点击Button开启一个子线程用handler发送一个message,handler中覆写handleMessage方法更新TextView 

     

     

    Android异步消息处理机制

    由四部分组成:Message,MessageQueue,Handler,Looper

     

    Message:

    用于线程间传递消息,可以携带一些简单的信息,例如Message.what,arg1,arg2存int型,obj可以存一个Object对象

    Handler:

    用于发送和处理消息,消息经过发送后,经过一系列处理最终传递到handleMessage()方法中,由于Handler在主线程中创建,所以它的handleMessage()也在主线程中运行,就可以更新UI操作了

    MessageQueue:

    用于存放Handler发送的消息,这部分消息存在于消息队列中等待被处理(每个线程只有一个MessageQueue)

    Looper:

    Looper的loop()方法运行后就会进入无限循环中,当发现MessageQueue存在一条消息,就取出MessageQueue中的消息并传递到Handler的handleMessage()方法中(每个线程也是只有一个Looper)

     

     

    runOnUIThread()的本质就是这个机制实现的 

     

    Message的三种创建方法的区别

    Message msg = new Message(); 
    Message msg2 = Message.obtain(); 
    Message msg1 = handle.obtainMessage(); 

    第一种直接创建一个新的Message对象

    第二种查看源码得知,从整个Messge池中返回一个新的Message实例,如果池中没有Message实例则新建一个,

    通过Message.obtain(); 能避免重复Message创建对象。 (推荐)

    第三种是Handler对象中的方法,机制和第二种一样

     

    Looper获取方法

    Looper.getMainLooper()获取主线程Looper

    Looper.myLooper()获取当前线程的Looper

    主线程默认存在Looper,且开启循环Looper.loop(),开启新线程不存在Looper

    Looper.prepare()启动Looper,           Looper.loop()开启循环

    mHandler.getLooper().quit()退出循环(直接清除MessageQueue中所有消息,终止Looper循环)

    mHandler.getLooper().quitSafely();API Level 18 后加入的方法, 作用同 quit()(调用后会在MessageQueue中所有非延时消息都处理后再终止Looper,更加安全)

    主线程或者子线程Looper.loop()无限循环不会导致ANR错误,只有主线程耗时操作才会ANR

    Looper.loop()运行在ActivityThread类的main方法中,如果不是无限循环,main方法执行完毕,应用就退出了.

    所以正是Looper.loop()无限循环才保持了APP一直显示在屏幕

     

    异步消息类AsyncTask

    封装了异步消息处理机制(有篇技术文章分析其子线程串行执行,还没看过源码不知道是真是假)

    有三个泛型参数Params,Progress,Result,分别为参数,进度,返回值

     

    四个主要的方法

    onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()
    public class AsyncTest extends AsyncTask<Void,Integer,Boolean> {
        
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
    
        @Override
        protected Boolean doInBackground(Void... voids) {
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }
    
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
        }
    }

    onPreExecute()执行一些初始化操作

    doInBackground()该方法是核心方法,代码在子线程中运行.所以耗时操作在这里完成,可以用publishProgress()方法来更新进度

    onProgressUpdate()调用publishProgress()方法就会执行该方法,可以根据进度对UI进行更新

    onPostExecute() 当doInBackground执行完毕的时候调用,进行一些收尾工作

     

    Android两个子线程之间通信

    主线程和子线程之间的通信可以通过主线程中的handler把子线程中的message发给主线程中的looper,或者,主线程中的handler通过post向looper中发送一个runnable。但looper默认存在于main线程中,子线程中没有Looper,该怎么办呢?其实原理很简单,把主线程Looper绑定到子线程中或者给子线程创建一个Looper。在另一个线程中通过这个handler发送消息,就可以实现子线程之间的通信了。 

    1.给子线程创建Looper对象

        public void run() {
    
            Log.i(TAG, "run: 线程1开始");
            Looper.prepare();
            handler = new Handler(Looper.getMainLooper()){
                public void handleMessage(Message msg) {
                    //这里处理消息
                    Log.i(TAG, "handleMessage:收到消息 "+msg.what);
                };
            };
            Looper.loop();
            Log.i(TAG, "run: "+Process.myTid());
        }

    2.获取主线程的looper 

    public void run() {
    
            Log.i(TAG, "run: 线程1开始");
            //区别在这里
            handler = new Handler(Looper.getMainLooper()){
                public void handleMessage(Message msg) {
                    //这里处理消息
                    Log.i(TAG, "handleMessage:收到消息 "+msg.what);
                };
            };
            Log.i(TAG, "run: "+Process.myTid());
        }

     

     

    _________________________________分割线___________________________________

    Android子线程与子线程通信示例代码请看我的另一篇博客

    https://blog.csdn.net/yh18668197127/article/details/86233593

    展开全文
  • 消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable) Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠。 这些类的主要...
  • Android异步消息处理机制 全解析

    千次阅读 2019-06-06 00:12:29
    Android异步消息处理机制的重要性不言而喻: 中高级工程师面试必问、阅读源码基础。 本文将通过结合源码的形式,全面解析Handler和MessageQueue、Looper的关系.并分析Android异步消息机制的相关原理.

    本文已授权微信公众号"Android技术杂货铺" 发布。转载请申明出处。

    Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程。

    本文将通过结合源码(api-28)的形式,全面解析Handler和MessageQueue、Looper的关系。并分析Android异步消息机制的相关原理。在分析之前,先给出结论性的东西,便于在分析过程中有一个主脉络。
    在这里插入图片描述

    一.Handler

    在分析Handler源码之前,我们尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程中创建,代码如下所示:

    //代码片1
    public class MainActivity extends Activity {
    	
    	private Handler handler1;
    	
    	private Handler handler2;
     
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		handler1 = new Handler();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				handler2 = new Handler();
    			}
    		}).start();
    	}
     
    }
    

    运行代码之后,会报错: java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
    并且提示是第16行代码报错.说是不能在没有调用Looper.prepare() 的线程中创建Handler。

    那我们在第16行代码前面尝试先调用一下Looper.prepare()呢,即代码如下:

    //代码片2
    new Thread(new Runnable() {
    	@Override
    	public void run() {
    		Looper.prepare();
    		handler2 = new Handler();
    	}
    }).start();
    

    运行后发现的确没有报错了。

    或许我们有疑问,为什么在子线程中加了Looper.prepare() 就不再报错了呢? 并且为什么第12行代码没有加Looper.prepare() 却没有报错。

    先带着第一个问题(即 子线程加了Looper.prepare() 后不报错),来看看Handler的源码吧:

    //代码片3
    public Handler(Callback callback, boolean async) {
          ......
          //仅贴出核心代码
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    可以看到,在第5行调用了Looper.myLooper()方法获取了一个Looper对象,如果Looper对象为空,则会抛出一个运行时异常,提示的错误正是 Can’t create handler inside thread that has not called Looper.prepare()!那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:

    //代码片4
     public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    这个方法非常简单,就是从sThreadLocal对象中调用get()方法。

    从名字我们可以大胆猜测一下,这是从sThreadLocal取出Looper,如果没有Looper存在自然就返回空了。

    既然有get,应该也有set()方法,我们搜索一下(在Looper.java类中) sThreadLocal.set,果然有:

    代码如下:

    //代码片5
     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));
        }
    

    上述代码片即为Looper.prepare()方法。

    可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。

    现在我们来通过源码分析 代码片1第12行代码没有添加Looper.myLooper() 方法也没有报错 的原因。这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:

    //代码片6
    public static void main(String[] args) {
       .....
       //核心代码如下
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();     //创建主线程
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        AsyncTask.init();
        if (false) {
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    可以看到,在第5行调用了Looper.prepareMainLooper()方法,而这个方法又会再去调用Looper.prepare()方法,代码如下所示:

    //代码片7
     public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
    //代码片8
    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));
        }
    

    因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。

    简单总结一下: Android的主线程是ActivityThread,主线程的入口为main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper对象,同时也会生成其对应的MessageQueue对象,即主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象需手动通过Looper.prepare()创建。

    学习完了Handler如何创建对象,我们看一下如何发送消息:

    这个流程相信大家也已经非常熟悉了,new出一个Message对象,然后可以使用setData()方法或arg参数等方式为消息携带一些数据,再借助Handler将消息发送出去就可以了,示例代码如下:

    //代码片9
    new Thread(new Runnable() {
    	@Override
    	public void run() {
    		Message message = new Message();
    		message.arg1 = 1;
    		Bundle bundle = new Bundle();
    		bundle.putString("data", "data");
    		message.setData(bundle);
    		handler.sendMessage(message);
    	}
    }).start();
    

    那我们来看一下sendMessage方法干了什么。源码如下:

    //代码片10
     public final boolean sendMessage(Message msg) {
            return sendMessageDelayed(msg, 0);
       }
    
    //代码片11
     public final boolean sendMessageDelayed(Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
      }
    
    //代码片12
    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);
        }
    
    //代码片13
       private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;       //把当前的Hanlder实例对象作为msg的target属性
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);      //执行接下来即将分析的代码片14
        }
    

    代码片10~代码片13这四个方法都是连贯在一起的。

    sendMessageAtTime()方法接收两个参数,其中msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数( SystemClock.uptimeMillis() )再加上延迟时间,如果你调用的不是sendMessageDelayed()方法,延迟时间就为0,然后将这两个参数都传递到MessageQueue的enqueueMessage()方法中。

    也说明了handler发出的消息msg,最终会保存到消息队列(MessageQueue )中去。

    二.MessageQueue

    在一中已经讲到handler发出的消息保存到了MessageQueue 中。消息队列在Android中指的是MessageQueue ,MessageQueue主要包括两个操作: 插入和读取,分别对应enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除。

    需要说明的是,MessageQueue 虽然叫消息队列,但它的内部实现并不是用的队列,而是通过一个单链表的数据结构来维护消息队列,单链表在插入和删除上比较有优势。

    1. enqueueMessage源码

    我们先来分析MessageQueue 的enqueueMessage源码:

    //代码片14
    boolean enqueueMessage(Message msg, long when) {
    
           .....
           //仅贴出核心代码
           
            synchronized (this) {
               .....
              //仅贴出核心代码
    
                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 {
                   //若消息队列中有消息,则根据消息(message)创建的时间插入到队列中
                    // 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;
        }
    

    观察上面的代码核心代码我们就可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数。具体的操作方法就根据时间的顺序调用msg.next,从而为每一个消息指定它的下一个消息是什么。当然如果你是通过sendMessageAtFrontOfQueue()方法来发送消息的,它也会调用enqueueMessage()来让消息入队,只不过时间为0,这时会把mMessages赋值为新入队的这条消息,然后将这条消息的next指定为刚才的mMessages,这样也就完成了添加消息到队列头部的操作。

    2. next 源码

    入队操作我们就已经看明白了,那出队操作是在哪里进行的呢?

    这里先告诉结论,在Looper.loop()中有这样一行代码(见 稍后会分析的代码片21的第12行)。

    next()方法在MessageQueue.class中。这段代码比较长,我们只贴出核心代码:

    //代码片15
    Message next() {
          ......
          //仅贴出核心代码
          
          // 该参数用于确定消息队列中是否还有消息,从而决定消息队列应处于出队消息状态 or 等待状态
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
               // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
    
                   // 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                         // 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1。下次循环时,消息队列则处于等待状态
                        nextPollTimeoutMillis = -1;
                    }    
    
                  ......
                  //仅贴出核心代码
            }    // 因为是死循环,回到分析原处
        }
    

    MessageQueue的next()方法关键思路在源码中我已经通过中文注释标明了,它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。继续看loop()方法的代码片第12行,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实就是Handler啦,你观察一下上面sendMessageAtTime()方法的第6行就可以看出来了。接下来当然就要看一看Handler中dispatchMessage()方法的源码了,如下所示(Handler,class中):

    //代码片16
     public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    在第5~6行进行判断,如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。那 handleMessage(msg)、handleCallback(msg) 具体是干嘛的呢? 我们继续看:

    //代码片17
    public void handleMessage(Message msg) {
    }
    

    可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。例如:

    //代码片18
    private Handler mHandler = new Handler(){
    		public void handleMessage(android.os.Message msg){
    			switch (msg.what){
    			case value:
    			    //do something
    				break;
    			default:
    			  //do something
    				break;
    			}
    		};
    	};
    

    至于handleCallback的代码如下:

    //代码片19
     private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    执行的是Message 回调的run方法.其实就是我们平时写的如下代码中的run方法:

    //代码片20
    public class MainActivity extends Activity {
     
    	private Handler handler;
     
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		handler = new Handler();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				 //do  something
    			}
    		}).start();
    	}
    }
    

    三.Looper

    Looper的源码分析在一、二部分或多或少提到过.这里集中分析。

    Looper主要是prepare()和loop()两个方法。

    1. Looper.prepare()方法:

    在Handler部分的代码片3、4、5、6、7、8及说明中已经谈论过,这里略过。

    2. Looper.loop()方法

    这段代码比较长,我们还是看核心代码:

    //代码片21
    public static void loop() {
            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;
            
            .....
            
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
              ......
              
                try {
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
              ......
    
                msg.recycleUnchecked();      //释放资源
            }
        }
    

    可以看到,这个方法从第11行开始,进入了一个死循环,然后不断地调用的MessageQueue的next()方法(即代码片15部分,也就是 二中MessageQueue的next()部分),我想你已经猜到了,这个next()方法就是消息队列的出队方法。在二中已经分析过了。

    总结一下,Looper主要作用:

    1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
    2. loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

    到此,这个流程已经解释完毕,让我们首先总结一下

    1.首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

    2.Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

    3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。

    4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。(代码片13第3行有中文注释说明)

    5.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

    看完了上述总结后,一个最标准的异步消息处理线程的写法应该是这样:

    //代码片22
    class LooperThread extends Thread {
          public Handler mHandler;
     
          public void run() {
              Looper.prepare();
     
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
     
              Looper.loop();
          }
      }
    

    那么我们还是要来继续分析一下,为什么使用异步消息处理的方式就可以对UI进行操作了呢?这是由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节,最后调用到了Handler的handleMessage()方法中,这时的handleMessage()方法已经是在主线程中运行的,因而我们当然可以在这里进行UI操作了。整个异步消息处理流程的示意图如下图所示:
    在这里插入图片描述

    四 . 其他在子线程操作UI的方法

    另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:

    1. Handler的post()方法
    2. View的post()方法
    3. Activity的runOnUiThread()方法

    我们先看Handler的post()方法:

    //代码片23
     public final boolean post(Runnable r)  {
           return  sendMessageDelayed(getPostMessage(r), 0);
      }
    

    这里分析一下getPostMessage®方法:

    //代码片24
    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    

    原来这里还是调用了sendMessageDelayed()方法去发送一条消息啊,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息.

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

    到了这里的sendMessageAtTime方法,其实已经和第11个代码片完全一样了.

    然后再来看一下View中的post()方法,代码如下所示:

    //代码片26
     public boolean post(Runnable action) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.post(action);
            }
    
            // Postpone the runnable until we know on which thread it needs to run.
            // Assume that the runnable will be successfully placed after attach.
            getRunQueue().post(action);
            return true;
        }
    

    如果attachInfo 不为空,就调用Handler中的post()方法。

    如果为空,我们看看一下代码片25第10行代码:

    //代码片27
    private HandlerActionQueue getRunQueue() {
            if (mRunQueue == null) {
                mRunQueue = new HandlerActionQueue();
            }
            return mRunQueue;
        }
    

    即 看 getRunQueue().post(action) 做看什么,只需要在HandlerActionQueue类中查看post()方法即可。
    继续看(HandlerActionQueue.class类)

    //代码片28
    public void post(Runnable action) {
            postDelayed(action, 0);
     }
    
    public void postDelayed(Runnable action, long delayMillis) {
            final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
    
            synchronized (this) {
                if (mActions == null) {
                    mActions = new HandlerAction[4];
                }
                mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
                mCount++;
            }
        }
    

    结合这篇文章(点击跳转),可以看出,如果attachInfo 为空,最终也会调用Handler中的post()方法。

    我们最后再来看一下Activity中的runOnUiThread()方法:

    //代码片29
       /**
         * Runs the specified action on the UI thread. If the current thread is the UI
         * thread, then the action is executed immediately. If the current thread is
         * not the UI thread, the action is posted to the event queue of the UI thread.
         *
         * @param action the action to run on the UI thread
         */
        public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
    

    如果当前线程不是UI线程,就调用Handler的post()方法;反之直接运行.(也可以结合方法上的注释看)。

    最后,我们总结一下Android异步消息处理机制流程:

    在子线程执行完耗时操作,当Handler发送消息时(Handler.sendMessage(Message msg) 或 Handler.post(Runable r)),就会调用MessageQueue.enqueueMessage,将消息根据时间放入到消息队列中。当通过Looper.loop开启循环后,会不断地从线程池中读取消息,即调用MessageQueue.next方法,然后调用目标Handler(即发送消息的Handler)的dispatchMessage方法传递消息,然后返回到Handler所在的线程,目标Handler收到消息,调用handleMessage方法,接收消息,处理消息。

    对此,Android的异步消息处理机制全部分析完毕。


    推荐阅读:

    1. 你真的懂Handler吗?Handler问答
    展开全文
  • Android 异步消息处理机制的几种实现

    千次阅读 2018-07-13 15:27:50
    Android 异步消息处理机制的几种实现 1、概述 Android需要更新ui的话就必须在ui线程上进行操作。否则就会抛异常。 假如有耗时操作,比如:在子线程中下载文件,通知ui线程下载进度,ui线程去更新进度等,这个...

    Android 异步消息处理机制的几种实现

    1、概述
    Android需要更新ui的话就必须在ui线程上进行操作。否则就会抛异常。
    假如有耗时操作,比如:在子线程中下载文件,通知ui线程下载进度,ui线程去更新进度等,这个时候我们就需要用到异步消息处理。


    一、什么是Handler
    Handler是Android提供用来异步更新UI的一套机制,也是一套消息处理机制,可以用它来发送消息,也可以用它来接收消息。


    二、为什么使用Handler
    Android在设计之时,就封装了一套消息的创建、传递、处理机制,作为系统原生的异步消息处理机制的实现之一,我们需要遵循这样的处理机制,该机制的另外一种实现是AsyncTask。


    三、Handler用法
    1、postdelayed()延时发送执行子线程(Demo)
    2、sendMessage()回调handleMessage()传递消息
    3、sendToTarget()传递消息


    四、为什么在Android中只能通过Handler机制在主线程中更新UI?
    最根本的是解决多线程并发问题。
    假如在同一个Activity中,有多个线程同时更新UI,且没有加锁,那会导致什么问题呢?
    UI更新混乱。
    假如加锁呢?
    会导致性能下降。
    使用Handler机制,我们不用去考虑多线程的问题,所有更新UI的操作,都是在 主线程消息队列中轮询去处理的。


    Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?
    异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
    —此处有图为证。
    这里写图片描述

    源码解析

    1、Looper

    对于Looper主要是prepare()和loop()两个方法。

    A. 首先看prepare()方法

    1.public static final void prepare() {    
    2.        if (sThreadLocal.get() != null) {    
    3.            throw new RuntimeException("Only one Looper may be created per thread");    
    4.        }    
    5.        sThreadLocal.set(new Looper(true));    
    }  

    sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。在第5行,将一个Looper的实例放入了ThreadLocal,并且2-4行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例~


    B. Looper的构造方法:

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }

    在构造方法中,创建了一个MessageQueue(消息队列)。

    C. 然后我们看loop()方法

    public static void loop() {  
            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  
                Printer logging = me.mLogging;  
                if (logging != null) {  
                    logging.println(">>>>> Dispatching to " + msg.target + " " +  
                            msg.callback + ": " + msg.what);  
                }  
    
                msg.target.dispatchMessage(msg);  
    
                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.recycle();  
            }  
    }
    

    第2行:
    public static Looper myLooper() {
    return sThreadLocal.get();
    }
    方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说loop方法必须在prepare方法之后执行。
    第6行:拿到该looper实例中的mQueue(消息队列)
    13到45行:就进入了我们所说的无限循环。
    14行:取出一条消息,如果没有消息则阻塞。
    27行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象,下面会进行分析。
    44行:释放消息占据的资源。


    Looper主要作用:
    1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
    2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
    好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:Handler登场了。

    2、Handler

    使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的。

    1.public Handler() {    
    2.        this(null, false);    
    3.}    
    4.public Handler(Callback callback, boolean async) {    
    5.        if (FIND_POTENTIAL_LEAKS) {    
    6.            final Class<? extends Handler> klass = getClass();    
    7.            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&    
    8.                    (klass.getModifiers() & Modifier.STATIC) == 0) {    
    9.                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +    
    10.                    klass.getCanonicalName());    
    11.            }    
    12.        }    
    13.    
    14.        mLooper = Looper.myLooper();    
    15.        if (mLooper == null) {    
    16.            throw new RuntimeException(    
    17.                "Can't create handler inside thread that has not called Looper.prepare()");    
    18.        }    
    19.        mQueue = mLooper.mQueue;    
    20.        mCallback = callback;    
    21.        mAsynchronous = async;    
    22.    }  

    14行:通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在19行又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

    A.sendMessage方法

    辗转反则最后调用了sendMessageAtTime方法。

    B. enqueueMessage方法

    1.private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
    2.       msg.target = this;    
    3.       if (mAsynchronous) {    
    4.           msg.setAsynchronous(true);    
    5.       }    
    6.       return queue.enqueueMessage(msg, uptimeMillis);    
    7.   } 

    enqueueMessage中首先为msg.target赋值为this,【如果大家还记得Looper的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。

    C. dispathMessage方法

    1.public void dispatchMessage(Message msg) {    
    2.        if (msg.callback != null) {    
    3.            handleCallback(msg);    
    4.        } else {    
    5.            if (mCallback != null) {    
    6.                if (mCallback.handleMessage(msg)) {    
    7.                    return;    
    8.                }    
    9.            }    
    10.            handleMessage(msg);    
    11.        }    
        } 

    可以看到,第10行,调用了handleMessage方法,下面我们去看这个方法:

    1./**  
    2.   * Subclasses must implement this to receive messages.  
    3.   */    
    4.  public void handleMessage(Message msg) {    
    5.  } 

    可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。


    3、Handler post
    post方法:

    1.public final boolean post(Runnable r)    
    2.   {    
    3.      return  sendMessageDelayed(getPostMessage(r), 0);    
       }

    getPostMessage方法:

    1.private static Message getPostMessage(Runnable r) {    
    2.      Message m = Message.obtain();    
    3.      m.callback = r;    
    4.      return m;    
    5.  }

    可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.
    注:产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。

    sendMessageDelayed方法和handler.sendMessage方法最终调用的都是:

    1.public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    
    2.       MessageQueue queue = mQueue;    
    3.       if (queue == null) {    
    4.           RuntimeException e = new RuntimeException(    
    5.                   this + " sendMessageAtTime() called with no mQueue");    
    6.           Log.w("Looper", e.getMessage(), e);    
    7.           return false;    
    8.       }    
    9.       return enqueueMessage(queue, msg, uptimeMillis);    
    10.   } 

    可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
    看dispatchMessage方法就能看出来。

    13.public void dispatchMessage(Message msg) {    
    14.        if (msg.callback != null) {    
    15.            handleCallback(msg);    
    16.        } else {    
    17.            if (mCallback != null) {    
    18.                if (mCallback.handleMessage(msg)) {    
    19.                    return;    
    20.                }    
    21.            }    
    22.            handleMessage(msg);    
    23.        }    
        } 

    第2行,如果不为null,则执行callback回调,也就是我们的Runnable对象。
    mCallback 的值是如何赋值的,可以查看Handler的构造方法,默认mCallback 的值为Null


    到此,这个流程已经解释完毕,总结一下

    1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
    2、Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
    3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。
    4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
    5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。


    在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。


    4、扩展

    其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

    代码:

    1.new Thread()    
    2.        {    
    3.            private Handler handler;    
    4.            public void run()    
    5.            {    
    6.    
    7.                Looper.prepare();    
    8.                    
    9.                handler = new Handler()    
    10.                {    
    11.                    public void handleMessage(android.os.Message msg)    
    12.                    {    
    13.                        Log.e("TAG",Thread.currentThread().getName());    
    14.                    };    
    15.                };                           
    16.                  Looper.loop();    
    17.                }

    四种更新UI的方法

    1、Handler.post();
    2、Handler.sendMessage();
    3、runOnUIThread()
    4、View.post()

    查看runOnUIThread()的源代码(Activity中)

    Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
    Parameters:
    action the action to run on the UI thread
    public final void  runOnUiThread(Runnable action) {
           if (Thread.currentThread() != mUiThread) {
               mHandler.post(action);
           } else {
                action.run();
           }
    }

    补充:
    1.异步消息处理机制的另一种实现:AsyncTask:

    主要方法:

    onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI
    Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出ProgressDialog


    doInBackground(Params… params):
    在onPreExecute()方法执行完后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker
    thread来执行这个方法(即在worker thread当中执行),执行完后将执行结果发送给最后一个 onPostExecute
    方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作


    onProgressUpdate(Progess… values): 这个方法也是在UI
    Thread当中执行的,在异步任务执行的时候,有时需要将执行的进度返回给UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新进度。这个方法在调用之前,我们需要在
    doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将进度时时刻刻传递给
    onProgressUpdate 方法来更新


    onPostExecute(Result… result): 当异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI
    Thread当中调用的,我们可以将返回的结果显示在UI控件上

    展开全文
  • 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,...这种处理方式被称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一起深入探究一下Handler和

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9991569


    之前也是由于周末通宵看TI3比赛,一直没找到时间写博客,导致已经有好久没更新了。惭愧!后面还会恢复进度,尽量保证每周都写吧。这里也是先恭喜一下来自瑞典的Alliance战队夺得了TI3的冠军,希望明年中国战队能够虎起!


    开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。


    这种处理方式被称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一起深入探究一下Handler和Message背后的秘密。


    首先来看一下如何创建Handler对象。你可能会觉得挺纳闷的,创建Handler有什么好看的呢,直接new一下不就行了?确实,不过即使只是简单new一下,还是有不少地方需要注意的,我们尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程中创建,代码如下所示:

    public class MainActivity extends Activity {
    	
    	private Handler handler1;
    	
    	private Handler handler2;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		handler1 = new Handler();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				handler2 = new Handler();
    			}
    		}).start();
    	}
    
    }
    如果现在运行一下程序,你会发现,在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在没有调用Looper.prepare() 的线程中创建Handler,那我们尝试在子线程中先调用一下Looper.prepare()呢,代码如下所示:
    new Thread(new Runnable() {
    	@Override
    	public void run() {
    		Looper.prepare();
    		handler2 = new Handler();
    	}
    }).start();
    果然这样就不会崩溃了,不过只满足于此显然是不够的,我们来看下Handler的源码,搞清楚为什么不调用Looper.prepare()就不行呢。Handler的无参构造函数如下所示:
    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());
            }
        }
        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;
    }
    可以看到,在第10行调用了Looper.myLooper()方法获取了一个Looper对象,如果Looper对象为空,则会抛出一个运行时异常,提示的错误正是 Can't create handler inside thread that has not called Looper.prepare()!那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:
    public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }
    这个方法非常简单,就是从sThreadLocal对象中取出Looper。如果sThreadLocal中有Looper存在就返回Looper,如果没有Looper存在自然就返回空了。因此你可以想象得到是在哪里给sThreadLocal设置Looper了吧,当然是Looper.prepare()方法!我们来看下它的源码:
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

    可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。


    咦?不对呀!主线程中的Handler也没有调用Looper.prepare()方法,为什么就没有崩溃呢?细心的朋友我相信都已经发现了这一点,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:

    public static void main(String[] args) {
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        AsyncTask.init();
        if (false) {
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    可以看到,在第7行调用了Looper.prepareMainLooper()方法,而这个方法又会再去调用Looper.prepare()方法,代码如下所示:
    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }

    因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。


    这样基本就将Handler的创建过程完全搞明白了,总结一下就是在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。


    看完了如何创建Handler之后,接下来我们看一下如何发送消息,这个流程相信大家也已经非常熟悉了,new出一个Message对象,然后可以使用setData()方法或arg参数等方式为消息携带一些数据,再借助Handler将消息发送出去就可以了,示例代码如下:

    new Thread(new Runnable() {
    	@Override
    	public void run() {
    		Message message = new Message();
    		message.arg1 = 1;
    		Bundle bundle = new Bundle();
    		bundle.putString("data", "data");
    		message.setData(bundle);
    		handler.sendMessage(message);
    	}
    }).start();

    可是这里Handler到底是把Message发送到哪里去了呢?为什么之后又可以在Handler的handleMessage()方法中重新得到这条Message呢?看来又需要通过阅读源码才能解除我们心中的疑惑了,Handler中提供了很多个发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法中,这个方法的源码如下所示:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

    sendMessageAtTime()方法接收两个参数,其中msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,如果你调用的不是sendMessageDelayed()方法,延迟时间就为0,然后将这两个参数都传递到MessageQueue的enqueueMessage()方法中。这个MessageQueue又是什么东西呢?其实从名字上就可以看出了,它是一个消息队列,用于将所有收到的消息以队列的形式进行排列,并提供入队和出队的方法。这个类是在Looper的构造函数中创建的,因此一个Looper也就对应了一个MessageQueue。


    那么enqueueMessage()方法毫无疑问就是入队的方法了,我们来看下这个方法的源码:

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }
            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                this.notify();
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                this.notify();
            }
        }
        return true;
    }

    首先你要知道,MessageQueue并没有使用一个集合把所有的消息都保存起来,它只使用了一个mMessages对象表示当前待处理的消息。然后观察上面的代码的16~31行我们就可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数。具体的操作方法就根据时间的顺序调用msg.next,从而为每一个消息指定它的下一个消息是什么。当然如果你是通过sendMessageAtFrontOfQueue()方法来发送消息的,它也会调用enqueueMessage()来让消息入队,只不过时间为0,这时会把mMessages赋值为新入队的这条消息,然后将这条消息的next指定为刚才的mMessages,这样也就完成了添加消息到队列头部的操作。


    现在入队操作我们就已经看明白了,那出队操作是在哪里进行的呢?这个就需要看一看Looper.loop()方法的源码了,如下所示:
    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
    }
    可以看到,这个方法从第4行开始,进入了一个死循环,然后不断地调用的MessageQueue的next()方法,我想你已经猜到了,这个next()方法就是消息队列的出队方法。不过由于这个方法的代码稍微有点长,我就不贴出来了,它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。继续看loop()方法的第14行,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实就是Handler啦,你观察一下上面sendMessageAtTime()方法的第6行就可以看出来了。接下来当然就要看一看Handler中dispatchMessage()方法的源码了,如下所示:
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    在第5行进行判断,如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。这样我相信大家就都明白了为什么handleMessage()方法中可以获取到之前发送的消息了吧!


    因此,一个最标准的异步消息处理线程的写法应该是这样:

    class LooperThread extends Thread {
          public Handler mHandler;
    
          public void run() {
              Looper.prepare();
    
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
    
              Looper.loop();
          }
      }

    当然,这段代码是从Android官方文档上复制的,不过大家现在再来看这段代码,是不是理解的更加深刻了?


    那么我们还是要来继续分析一下,为什么使用异步消息处理的方式就可以对UI进行操作了呢?这是由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节,最后调用到了Handler的handleMessage()方法中,这时的handleMessage()方法已经是在主线程中运行的,因而我们当然可以在这里进行UI操作了。整个异步消息处理流程的示意图如下图所示:




    另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:


    1. Handler的post()方法

    2. View的post()方法

    3. Activity的runOnUiThread()方法


    我们先来看下Handler中的post()方法,代码如下所示:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    原来这里还是调用了sendMessageDelayed()方法去发送一条消息啊,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息,我们来看下这个方法的源码:
    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    在这个方法中将消息的callback字段的值指定为传入的Runnable对象。咦?这个callback字段看起来有些眼熟啊,喔!在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那我们快来看下handleCallback()方法中的代码吧:
    private final void handleCallback(Message message) {
        message.callback.run();
    }
    也太简单了!竟然就是直接调用了一开始传入的Runnable对象的run()方法。因此在子线程中通过Handler的post()方法进行UI操作就可以这么写:
    public class MainActivity extends Activity {
    
    	private Handler handler;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		handler = new Handler();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				handler.post(new Runnable() {
    					@Override
    					public void run() {
    						// 在这里进行UI操作
    					}
    				});
    			}
    		}).start();
    	}
    }
    虽然写法上相差很多,但是原理是完全一样的,我们在Runnable对象的run()方法里更新UI,效果完全等同于在handleMessage()方法中更新UI。


    然后再来看一下View中的post()方法,代码如下所示:

    public boolean post(Runnable action) {
        Handler handler;
        if (mAttachInfo != null) {
            handler = mAttachInfo.mHandler;
        } else {
            ViewRoot.getRunQueue().post(action);
            return true;
        }
        return handler.post(action);
    }
    原来就是调用了Handler中的post()方法,我相信已经没有什么必要再做解释了。


    最后再来看一下Activity中的runOnUiThread()方法,代码如下所示:

    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
    如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。还有什么会比这更清晰明了的吗?


    通过以上所有源码的分析,我们已经发现了,不管是使用哪种方法在子线程中更新UI,其实背后的原理都是相同的,必须都要借助异步消息处理的机制来实现,而我们又已经将这个机制的流程完全搞明白了,真是一件一本万利的事情啊。


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自【张鸿洋的博客】很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为...
  • 1. 引入 ALooper、AHandler、AMessage ...这几个类是为了实现异步消息机制而设计的,这里有两个概念 “异步” &amp; “消息机制”,下面详细说明一下。 同步和异步概念区别比较简单,可以举个例子说明: ...
  • Handler原理 及使用过程 源码中分析理解.
  • 一、在子线程中更新UI概述和许多其他的GUI 库一样,Android 的UI 也是线程不安全的。也就是说,如果想要更新应用...对于这种情况,Android 提供了一套异步消息处理机制,完美地解决了在子线程中进行UI 操作的问题。二、
  • 第一个专题是Android异步消息处理机制,虽然平时经常用到Handler,用法也比较简单,但是看完源码之后,发现里面还是有很多知识点的^_^ 1.概述 Handler在平常开发中经常使用到,用来在不同组件(这里的组件不是...
  • Android 提供了一个好用的工具---AsyncTask ,方便我们在子线程中对 UI 进行操作,AsyncTask 背后的实现原理也是基于异步消息处理机制的。
  • Android异步信息处理机制 我们都知道android的ui更新操作都是在主线程执行的,但是很多时候我们都需要在子线程 中执行一些费时的操作,以获取我们所需要的变更数据。很多刚入门的同学都容易犯的一个错误就是在子...
  • 解析Android异步信息处理机制

    千次阅读 2019-10-30 17:09:21
    Android 异步消息处理机制解析     Android 中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue、Looper。下面将会对这四个部分进行一下简要的介绍。 Message:  &...
  • Android中的异步消息机制分为四个部分:Message、Handler、MessageQueue和Looper。 其中,Message是线程之间传递的消息,其what、arg1、arg2字段可以携带整型数据,obj字段可以携带一个Object对象。 Handler是处理者...
  • Handler异步加载图片 GridView 上拉刷新 下拉刷新 里面也有其他控件(比如ListView ScrollView等等)的上拉刷新 下拉刷新
  • Android异步消息处理机制详解及源码分析

    万次阅读 多人点赞 2015-05-28 09:20:12
    怎么样,这和你平时用的Handler一样吧,对于Handler异步处理的简单基础示例先说到这,接下来依据上面示例的写法分析原因与源代码原理。 3 分析Android 5.1.1(API 22)异步消息机制源码 3-1 看看Handler的...
  • android
  • android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException( "An error occured while executing doInBackground()" , e.getCause()); } catch ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,059
精华内容 18,423
关键字:

android异步消息处理机制