天天看点

重识Handler

关于Handler,最开始的使用就是子线程想要刷新UI,必须用Handler来实现,那为什么Handler能够异步更新UI?Handler的内部机制是什么样的?Handler跟Looper,Message,MessageQueue之间是什么关系?等等,本文记录Handler需要了解的一些知识。

##了解一些概念。

Android的消息机制:一个线程开启一个无限循环模式,不断遍历自己的消息列表,如果有消息就挨个拿出来做处理,如果列表没消息,自己就堵塞(相当于wait,让出cpu资源给其他线程),其他线程如果想让该线程做什么事,就往该线程的消息队列插入消息,该线程会不断从队列里拿出消息做处理。

(摘自知乎的回答)

###什么是Handler?

官方解释为:Handler可以发送一个消息到与当前线程关联的MessageQueue中,每一个Handler实例都与当前线程及当前线程的MessageQueue有关,当你创建一个新的Handler,这个Handler会与当前线程及当前线程的MessageQueue关联,这个时候你通过Handler发送消息到当前的MessageQueue中的时候,会在当前的MessageQueue中处理它。

调度消息使用
  • post(Runnable)
  • postAtTime(Runnable, long)
  • postDelayed(Runnable, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message, long)
  • sendMessageDelayed(Message, long)
处理消息有两种办法

第一种:通过重写Handler的handleMessage方法来处理

Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
           

第二种:实现Handler的CallBack接口

Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //如果return false则会继续调用handleMessage方法
            //return true则不继续调用handleMessage方法
            return false;
        }
    });
           

###什么是MessageQueue?

官方解释:MessageQueue是Message的存储列表(单链表),供Looper使用,Message并不是直接添加到MessageQueue中的,而是通过与Looper关联的Handler来完成的。

###什么是Looper?

官方解释:Looper用于运行线程的消息循环,线程默认是没有消息循环的,必须通过

Looper.prepare()

;然后

Looper.loop()

开启线程的消息循环,example:

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();
      }
  }
           

###什么是Message?

官方解释:定义一个包含描述和任意数据对象的消息,Message可以被发送到Handler,然后由Handler处理。

虽然Message的构造方法是public,但是创建Message最好的方法是通过

或者

Handler mHandler = new Handler();
//其实调用的也是Message.obtain();
Message mMessage = mHandler.obtainMessage();
           

##Handler跟MessageQueue、Looper、Message的关系。

  • Handler: 用于发送Message跟处理Message。
  • MessageQueue: Message队列。
  • Looper: 负责从 MessageQueue中取出 Message,并分发给对应的 Handler 进行处理。
  • Message: 具体的消息内容(包含一个int的what,一个int的arg1,一个int的arg2跟一个任意对象的obj。)

    一张图展示他们之间的联系:

    重识Handler

##Handler为什么能够异步刷新UI

public class MainActivity extends AppCompatActivity {
    private final int REFRESH_UI_TAG = 0x1;
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.sendEmptyMessage(REFRESH_UI_TAG);
            }
        }).start();
    }
    private final Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == REFRESH_UI_TAG) {
                mTextView.setText("子线程更新UI");
            }
        }
    };
}
           

由于Android的UI操作是非线程安全的,为了防止子线程更新UI出现一些不可预见的结果,所以Android只允许在主线程中更新UI。

这是一段最简单的例子,子线程需要更新UI,通过Handler来实现,为何Handler能够异步更新UI?

new Handler()

的时候,初始化了

Looper

MessageQueue

public Handler() {
        this(null, false);
    }
    
public Handler(Callback callback, boolean async) {
        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());
            }
        }
        //初始化Looper,如果在主线程实例化,Looper.myLooer()获取的就是Looper.getMainLooper();(这个mainLooper在什么时候赋值的?往下看。)
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //初始化MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
           

Looper.myLooper()

获取到的是什么?

看源码之前了解一下

ThreadLocal

