安卓Handler机制重要知识点汇总(必知必会)

  1. 在没有Looper的线程,创建Handler会报RuntimeException异常,源码如下:
 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) {//在当前线程中没有Looper会报运行时异常。
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  1. 一个线程仅仅只有一个Looper,实现此功能用到了ThreadLocal,ThreadLocal是线程私有的,具体实现原理请阅读ThreadLocal的源码。
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//如果此线程已经初始化了Looper,则抛异常。
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

  1. 注意prepare的quitAllowed参数,此参数在子线程中为true,代表可以终止,主线程为false,代表不可以终止,当在主线程中终止,则会报异常。
void quit(boolean safe) {
        if (!mQuitAllowed) {//主线程是不允许退出的,因为主线程是以消息作为驱动的。
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

      ...
    }
  1. handle中的消息用到了享元模式,建议创建Message对象的时候用Handler.obtainMessage()方法,此方法有利于防止频繁的创建Message对象造成的内存抖动,但是Message的缓存的个数最大为50个,当超过50个的message的时候,仍然会创建新的Message对象。
//android.os.Handler 
private static final int MAX_POOL_SIZE = 50;
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
  1. MessageQuene采用的是单向链表的数据结构,其按照when的时间进行升序排序。

  2. IdleHandler的作用是什么

    IdleHandler的触发条件是,消息队列为空,或者第一条消息的触发时间还没到。所以如果屏障的超时时间还没有到,也就是目前还没有消息要处理,会触发IdleHandler

  3. post和send方法都会将某个Message放到MessageQuene中,send的参数直接是Message消息,而Post是将其他类型转换为Message消息放到MessageQuene中。

  4. Hander的消息派发流程,Message.callback(runnable)>Handler.mCallback>Handler.handleMessage

//android.os.Handler   
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
  1. Handler的创建,由于Hanlder创建的线程必须存在Looper,所以创建有以下两种方式。

​ 方式一:

  class LooperThread extends Thread {
        @Override
        public void run() {
            Looper.prepare();

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

            Looper.loop();
        }
    }

方式二:

//IntentService也是利用此种方式创建Handler消息的。 
HandlerThread thread_name = new HandlerThread("thread name");
 thread_name.start();
 Handler mHandler = new Handler(thread_name.getLooper());

创建HandlerThread可以直接用getLooper()方法获取Looper,是由于在getLooper()方法中增加了等待方法,其实起内部的实现原理与方法一类似,如下:

//HandlerThread的run()方法,开启线程的时候调用。
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

//此方法用来等来Looper的创建并且返回。
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

  1. 分析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 (;;) {
            //此方法可能会阻塞,造成阻塞的原因是next()方法中的nativePollOnce(),唤醒实在消息队列enqueueMessage方法中的nativeWake,此处用到了epll机制,类似java的wait(),notify()方法。
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

          ...
          
        }
    }
  1. 消息屏障,Message分为3中:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。在Android系统中存在一个VSync消息,它主要负责每16ms更新一次屏幕展示,如果用户同步消息在16ms内没有执行完成,那么VSync消息的更新操作就无法执行在用户看来就出现了掉帧或卡顿的情况,为此Android开发要求每个消息的执行需要限制在16ms之内完成。但是消息队列中可能会包含多个同步消息,假如当前主线程消息队列有10个同步消息,每个同步消息要执行10ms,总共也就需要执行100ms,这段时间内就会有近7帧无法正常刷新展示,应用执行过程中遇到这种情况还是很普遍的。

    Android系统中的异步消息就是专门解决消息处理延迟的问题,它需要配合同步屏障(SyncBarrier)一起工作,在发送异步消息的时候向消息队列插入同步屏障对象,消息队列会返回同步屏障的token,此时消息队列中的同步消息都会被暂停处理,优先执行异步消息处理,等异步消息处理完成再通过消息队列移除token对应的同步屏障,消息队列继续之前暂停的同步消息处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈德山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值