Android Handler机制

目录

Handler定义

Handler作用

Handler、Message、MessageQueue、Looper 关系图

Message 源码

重要成员变量

生成消息 obtain方法

回收消息recycle方法

处理消息

MessageQueue源码

重要成员变量

插入消息 enqueueMessage 方法

循环读取消息 next方法

ThreadLocal类

ThreadLocal类的特点

ThreadLocal类的简单示例

Looper源码

重要成员变量

Looper构造方法

初始化方法 prepare()

获取方法

loop()方法

quit()退出方法

Handler

构造方法初始化数据

消息的发送

消息的处理

为什么在主线程中初始化Handler对象不需要先初始化Looper

总结


Handler定义

Handler 即处理器,常用于跨线程通讯:

线程A 和线程 B 拥有同一个 handler 对象

在线程 A 中使用 handler 的 sendMessage() 方法发送消息

在线程 B 中使用 handler 的 handleMessage() 方法处理消息

Handler作用

子线程处理完耗时操作后,请求主线程更新 UI 
线程 A 定时给线程 B 发送消息
线程 A 周期性给线程 B 发送消息

Handler、Message、MessageQueue、Looper 关系图

Message 源码

重要成员变量


    public int what;
    public int arg1;
    public int arg2;
    public Object obj; // 常用携带数据标识
    long when; //标识当前消息的触发时间
    Handler target; //存储发送消息的handler
    Message next; // 当前消息对象的下一个消息对象的地址,通过next属性构建出一个链表结果的消息池
    private static Message sPool; //sPool属性,当前池的头指针位置, 即只存出链表的第一个消息地址
    private static final Object sPoolSync = new Object();//同步锁防止线程污染
    private static int sPoolSize = 0;//消息池大小
    private static final int MAX_POOL_SIZE = 50;//消息池最大容量

生成消息 obtain方法

    public static Message obtain() {
//        if (sPool为空){
//            return new Message();  //无可复用的消息,消息池为空
//        }
//        else {
//            return 从消息池中获取;
//        }
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool; //取出表头的Message对象返回
                    sPool = m.next; //讲链表后移,记录新的表头消息
                    m.next = null; //移除第一个
                    m.flags = 0;  // clear in-use清除标记
                    sPoolSize--; //链表长度减去1
                    return m;
                }
            }
            return new Message();
        }
    }

 sPool 是可用的空消息对象池 ,其本质是链表,sPool 指向链表的头结点,next 指向下一个节点 

回收消息recycle方法

先判断当前消息对象是否在使用中,如果 在使用中,则该消息对象无法回收 直接return,  否则调用recycleUnchecked()方法回收消息。

需要注意的是recycle方法不需要我们手动调用,它的调用实在Looper的loop()方法中自动调用
 

/**
     * Return a Message instance to the global pool.
     * <p>
     * You MUST NOT touch the Message after calling this function because it has
     * effectively been freed.  It is an error to recycle a message that is currently
     * enqueued or that is in the process of being delivered to a Handler.
     * </p>
     */
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            } //在使用中的消息对象无法回收 直接return
            return;
        }
        recycleUnchecked();
    }

 /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     * recycleUnchecked回收未在使用中的消息对象,实现链表加1
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null; //把要回收的message对象的成员变量回归初始值
	
		//重点, 实现链表连接池的加1,将message存入消息池中
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool; //把当前回收的message的next指向消息池的链表头
                sPool = this; //将自己当做新的表头
                sPoolSize++;//长度加1
            }
        }
    }

处理消息

msg.target.dispatchMessage(msg)
 
//最终会执行如下一种
msg.callback.run()
handler.mCallback.handleMessage(msg)
handler.handleMessage(msg)

target 是 Message 绑定的 handler 对象

MessageQueue源码

重要成员变量

private final boolean mQuitAllowed;  // MessageQueue是否可以退出,如果是主线程的就不能退出
private long mPtr; // used by native code,native方法中使用的,表示native代码中的MessageQueue地址
Message mMessages; // Message是一个链表结构,mMessages表示链表的第一个元素,也就是消息队列的队首
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();// 用来保存IdleHandler对象的集合
private IdleHandler[] mPendingIdleHandlers; // 用来保存IdleHandler对象的数组,在后面会将集合中的对象复制到数组中
private boolean mQuitting; // 当前消息队列是否处于退出状态
private boolean mBlocked;  // 表示在next()方法中是否被阻塞在超时时间非0的native pollOnce()方法上。
private int mNextBarrierToken; // 表示下一个Barrier(障碍物,屏障;界线)的标记(令牌)

插入消息 enqueueMessage 方法

boolean enqueueMessage(Message msg, long when) {
            ...

            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;//指示next()是否被阻止在pollOnce()中以非零超时等待。如果阻塞,则需要唤醒looper
            } 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.
                /*插入队列中间。 通常,除非队列的开头有障碍并且消息是队列中最早的
                  异步消息,否则我们不必唤醒事件队列。(队列中消息不为空,并且next()也没有阻塞)*/
                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;
            }

            // 如果looper阻塞/休眠中,则唤醒looper循环机制处理消息
            if (needWake) {
                nativeWake(mPtr);//唤醒
            }
        }
        return true;
    }

