zoukankan      html  css  js  c++  java
  • 25、Android--Handler

    Handler

    Android只能在UI线程(主线程)更新UI显示,一般情况在子线程做耗时操作,我们平时通过Handler消息机制让线程之间进行通信。它有三个构造函数:

    构造函数 描述
    Handler() 无参构造函数
    Handler(CallBack mCallBack) 带回调的构造函数
    Handler(Runnable runnable) 带任务的构造函数

    其中Handler的内部运行机制如下图所示:

    Handler创建

    创建Handler要区分两种情况,在子线程创建Handler和在主线程创建Handler,它们有部分区别。

    子线程创建

    如果直接在子线程创建Handler会导致崩溃,是因为子线程创建Handler必须先调用looper.prepare()

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

    通过源码我们可以知道为什么要先调用looper.prepare(),我们看看Handler构造函数,无参的构造其实都调用带callBack的构造函数:

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

    可以看出在第10行调用了Looper.myLooper(),如果looper不存在则抛出异常。我们继续查看Looper.myLooper()中的代码:

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

    方法非常简单,就是在ThreadLocal中拿到Looper实例,其中ThreadLocal是线程副本,保证不同线程都有一份独立的数据。其中,给sThreadLocal设置Looper的地方在于Looper.prepare()方法中:

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

    这里就是判断ThreadLocal中是否存在Looper,不存在则直接创建一个。这样就解决为什么创建Handler需要调用Looper.prepare()方法。而且每个线程只能存在一个Looper实例。

    主线程创建

    在主线程创建Handler可以直接创建,不需要调用Looper.prepare()方法:

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

    主线程创建Handler不需要调用Looper.prepare()方法是因为系统已经自动调用Looper.prepare()方法,我们看下ActivityThread(程序主线程)中的main()方法:

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("<pre-initialized>");
        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();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    可以看到在第11行调用了Looper.prepareMainLooper()方法,该方法又调用了myPrepare()方法,我们在Looper中查看该方法的源码:

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    总结:子线程创建Handler实例必须先调用Looper.prepare()才能正常创建,而主线程中创建Handler则不需要。

    消息传递

    当我们创建Message后,可以通过setData、args参数以及obj参数等当时为消息携带数据或对象,再借助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提供很多发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法外,其他发送消息的方法最终都会调用sedMesageAtTime()方法

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

    它所携带的两个参数都会传递到MessageQueue的enqueueMessage(..)方法中,MessageQueue是一个先进先出的消息队列,它在Looper构造函数中创建:

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

    因此一个Looper对应一个MessageQueue,那么MessageQueue中的enqueueMessage()就是入队方法。其中sendMessageAtFrontOfQueue()也调用enqueueMessage()来让消息入队,只是时间为0而已。然而出队操作是通过Looper.looper()来进行操作。

    public static void loop() {
        final Looper me = myLooper();
        .....
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
        ......
            msg.recycleUnchecked();
        }
    }
    

    每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,而msg.target就是Handler。

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

    dispatchMessage会根据不同的传递消息的方式进行判断,其中msg.callback是Runnable的实例。

    Android中除了Handler可以发送消息外,还有其他的方式,主要方式如下所示:

    Handler中的post()方法发送消息
    View中的post()方法发送消息
    Activity的runOnUiThread()发送消息

    View中的post()方法其实就是调用Handler的post()方法,而Activity的runOnUIThread()方法则是先判断线程如果当前线程不是主线程,就调用Handler的post()方法,否则直接调用Runnable对象的run()方法。

    Handler原理

    Handler是支撑整个Android系统运行的基础,本质上Android系统都是由事件驱动的。而处理事件的核心就在于Handler。

    关于Handler、Looper、MessageQueue和Message的概述如下:

    Handler 消息机制中作为一个对外暴露的工具,其内部包含了一个 Looper 。负责Message的发送及处理。
    Looper 作为消息循环的核心,其内部包含了一个消息队列 MessageQueue,用于记录所有待处理的消息。(线程切换在这里完成)
    MessageQueue 则作为一个消息队列,则包含了一系列链接在一起的Message(内部是单链表)
    Message 则是消息体,内部又包含了一个目标处理器target,这个target正是最终处理它的Handler

    Handler的使用

    Handler的使用非常简单,一般分为在主线程创建Handler和在子线程创建Handler两种方式。
    一般是在主线程中实现一个Handler,然后在子线程中使用它。

    public class MainActivity extends AppCompatActivity {
        private Handler handler = new MyHandler();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(){
                @Override
                public void run() {
                    handler.sendEmptyMessageDelayed(1, 1000);
                }
            };
        }
        // 自定义一个Handler
        class MyHandler extends Handler {
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i("MainActivity", "主线程:handleMessage:" + msg.what);
            }
        }
    }
    

    或者有时候需要在子线程中创建运行在主线程中的Handler

    public class MainActivity extends AppCompatActivity {
        private Handler handler = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(){
                @Override
                public void run() {
                    Looper.prepare();
                    handler = new MyHandler();
                    handler.sendEmptyMessageDelayed(1, 1000);
                    Looper.loop();
                }
            };
        }
        // 自定义一个Handler
        class MyHandler extends Handler {
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i("MainActivity", "主线程:handleMessage:" + msg.what);
            }
        }
    }
    

    Handler

    Handler可以用来发送消息,我们这里从sendMessage()方法开始,源码如下所示:

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    

    调用了sendMessageDelayed方法:

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

    继而调用sendMessagAtTime方法:

    public boolean sendMessageAtTime(@NonNull 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);
    }
    

    在这里赋值MessageQueue并且将它作为参数传递给enqueueMessage()方法,其方法的调用顺序如下:

    可以看到无论如何,最后都会走到enqueueMessage()方法中。接下来看看enqueueMessage()方法:

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    enqueueMessage()方法一共做了两件事情,一个是给Message赋值,一个是调用传进来的这个MessageQueue的enqueueMessage()方法。

    target表示的就是当前的Handler,也就是说每个发出去的Message都持有把它发出去的Handler的引用。

    MessageQueue

    Handler这个mQueue就是上文我们提到过的MessageQueue对象,它是一个单链表(一种链式存取的数据结构)。

    我们在sendMessageAtTime()方法中对MessageQueue进行赋值,并且当做参数传递给Handler的enqueueMessage()方法。

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        // 对MessageQueue进行赋值,mQueue来源于构造方法
        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);
    }
    

    其实MessageQueue是在构造方法中进行赋值的,接下来我们看看构造方法的源码:

    public Handler(@Nullable 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 " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        // 对MessageQueue进行赋值,来自于Looper
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    这里我们可以看到,MessageQueue的实例创建应该在Looper中,这里先不研究Looper,继续探索MessageQueue。

    最后发送消息都调用的是MessageQueue的queue.enqueueMessage(msg, uptimeMillis)方法。

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            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) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 消息插到队列中间
                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; 
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

    主要分为三个步骤,可以参看代码标注:

    mMessages 是队列的第一消息,获取到它判断消息队列是不是空的,是则将当前的消息放到队列头部。
    如果当前消息不需要延时,或当前消息的执行时间比头部消息早,也是放到队列头部。
    如果不是以上情况,说明当前队列不为空,并且队列的头部消息执行时间比当前消息早,需要将它插入到队列的中间位置。

    我们也许会产生疑问,如何判断这个位置呢?其实依旧是通过消息被执行的时间。

    通过遍历整个队列,当队列中的某个消息的执行时间比当前消息晚时,将消息插到这个消息的前面。

    Looper

    我们再来看看Handler的构造方法,如下所示:

    public Handler(@Nullable 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的实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        // 对MessageQueue进行赋值,来自于Looper
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    可以看到在这里通过mLooper = Looper.myLooper()代码赋值了Looper的实例,它作为消息循环的核心,不断的从MessageQueue中取出消息进行分发。

    我们再来看看Lopper的myLooper()方法,源码如下:

    /**
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    可以看到这个sThreadLocal是一个ThreadLocal类,并且它的泛型是Looper对象。

    ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

    我们看到源码中有个注释:除非您已调用prepare(),否则sThreadLocal.get()将返回null。接下来看看prepare()方法:

    public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        // 如果sThreadLocal有值,就抛异常,没有值才会塞进去一个值。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    

    prepare方法必须调用但也只能调用一次,不调用没有值,抛异常,调用多次也还抛异常。

    在构造Handler之前,必须调用Looper的prepare()方法创建Looper。

    接下来看看sThreadLocal.set(new Looper(quitAllowed))方法的源码:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    set方法首先获取到了当前的线程,然后获取一个map。这个map是以键值对形式存储内容的。

    如果不为空就塞进去值,如果获取的map为空,就创建一个map。这里面的key是当前的线程,这里面的value就是Looper。(线程绑定)

    接下来我们去看看Lopper的构造方法,源码如下所示:

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

    可以看到在Looper的构造方法中创建了MessageQueue对象。
    由于prepare()方法只能调用一次,所以只会创建一个Looper对象,所以:

    一个线程中只会创建一个Looper对象,而一个Looper对象也只会创建一个MessageQueue对象。

    最后,主线程创建Handler没有调用prepare()方法,那么Looper是从哪里来的?

    其实主线程的Looper是在ActivityThread类中main()方法中创建的,接下来我们看看这个main()方法:

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        Process.setArgV0("<pre-initialized>");
        // 就是这里调用了prepare方法
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        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");
    }
    

    接下来我们看看Looper的prepareMainLooper方法:

    public static void prepareMainLooper() {
        //设置不允许退出的Looper
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    prepare(false)创建了Looper并且把它和当前线程一起塞进map中的。
    Looper要分发消息并不知道具体发送消息的时间,只能开启死循环不断从MessageQueue中获取消息,所以Looper.loop()方法就是开启了死循环:

    public static void loop() {
        // 拿到当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        
        // ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // ......
            msg.recycleUnchecked();
        }
    }
    

    在循环中Looper不停的取出消息,拿到Message对象以后,会去调用Message的target字段的dispatchMessage方法,其中target就是发送消息的Handler。

    可以再来看看dispatchMessage方法,源码如下所示:

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    可以看到在回调中调用了handleMessage(msg)方法。

    通过Handler发送消息时,消息会回到Handler初始化的线程,而不一定是主线程,所以Handler其实是借助共享变量来进行线程切换的。

    • Looper和ANR

    其实整个Android就是在一个Looper的loop循环的,整个Android的一切都是以Handler机制进行的,即只要有代码执行都是通过Handler来执行的,而所谓ANR便是Looper.loop()没有得到及时处理,一旦没有消息,Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android里调用了linux层的代码实现在适当时会睡眠主线程

  • 相关阅读:
    看过设计模式第一章的心得
    支付宝支付过程填坑
    C# 合并只要有交集的所有集合
    C#中的反射 Reflection
    动态更改配置文件
    六种弹窗
    Respone弹窗
    Aspose是一个很强大的控件,可以用来操作word,excel,ppt等文件
    使用ECharts报表统计公司考勤加班,大家加班多吗?
    排污许可管理条例-中华人民共和国国务院令第736号
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5510134.html
Copyright © 2011-2022 走看看