Android面试准备之Android基础

本文深入剖析了Android中Handler、Looper和MessageQueue的工作原理,详细阐述了主线程Looper的初始化和Message的入队过程,以及如何保证线程安全。同时,介绍了View的绘制流程,从setContentView开始,讲解了如何将布局设置到窗口并进行UI更新的线程检查。最后,探讨了触摸事件的处理,包括ACTION_DOWN、ACTION_MOVE和ACTION_UP等不同阶段的关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Handler机制

创建一个Handler

//提示已过时
Handler handler = new Handler();
Handler handler = new Handler(Looper.myLooper());

隐式指定LooperHandler初始化方法已被Android 11报过时,根据注释,是由于不指定Looper在一些场景下会导致任务丢失或程序崩溃,比如没有Looper的线程。

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

下面寻找这个Looper是在哪里初始化的

ActivityThread.java#main

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

Looper.java

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

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

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

根据ThreadLocal原理,调用Looper.prepare(boolean quitAllowed)时将该<ThreadLocal<Looper>,Looper>键值对设置到了当前线程的成员变量ThreadLocal.ThreadLocalMap threadLocals中,从而保证了每个线程对应唯一一个Looper,且不能被重复初始化,否则将抛出异常。而且Looper对象在初始化时也初始化了MessageQueue,并将当前线程保存在成员变量中。

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

以上初步实现了主线程对应sMainLooper,同时对应唯一一个MessageQueue
下面分析LooperMessageQueueHandler如何运转起来

public static void loop() {
   
   
	final Looper me = myLooper();
	...
	final MessageQueue queue = me.mQueue;
	...
	//死循环
	for (;;) {
   
   
		Message msg = queue.next();
		...
		try {
   
   
			//将消息分发给相应Handler处理
            msg.target.dispatchMessage(msg);
            ...
        } 
	}
	...
}

下面看Handler处理消息的过程
sendMessage()postDelayed()post()都会调用到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);
    }

下面看MessageQueue#enqueueMessage

boolean enqueueMessage(Message msg, long when) {
   
   
	...
	synchronized (this) {
   
   
		...
		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 {
   
   
			...
			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;
		}
		...//唤醒线程本地方法
	}
}

可以看到,消息入队时加了锁,保证了多个HandlerMessageQueue添加数据时的线程安全。添加消息首先判断队列中是否有消息,没有消息的时候需要对线程进行唤醒。如果不需要唤醒,则按照延迟时间入队。至于时间的判定,来自于Handler#sendMessageDelayed


                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值