循环读取消息 next方法

当looper循环机制在MessageQueue的next()读取消息时发现消息队列中没有消息时,就会调用nativePollOnce(ptr, nextPollTimeoutMillis);将next()阻塞在PollOnce中。looper也就进入了休眠期

@UnsupportedAppUsage
    Message next() {
        // 如果消息循环已经退出并被处理,请返回此处。
        // 如果应用程序尝试退出后不支持的循环程序,则会发生这种情况。
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;//判断消息队列中是否有消息
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //就是在这里根据nextPollTimeoutMillis判断是否要阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // 尝试检索下一条消息。 如果找到则返回。
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 被障碍挡住了。 在队列中查找下一条异步消息。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {//队列中拿到的消息不为null
                    if (now < msg.when) {
                        // 下一条消息尚未准备好。 设置超时以使其在准备就绪时醒来。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 正常返回处理
                        ...
                } else {
                    // 队列中没有消息,标记阻塞looper循环进入休眠
                    nextPollTimeoutMillis = -1;
                }

                // 现在已处理所有挂起的消息,处理退出消息。
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // 空闲句柄仅在队列为空或将来要处理队列中的第一条消息(可能是屏障)时才运行。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                ...
            }

            ...

            // 将空闲处理程序计数重置为0,这样我们就不会再次运行它们。
            pendingIdleHandlerCount = 0;

            // 在调用空闲处理程序时,可能已经传递了一条新消息,
            //因此返回并再次查找未处理消息,而无需等待。
            nextPollTimeoutMillis = 0;
        }
    }

ThreadLocal类

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说无法获取到数据。

一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。

对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。


ThreadLocal类的特点

1. 每个线程都有自己的局部变量 每个线程都有一个独立于其他线程的上下文来保存这个变量,一个线程的本地变量对其他线程是不可见的


2. 独立于变量的初始化副本ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份相同的拷贝


3. 状态与某一个线程相关联ThreadLocal 是为了方便每个线程处理自己的状态而引入的一个机制

ThreadLocal类的简单示例

ThreadLocal有一个内部类ThreadLocalMap,这个类就是真正保存线程自己本地变量的容器。

每一个线程都有自己的单独的一个ThreadLocalMap实例,其所有的本地变量都会保存到这一个map中

// 创建一个ThreadLocal对象
private static ThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<Integer>(){
    // 重写方法,指定一个初始值
    @Override
    protected Integer initialValue() {
        return 0;
    }
};
 
// 测试不同多个线程同时访问
// 定义5个线程
Thread[] threads = new Thread[5];
for (int i = 0; i < 5; i++) {
    // 创建多个线程
    threads[i] = new Thread(){
        @Override
        public void run() {
            // 获取当前线程的本地变量,然后累加
            Integer integer = integerThreadLocal.get();
            for (int i1 = 0; i1 < 5; i1++) {
                integer += 1;
            }
            // 重新设置累加后的本地变量
            integerThreadLocal.set(integer);
            // 重新获取值并打印出来
            Log.i("MainActivity", Thread.currentThread().getName() + " - integer value : " + integerThreadLocal.get());
        }
    };
}
// 开启线程
for (int i = 0; i < threads.length; i++) {
    threads[i].start();
}

Looper源码

重要成员变量

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // ThreadLocal对象
private static Looper sMainLooper;  // 主线程的Looper对象
final MessageQueue mQueue; // 与Looper绑定的MessageQueue对象
final Thread mThread; // 当前Looper所在的线程

Looper构造方法

// 构造方法用private修饰,表示不能其他类不能创建,需要一个boolean类型的参数
private Looper(boolean quitAllowed) {
    // 在构造方法中与MessageQueue绑定
    // 将boolean类型的参数quitAllowed传给MessageQueue的构造
    // 我们在MessageQueue中说过这个参数的作用,true表示队列不能退出,false表示能退出
    mQueue = new MessageQueue(quitAllowed);
    // 初始化mThread变量为当前线程
    mThread = Thread.currentThread();
}

初始化方法 prepare()

// 准备方法,调用带参数的prepare()方法
public static void prepare() {
    // 参数为true,表示队列可以退出
    prepare(true);
}
// 带一个参数 quitAllowed 的prepare()方法
private static void prepare(boolean quitAllowed) {
    // 先从sThreadLocal获取当前线程的Looper对象,如果获取到了,表示当前线程已经有一个Looper了
    // 抛出一个异常,表示在一个线程当中只能创建一个Looper对象
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 当前线程没有创建过Looper,那么就创建一个Looper并指定与Looper绑定的MessageQueue可以退出
    sThreadLocal.set(new Looper(quitAllowed));
}
// 这个方法是专门为主线程创建Looper的
public static void prepareMainLooper() {
    // 同样调用带参数的prepare()方法创建Looper,但是参数为false,
    // 表示与Looper绑定的MessageQueue可以退出,也就是主线程的MessageQueue不能退出
    prepare(false);
    // 进入同步代码块
    synchronized (Looper.class) {
        // 判断成员变量 sMainLooper 是否为null,如果不为null,表示主线程已经创建过了,抛出异常
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // sMainLooper 为null,调用myLooper()方法给 sMainLooper赋值
        sMainLooper = myLooper();
    }
}

