Android组件_Handler Looper Message理解

Android组件_Handler Looper Message理解

一、Handler机制概述

Handler机制是Android中一种消息处理机制。
主要组成或重要概念:

  1. Message,线程间通讯的数据单元。
  2. Message Queue,消息队列,用来存放Handler发布的消息,按照FIFO执行。
  3. Handler是Message的主要处理者,负责将Message添加到消息队列意见对消息队列中的Message进行处理。
  4. Looper循环器,循环去除Message Queue里面的Message,并交付给相应的Handler进行处理。
  5. Thread UI thread通常就是main thread,Android启动程序时会替它建立一个Message Queue。
    每一个线程里可以含有一个Looper对象以及MessageQueue数据结构。
  6. ThreadLocal 他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)

二、使用场景

  1. 我们常常用Handler来更新UI,但是不是说Handler就是把用来更新UI的,耗时的I/O操作,读取文件,访问网络等等都是可以在Handler里面操作的。

  2. 子线程间通讯,可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

  3. 延时任务等

三、使用方法

3.1. 子线程线程里更新UI

public class MainActivity extends AppCompatActivity {
    //主线程中的handler
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //获得刚才发送的Message对象,然后在这里进行UI操作
            Log.e(TAG,"------------> msg.what = " + msg.what);
            ...
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在子线程中发送更新消息给主线程的handler去更新UI
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message m = mHandler.obtainMessage();
                ...
                mHandler.sendMessage(m);
            }
        }).start();
    }
}

3.2 子线程间通信

        final Handler[] h = new Handler[1];
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();//创建子线程的looper,子线程使用handler消息传递,这一步必须要有,因为默认的子线程是没有Looper对象的
                h[0] = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.e(TAG,"------------> msg.what = " + msg.what);
                    }
                };
                Looper.loop();//消息池消息循环处理
            }
        }, "work1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = h[0].obtainMessage();
                //msg.sendToTarget();
                h[0].sendEmptyMessage(0);//在work2子线程中使用持有work1子线程looper的handler发送消息至work1中去做消息处理
            }
        }, "work2").start();

3.3 HandlerThread使用

    {
        MyHandlerThread mHandlerThread = new MyHandlerThread("work");
        Handler mHandler = new Handler(mHandlerThread.getLooper()){//将该mHandler与HandlerThread对象的Looper关联起来
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    }

    MyHandlerThread mHandlerThread = new MyHandlerThread("work");
        Handler mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    }

3.4 Handler CallBack参数使用
在构造Handler对象的时候可以穿入CallBack参数,如下

//主线程:
  Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });
//其他线程中:
handler.sendMessageXXX(msg);

3.5 Handler post方法使用

    //主线程中创建mPostHandler
    Handler mPostHandler = new Handler();
    //子线程中使用post方法
     new Thread(new Runnable() {
            @Override
            public void run() {
               /* Message m = mHandler.obtainMessage();
                mHandler.sendMessage(m);*/
                mPostHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //更新UI ,注意这里虽然操作写在子线程中,事实运行时是在主线程中,原因分析在4.2.5中
                    }
                });
            }
        }).start();

3.6 Handler 延时任务/循环定时操作

延时任务
new Handler().postDelayed(new Runnable(){   
            public void run() {  
                   //show dialog
            }  
  }, 5000);  //延时5秒后执行runnable run方法
循环定时操作:
1,首先创建一个Handler对象  
 Handler handler=new Handler();  

2,然后创建一个Runnable对
Runnable runnable=new Runnable(){  
   @Override  
   public void run() {  
    // TODO Auto-generated method stub  
    //要做的事情,这里再次调用此Runnable对象,以实现每两秒实现一次的定时器操作  
    handler.postDelayed(this, 2000);  
   }  
};  

3,使用PostDelayed方法,两秒后调用此Runnable对象  
handler.postDelayed(runnable, 2000);  

4,如果想要关闭此定时器,可以这样操作
handler.removeCallbacks(runnable); 

四、原理分析

Handler机制的铁三角-Handler、Looper和MessageQueue,另外还有下面进行浅入分析。
整体架构 :

Android组件_Handler Looper Message理解_第1张图片
image.png

4.1 主线程Handler对象与主线程Looper对象关联

对3.1中的例子进行浅入的分析。

4.1.1 Handler对象初始化获取主线程的Looper对象

1.主线程Handler对象初始化:

Handler mHandler = new Handler(){...}

默认的构造函数会将该Handler对象与当前线程的Looper对象关联。下面我们看下构造函数是如何关联上当前线程的looper的。
Handler的默认构造函数:

frameworks/base/core/java/android/os/Handler.java
    public Handler() {
        this(callback:null, async:false);
    }

2.如果该线程没有looper对象,该handler将抛异常:

    public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//
        if (mLooper == null) {//如果当前线程没有Looper对象将抛出异常
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

3.sThreadLocal对象中保存着当前线程的looper对象,Looper.myLooper()即用来获取当前线程的Looper对象:

frameworks/base/core/java/android/os/Looper.java
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//这里获取当前线程的Looper对象
    }

4.1.2 主线程初始化时创建Looper对象及MessageQueue对象

1.对于UI线程即我们所说的Main线程及ActivityThread,在ActivityThread创建的时候,main函数中创建了Looper对象,并通过Looper.loop()是主线程进入消息循环中:

