Android学习之Handler使用小结

Handler的使用大家都很熟悉,今天做一下关于Handler的总结,方便以后使用。

为什么用Handler

UI线程是非安全线程,不能进行耗时操作,子线程做耗时操作,不能更新UI,否则会出现程序长时间假死状态,即ANR,程序无响应异常。

为了解决以上问题,Android设计了Handler机制,由Handler来负责与子线程进行通讯,从而让子线程与主线程之间建立起协作的桥梁,使Android的UI更新的问题得到完美的解决。

Handler的作用:

1. 在新启动的线程中发送消息;
2. 在主线程中获取、处理消息。

Handler和Looper

 Message:消息体
 Handler:负责发送消息sendMessage(message),接收消handleMessage(Message msg)
 Looper:消息泵,用来管理特定线程内对象之间的消息交换,一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。主要包含两个方法Looper.prepare(),Looper.loop()

Message Queue:消息队列,存放线程放入的消息
UI线程:主线程,启动的时候会创建消息队列已经 给线程创建一个消息循环Looper.prepare()
@Override
public void run() {
  try {
    // preparing a looper on current thread     
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面理论上看起来理解不是很理想,下面我找了几张图来方便理解。

Looper.prepare():

这里写图片描述

可以看出通过prepare()方法,创建了线程和消息队列。下面我们看源码

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());
    }
    // 其他方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

prepare()会new Looper()创建一个Looper对象,对象里面分别又创建了线程和MQ。由此可知一个Thread只能有一个Looper对象对应一个MQ,三者一一对应。

Looper.loop():

这里写图片描述

Looper会不断从MQ中取出消息头执行,并推送给Handler执行。

Handler发送消息:

这里写图片描述

一个线程可以有多个Handler,但是只能有一个Looper!Handler发送消息通过Looper添加到MQ。

Handler处理消息:
这里写图片描述

Looper取出消息头,Handler执行,完毕后返回Looper继续执行。

总流程:

这里写图片描述

使用案例

子线程向主线程发送消息:

new Thread(new Runnable() {
                    @Override
                    public void run() {
                        MyHandler handler = new MyHandler(getMainLooper());
                        Message message = new Message();
                        message.obj = "子线程发送的消息";
                        handler.sendMessage(message);

                    }
                }).start();

    class MyHandler extends Handler {
        public MyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.e("TAG", msg.obj.toString());
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

主线程向子线程发送消息:

 class TestThread extends Thread
    {
        private Handler handler;
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            handler=new Handler(getMainLooper()){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.e("TAG",msg.obj.toString());
                }
            };
            Looper.loop();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

主线程发送:

 new TestThread().handler.sendEmptyMessage(0);
  • 1
  • 1

Handler的post()方法:

 new Thread(){  
  @Override
                public void run() {  
                    mHandler.post(new Runnable() {  
                        @Override  
                        public void run() {   
                        //更新UI
                        }  
                    });  
                };  
            }.start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

runOnUiThread()方法:

 new Thread(){  
                public void run() {  
                    mActivity.runOnUiThread(new Runnable() {  
                        @Override  
                        public void run() {  
     //更新UI
                        }  
                    });  
                };  
            }.start();  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

post和runOnUiThread都可以在子线程中更新UI,但是实际上他们是将消息post到UI线程中处理,所以run()里面不能做耗时操作。

posted @ 2016-11-11 22:08  天涯海角路  阅读(98)  评论(0)    收藏  举报