获取方法

// 从sThreadLocal中取出当前线程的Looper对象并返回
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
// 获取与当前线程绑定的MessageQueue对象
public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}
// 获取当前Looper所在的线程
public @NonNull Thread getThread() {
    return mThread;
}

loop()方法

public static void loop() {
    // 获取与当前线程绑定的Looper对象
    final Looper me = myLooper();
    // 为null,表示当前线程没有Looper对象
    // 还不是Looper线程,抛出异常,没有调用Looper.prepare()方法
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 是Looper线程,初始化queue指向当前线程的MessageQueue对象
    final MessageQueue queue = me.mQueue;
    // 确保这个线程是运行在本地进程
    Binder.clearCallingIdentity();
    // 保存一个用于跟踪的身份令牌
    final long ident = Binder.clearCallingIdentity();
 
    // 进入无限循环
    for (;;) {
        // 调用MessageQueue的next()方法从消息队列中去消息
        // 有可能会阻塞,next()方法在上一篇博客中有详细说明
        Message msg = queue.next(); // might block
        if (msg == null) {
            // 没有消息表示消息队列是退出状态,直接返回
            return;
        }
 
        // 如果调用了setMessageLogging(@Nullable Printer printer)方法
        // 那么就调用Printer接口中的方法打印日志信息
        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 {
            // 找到Message了,调用Handler中的dispatchMessage(msg)方法,分发和处理消息
            // msg.target表示Message的目标Handler,前面的博客强调过这个变量
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
 
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
 
        // 获取一个新的身份令牌,和原来的身份令牌进行比较
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            // 如果两个身份令牌不同,打印一个错误级别很高的日志(What The Fuck)
            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();
    }
}

quit()退出方法

// 调用MessageQueue中的方法,在上一篇博客中有说明
public void quit() {
    // 参数为false,表示非安全退出
    mQueue.quit(false);
}
// 安全退出MessageQueue,参数为true
public void quitSafely() {
    mQueue.quit(true);
}

Handler

构造方法初始化数据

1. 通过 Looper.myLooper()实例化了当前线程的Looper对象 mLooper
2. 并且通过looper对象获取MessageQueue mQueue
3. 从构造器中可以看出初始化Handler必须先初始化Looper对象,而在我们平时使用过程中主线程并没有先调用 Looper.myLooper()实例化Looper对象的原因是:在应用程序启动时ActivityThread主线程的main()方法中实例化了主线程中的Looper对象
 

 /**
     * @hide
     */
    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();//获取当前线程的Looper对象
        if (mLooper == null) {
            throw new RuntimeException(
            	//从此处可以看出初始化Handler必须先初始化Looper对象
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//通过Looper对象获取Looper中的消息队列MessageQueue
        mCallback = callback;
        mAsynchronous = async;
    }

消息的发送

最终是通过调用MessageQueue调用消息队列的enqueueMessage()方法进行存储消息,即入队操作。我们无论是通过调用handler的sendMessage(Message msg)、sendEmptyMessage(int what)、sendMessageDelayed(msg, delayMillis)等方法还是调用post(Runnable r)、postDelayed( )等post相关方法最终都会调用sendMessageAtTime()方法,即handler发送消息无论通过send相关方法还是post相关方法,最终都是调用sendMessageAtTime()方法发送消息


消息的处理

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                //处理消息时判断是否拦截消息mCallback.handleMessage(msg),返回true则不会执行下面的 
                //handleMessage(msg),即拦截了消息
                    return;
                }
            }
            //如果不拦截,回掉handleMessage方法处理消息
            handleMessage(msg);
        }
    }

为什么在主线程中初始化Handler对象不需要先初始化Looper

ActivityThread主线程中的main方法的源码

//此方法是应用程序的主入口
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        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.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

总结

总结

ActivityThread类

ActivityThread类的成员变量 mLooper 
final Looper mLooper = Looper.myLooper();

public static void main(String[] args) {
        Looper.prepareMainLooper();
}
————————————————————————————
Looper 类

Looper 类的成员变量 sThreadLocal 
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper 类的成员变量 mThread;
final Thread mThread; 
mThread = Thread.currentThread();

// Looper的成员变量
final MessageQueue mQueue;

//set 方法
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    map.set(this, value);

// get 方法
sThreadLocal.get(){
    ThreadLocalMap map = getMap(t);
    ThreadLocalMap.Entry e = map.getEntry(this);
}
————————————————————————————
Message类

Messag成员变量 what,Handler 
public int what;

Messag成员变量 what,Handler 
    /*package*/ Handler target;

————————————————————————————
//引用链 内存泄漏引用链
Thread >> Looper >> MessageQueue >> Message >> Handler >> 内部类 >> MainActivity


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值