frameworks/base/core/java/android/app/ActivityThread.java
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper(); //创建主线程Looper对象

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ...
        Looper.loop();//不断循环处理MessageQueue中的消息
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

2.Looper.prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环:

frameworks/base/core/java/android/os/Looper.java
    public static void prepareMainLooper() {
        prepare(false);//创建Looper对象,并且将新建的looper对象与当前线程关联
        synchronized (Looper.class) {
            if (sMainLooper != null) { //可以看出主线程应该只有一个Looper对象
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();//返回当前线程的Looper对象
        }
    }

3.Looper.prepare()函数为当前线程(此处为主线程,也可以是其他线程)创建Looper对象,并将该对象设置至线程的sThreadLocal变量中:

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { // 说明prepare不能调用两次,否则会抛出异常,保证一个线程只有一个Looper实例
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。
    }

4.Looper.myLooper()返回当前线程的Looper对象,这里也就验证了myLooper()的确是从sThreadLocal变量中获取了当前线程的Looper对象。

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

概述一下就是,主线程在初始化时就生成了含有一个不可退出的MessageQueue的Looper对象,并将该Looper对象保存在主线程的sThreadLocal变量中;
当在主线程中创建Handler对象时,会将该Handler对象与当前线程的sThreadLocal变量中保存的主线程Looper对象关联起来。
因此,在上面3.1的例子中,主线程new一个Handler对象时会将该Handler对象与主线程中的Looper对象关联,执行Looper.loop()不断循环处理消息;

4.2 Handler、Looper、Message及HandlerThread浅析

4.2.1 Handler

1.可以使用的方法

post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageDelayed(Message,long)
sendMessageAtTime(Message,long) 

2.以上方法都最终都将调用sendMessageAtTime(),将消息加入msg的目标Handler对象关联的Looper持有的消息队列中,之后就是排队消息队列循环处理消息了:

    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); //将该消息加入该消息的目标handler关联的looper中的消息队列中
    }

4.2.2 Looper:

1.Looper构造函数,看到Looper的构造函数里新建了一个MessageQueue对象:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建消息队列
        mThread = Thread.currentThread();
    }

2.Looper.loop(),循环处理消息队列中消息的函数:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        ...
        // 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,若无消息则会阻塞在这里。
            ...
            try {
                msg.target.dispatchMessage(msg); //调用发该message的handler的dispatchMessage方法;
            } finally {
            }
            ...
            msg.recycleUnchecked();//处理完的msg可以进行回收,实现的地方清除了该msg对象的内容,并将其保留在消息池中,以供循环使用            
        }
    }

3.在dispatchMessage中进行消息处理,post方法中的runnable就是这里的msg.callback

frameworks/base/core/java/android/os/Handler.java
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg); //执行post方法中的runnable方法
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) { //如果创建handler时有传入callback的话
                    return;
                }
            }
            handleMessage(msg);//创建handler时需复写方法handleMessage(),处理消息
        }
    }

3.1首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:

private static void handleCallback(Message message) {
  message.callback.run();
}

这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。
3.2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。
3.3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。

4.2.3 MessageQueue

1 MessageQueue的next()方法
在消息队列中不断取出消息,next方法是个无限循环的方法,如果有消息返回这条消息并从链表中移除,而没有消息则一直阻塞在这里。

Message next() {
        for (;;) {
            synchronized (this) {
                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());
                }
                if (msg != null) {
                    ...
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } 
        }
    }

4.2.4 HandlerThread

1.自带Looper的Thread:

frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
@Override
    public void run() {
        ...
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        onLooperPrepared();
        Looper.loop();
        ...
    }
}

我们看到HandlerThread继承自Thread,并且在run函数中使用Looper.prepare()为使用线程创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal);在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量;loop()开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
因此也可以使用HandlerThread,这样可以不用像普通线程那样需要Looper.prepare()和Looper.loop(),因为HandlerThread为我们做好了准备工作;

2.另外可以通过HandlerThread的getLooper方法获的该HandlerThread的Looper对象:

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

handleMessage

4.2.5 Handler中post方法

3.5中的例子中子线程中post中的run方法运行在主线程中,原因分析如下:
1.传入Runnable对象

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

2.这里将Runnable封装到一个Message对象中

  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r; //将runnable传入message对象中的callback变量中
        return m;
    }

3.之后与普通的handler sendMessage方法流程一致,调用用sendMessageAtTime,将该消息加入该消息的目标handler,及mPostHandler关联的looper中的消息队列中,如何处理在4.2.1 中有提到过。

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

也就是说,虽然runnable复写方法在子线程中,但是同样是封装成message对象传入主线程的looper持有的MessageQueue对象中,在主线程中对封装了runnable变量的message进行消息执行处理,因此post中的run方法是运行在主线程中的。

五、注意事项

  1. Handler在处理消息需要严格区分是否是在UI线程中,Handler一般用在非UI线程中来传递消息,在非UI线程中使用Handler来发送消息,消息
    处理会被严格执行,但如果在UI线程中使用Handler来发送消息,相同的消息在内部会被合并,且执行时序也得不到保证
  2. 因为HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程,比较合适处理那些需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。
  3. android中handler使用应该注意的由handler引起的OOM内存泄漏) http://blog.csdn.net/javazejian/article/details/50839443
  4. 在普通线程中使用机制时要记得先Looper.prepare(); 最后还要Looper.loop();

你可能感兴趣的:(Android组件_Handler Looper Message理解)