线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。这里线程自己的本地存储区域存放是线程自己的Looper
public static @Nullable Looper myLooper() {
        //返回的是Looper中ThreadLocal保存的内容
        return sThreadLocal.get();
    }
           

那sThreadLocal什么时候set的内容?在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");
        }
        //在这里设置了Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
           

接下来

mHandler.sendEmptyMessage(REFRESH_UI_TAG)

做了什么操作?

//第一步
    public final boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
    }
    //第二步
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        //构建消息
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    //第三步
     public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    //第四步
     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        //如果MessageQueue为null,则抛出异常,
        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);
    }
    //第五步
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //设置msg.target,这个在Looper.loop()的时候会用到
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    //该方法在Looper.loop()中调用,是用来处理发出去的消息,如果没有采用callBack来处理消息的话,最后会调用handleMessage()。
    public void dispatchMessage(Message msg) {
        //msg的callBack是一个runnable
        if (msg.callback != null) {
            //调用runnable的run方法
            //handleCallback(msg)其实就是调用了Message.callback的run方法,这个在Handler#post(Runnable)会被使用到
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //这个才是重点
            handleMessage(msg);
        }
    }
           

当调用

sentEmptyMessage(int what)

之后最终调用了

MessageQueue.enqueueMessage(msg,uptimeMillis)

;

点开MessageQueue

boolean enqueueMessage(Message msg, long when) {
        //msg.target指的就是Handler
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //msg的一个标志位,用于判断当前msg是否正在被使用
        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正在被使用
            msg.markInUse();
            //when 表示这个消息执行的时间,队列是按照消息执行时间排序的
        //SystemClock.uptimeMillis()获取到的是系统从开机到现在的毫秒数
            //如果handler 调用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillis
            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.
             //如果需要唤醒Looper线程,这里调用native的方法实现epoll机制唤醒线程
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
           

到这里都没有看到任何代码触发

Handler

handleMessage()

方法。

其实

handleMessage()

被调用实在

Looper.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
            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.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                //到这里才调用了Handler的dispatchMessage() 
                //dispatchMessage()最后调用了handleMessage();
                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();
        }
    }
           

因为

Handler

在主线程实例化的,所以当在其他线程发送消息之后,最后都会调用在主线程实例化的

Handler

中的

dispatchMessage

方法。

##主线程的Looper在什么时候实例化的?

如果在子线程中直接new Handler的时候

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Handler mHandler = new Handler();
            }
        }).start();
    }
           

会抛出异常

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                                                     at android.os.Handler.<init>(Handler.java:200)
                                                     at android.os.Handler.<init>(Handler.java:114)
                                                     at com.qfxl.handlersample.MainActivity$1.run(MainActivity.java:18)
                                                     at java.lang.Thread.run(Thread.java:818)
           

这是因为在当前线程没有获取到Looper对象,报错位置在于

public Handler(Callback callback, boolean async) {
        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 = callback;
        mAsynchronous = async;
    }
           

但是在主线程中直接

new Handler()

并不会抛出这个异常。

因为主线程中的Looper在APP启动的时候就已经创建好了,位置在于

ActivityThread#main

方法中

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        
        //这里已经创建好了Looper
        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循环
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
           

所以如果想要在子线程中使用Handler的话,得先创建当前线程的Looper对象

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //创建当前线程的Looper
                Looper.prepare();
                Handler mHandler = new Handler();
                //开启Looper循环
                Looper.loop():
            }
        }).start();
    }
           

Looper.prepare()

Looper.prepareMainLooper()

的区别在于

Looper.prepareMainLooper()

创建的MessageQueue是不允许退出的。

Looper.loop为什么不会造成线程的死循环?

Looper.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
            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.isTagEnabled(traceTag)) {
                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();
        }
    }
           

之所以这个死循环不会导致线程卡死,是因为Android的消息机制采用了Linux的

pipe

机制。简单一句话是:Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。所以线程只是出于阻塞并不会卡死。另外Activity的生命周期回调也是AMS通过Binder发送ipc调用给app进程,app进程里的binder stub接收到调用后,给main looper插了条runnable。