一、handler的用法
1.1、用于线程切换
Handler handler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
@Override
public void run() {
handler.post(() -> {
binding.tv.setText("hello world");
});
}
}).start();
1.2、线程间通信
为了防止handler导致内存泄露,handler对Activity采用弱引用,因弱引用不会影响Activity对象生命周期。
static class MyHandler extends Handler {
WeakReference<MainActivity> mActivityWeakReference;、
public MyHandler(MainActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
MainActivity activity = mActivityWeakReference.get();
if (activity != null && msg!=null && msg.obj!=null) {
//todo
}
}
}
MyHandler myHandler = new MyHandler(MainActivity.this);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.obj = "aaa";
mMyHandler.sendMessage(message);
}
}).start();
1.3、任务延时执行
static class MyRunnalbe implements Runnable {
@Override
public void run() {
Log.e("test", "test memory leak?");
}
}
//发送延时任务
Handler handler3 = new Handler(Looper.getMainLooper());
handler3.postDelayed(new MyRunnalbe(), 10000);
最后大家不要忘了,一定记得在Activity的onDestroy及时解除Message对Handler的强引用。
@Override
protected void onDestroy() {
super.onDestroy();
mMyHandler.removeCallbacksAndMessages(null);
}
二、Handler(Loope(MessageQueue))的创建
2.1、Handler创建
先看看handler的构造函数。构造函数肯定是相互调用,只需要看最多的参数的那个
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { xxxx }
从构造可以看出,Handler的创建需要Looper,没有Looper玩不转呐,我们之前写handler用法的时候,都是在主线程创建Handler,它构造里面会调用Looper.myLoop()获取Looper。
ThreadLocal可用于线程间的数据隔离,这是解决多线程并发的一种手段 (不共享可变资源)。将线程需要的Looper保存在ThreadLocal里面。
我们App的入口函数就是ActivityThread的main方法,一般在main方法里面就做两件事,第一件就是启动创建主线程的Looper,启动Looper轮询;第二件事就是将ApplicationThread的binder对象注册给AMS(AMS拿到它后就可以调用App的方法)。loop轮询一旦跳出了死循环,App直接就抛异常了。
如果最后一个参数async为true,那么通过handler.sendMessage的message都会打上异步消息的标签。
//from Handler
public Handler(@Nullable Callback callback, boolean async) {
.....
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
.......
}
//from Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//myLooper直接从Threadlocal中取
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//创建主线程Looper,因为主线程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();
}
}
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));
}
//from ActivityThread
public static void main(String[] args) {
.......
//创建主线程Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//向AMS注册,将app的binder对象给AMS
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
.....
//启动轮询
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Q: 如果直接在子线程里面new Handler()创建,从ThreadLocals里面肯定是取不到对应的looper的,所以会抛异常。
需要显式调用Looper.prepare()才行。
子线程中弹吐司也要先通过Looper.prepare()创建Looper,不然也会奔溃。
原因同上👆
2.1.1、ThreadLocal原理
都说ThreadLocal可用于线程间数据隔离,完美的解决并发问题,那啥原理呢? 先看看用法。public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = t.threadLocals; //线程的t.threadLocals
if (map != null)
map.set(this, value); //直接添加
else
createMap(t, value); //那就给当前线程创建这个ThreadLocalMap类型的t.threadLocals成员
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); //斐波那契散列算法
for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) { //开放寻址法解决冲突
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); //key就是ThreadLocal
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
threadlocal.set(xxx)时,会从当前线程(主线程/子线程)取出它的ThreadLocalMap类型的成员threadLocals,不为null就直接添加了,为空则先创建。ThreadLocalMap底层采用数组实现,不像HashMap采用数组+链表/红黑树实现。这里的元素的index采用斐波那契散列算法实现,采用开放寻址法解决散列冲突,具体参考ThreadLocalMap的hash算法。数组元素Entry很有特点。
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry是一个弱引用类,直接通过entry.get()可以获取到Threadlocal引用,因其作为entry的key,只能对应一个值value。同时也参与table数组index的hash算法(i = key.threadLocalHashCode & (len-1))。如通过entry.get()获取threadLocal为null时,此时value不为null,就有发生泄漏可能,一般建议就是用完及时进行remove掉。
2.2、Looper的创建
public static void prepare() {
prepare(true);
}
public static void prepare() {
prepare(true);
}
//quitAllowed 是否支持退出,默认是支持退出的。但是主线程的looper不支持退出。
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
Handler创建需要Looper,Looper创建需要MessageQueue。如果多次调用Loopr.prepare来创建Looper会报错,即在一个线程里面创建多个Looper会报错。
主线程的Looper当然不支持退出,不然随便调用looper.quit,App就奔溃了。用户子线程中通过Looper.prepare()创建Looper默认是true,支持退出的。
2.3、MessageQueue的创建
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue创建时,创建了一个long类型的成员,native可以通过这个long指针直接强转成native对象用,这里是底层的NativeMessageQueue的指针。
从这里可以看出,一个线程可以创建无数个Handler,但只能有一个Looper,一个MessageQueue。
三、Looper.loop() 与 handler.sendMessage(xx)
3.1、开启轮询消息Looper.loop()
入口函数ActivityThread.main()方法中,就开启了Looper.loop()。
//from Looper
public static void loop() {
......
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
......
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked(); //消息进入循环池
}
}
Q: 如果让我们写个Handler分发、处理Message,你会怎么写?
因为Handler的分发事件可能在其他线程,被称为生产者,处理Message的Handler被称为消费者,典型的生产消费模型,肯定会考虑生产多了+消费慢,生产少了+消费快的场景,此时伪代码如下:
public synchronized void loop() {
long now = System.currentTimeMillis();
int count; //mMessageQueue.top()就是获得一个Message
while ((count = mMessageQueue.size()) == 0 || mMessageQueue.top().when > now) {
long diff = (count == 0) ? 0 : (now - mMessageQueue.top().when);
try {
this.wait(diff); //这里就类似Message msg = queue.next();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
......
this.notifyAll();
}
系统肯定不会用wait/notify+synchronized这种性能欠佳的组合实现,对的,底层用的是Epoll机制实现
来看看MessageQueue的Next方法。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands(); //被调用说明后面的代码可能会引起线程阻塞(此处不懂)
}
nativePollOnce(ptr, nextPollTimeoutMillis) //epoll_wait
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
/此处msg没有target(handler),那就表示这个msg为同步屏障消息,配合异步消息使用的
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//获取需要等待的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//终于拿到一个可用的Message了,单链表取出Message的操作,模板代码
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next; //所以mMessage就是下一个要处理的Message。记住了
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1; //-1传给native的epoll_wait,表示永远不能唤醒,需要主动被wake
}
//messageQueue为empty了 或者 mMessage是将来才处理的(when还没到)
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true; //那就继续阻塞
continue;
}
//每次处理的handler最多只能是4个,多了不处理????
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = idler.queueIdle(); //idleHandler的queueIdle返回值true/false处理不同
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0; //处理完idle消息后,立马就唤醒了,接下来再取新的。
}
}
nextPollTimeoutMillis(阻塞的超时时间)有两种情况
第一种:首次启动loop,nextPollTimeoutMillis = 0,进入到nativePollOnce(ptr, nextPollTimeoutMillis) 这里,其底层epoll_wait(epFd,events,MAX_EVENTS,timeout)阻塞,这个阻塞可因超时时间到了或者关注的事件可读了被唤醒,这里timeout为0,所以很快会被唤醒。
第二种:非首次,nextPollTimeoutMillis就来自 (msg.when-now),epoll_wait的超时时间就可能不是0了,时间未到,就阻塞着,直到被唤醒。
3.1.1、nativePollOnce(ptr, nextPollTimeoutMillis)的底层调用
看源码C++代码可以使用官方网站:Android Code Search
private native void nativePollOnce(long ptr, int timeoutMillis);
之前说java层创建MessageQueue的时候,native层返回一个NativeMessageQueue的指针,NativeMessageQueue的创建伴随着native层的Looper的创建。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //源码是CPP所以new的
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue); //long类型的指针
}
//NativeMessageQueue的构造函数:通过参数化表进行构造
NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false); //创建了一个native层Looper,给native线程绑定了一个Looper
Looper::setForThread(mLooper);
}
}
nativePollOnce的调用,发现java层的long类型指针强转成nativeMessageQueue,这是常规操作,java层传递过来的timeoutMillis一路携带。
//nativePollOnce方法
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) { //java的long类型指针这里又强转成nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
....
mLooper->pollOnce(timeoutMillis);
......
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
......
//与handler功能关系不大的逻辑略去,这里是用底层Looper干其他的事情。
......
result = pollInner(timeoutMillis);
}
}
下面👇的方法是重点 epoll_wait出来了。
//timeoutMillis一路携带
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
.....
int result = POLL_WAKE;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //经典的Epoll机制
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPolling = false;
.....
for (int i = 0; i < eventCount; i++) {
const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
if (seq == WAKE_EVENT_FD_SEQ) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
......//处理其他非handler的功能
}
}
return result;
}
void Looper::awoken() {
uint64_t counter;
//及时从管道mWakeEventFd.get()读描述符中读取数据
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
3.1.1.1、为了理解这里的调用可以看个管道配合Epoll的例子 具体来自这个博客
早期版本4.4及其以下版本,handler底层用的管道+Epoll实现,现在采用eventfd()+Epoll机制实现。
..... //Mac上用CLion跑demo发现Mac找不到epoll的库
int main(){
int ret,pid;
int pipe_fd[2];
if((ret=pipe(pipe_fd))<0) { //调用linux的pipe方法,获取读写描述符,读0 写1
return -1;
}
const int MAXEVENTS = 1024;
struct epoll_event events[MAXEVENTS]; //就绪列表
struct epoll_event ev; //epoll_event事件
ev.data.fd = pipe_fd[0]; //读描述
ev.events = EPOLLIN|EPOLLET; //EPOLLIN事件触发表示对应的文件描述符上有可读数据。EPOLLET边缘触发
int epfd=epoll_create(MAXEVENTS); //创建epoll的句柄,size(MAXEVENTS)用来告知内核监听的数目
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,pipe_fd[0],&ev);//注册要监听的事件,pipe_fd[0]再次传入。
if (ret != 0){
close(pipe_fd[0]);
close(pipe_fd[1]);
close(epfd);
return -1;
}
//fork()有意思:会返回两次分别在父子进程返回,pid>0表示的是父进程,返回的pid==0表示子进程,
if((pid=fork())>0) {
int count=epoll_wait(epfd,events,MAXEVENTS,5000);
char r_buf[100];
for(int i=0;i<count;i++){
if((events[i].data.fd==pipe_fd[0])&&(events[0].events&EPOLLIN)){
int r_num=read(pipe_fd[0],r_buf,100); //及时从管道中读
cout<<"read "<<r_num<<" bytes data from the pipe : "<<r_buf<<endl;
}
}
close(pipe_fd[1]);
close(pipe_fd[0]);
close(epfd);
}else if(pid==0){
close(pipe_fd[0]); //子进程的资源继承自父进程
char w_buf[100];
strcpy(w_buf,"hello world");
if(write(pipe_fd[1],w_buf,12)!=-1) //往写描述符中写入
cout<<"data had written!\n";
close(pipe_fd[1]);//write
}
return 0;
}
epoll的三个关键函数epoll_create返回epollFd,epoll_ctl注册要监听的事件,epoll_wait阻塞等待关注的事件到来。
epoll_wait函数说明
- 返回值:成功时返回就绪列表中文件描述符的个数,超时返回0,异常返回-1。
- timeout:指定epoll的超时时间,单位是毫秒。当timeout为-1是,epoll_wait调用将永远阻塞,直到某个事件发生。当timeout为0时,epoll_wait调用将立即返回。
- maxevents:指定最多监听多少个事件,必须大于0。
- events:检测到事件,将所有就绪的事件从内核事件表中复制到它的第二个参数events指向的数组中。
3.1.1.2、mWakeEventFd
Looper创建时会创建Epoll实例,然后注册 mWakeEventFd。
//Looper创建
Looper::Looper(bool allowNonCallbacks):.....{
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
AutoMutex _l(mLock);
if (mEpollFd >= 0) {
mEpollFd.reset();
}
// Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
......
}
//捕捉一下WakeEventFd的操作
using unique_fd = unique_fd_impl<DefaultCloser>;
android::base::unique_fd mWakeEventFd;
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
......
}
void Looper::awoken() {
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
之前例子采用管道读写,通过pipe方法实现,轻易获得读、写描述符,读写描述符分开的,而这里读写都是mWakeEventFd.get(),通过eventfd函数创建fd。
什么是eventfd
eventfd是Linux 2.6提供的一种系统调用,它可以用来实现事件通知。eventfd包含一个由内核维护的64位无符号整型计数器,创建eventfd时会返回一个文件描述符,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值,从而实现进程间通信。(mac完全用不了这个epoll和eventfd,尴尬)
EPOLLIN的触发条件是内核接收到新发来的数据,从不可读状态变为可读状态。
一旦我们 nativePollOnce(ptr, nextPollTimeoutMillis)经过底层走一圈,混到timeout时间到了,epoll_wait返回,不阻塞了,就进入java层做取消息的操作。
因java取消息它是优先判断链表头是不是同步屏障,所以接下来先看同步屏障消息和异步消息👇。
3.1.2、同步屏障消息和异步消息
Message的三种类型 普通消息、同步屏障、异步消息,区分它们很简单。
- 普通消息 我们一般发送的Message默认就是,其target就是handler。
- 同步屏障 因没有message.target,所以不需要分发,(正常发送的消息必须指定target),没有handler的消息就可以认定为同步屏障。它也有时间戳,只会影响后面的消息。消息队列可以插入多个同步屏障,插入同步屏障没有唤醒线程处理消息,普通消息插入会唤醒。插入同步屏障的会返回token(序列号),作为remvoe同步屏障方法的参数用。
- 异步消息 设置过msg.setAsynchronous(true)的Message。
//MessageQueue的next方法部分逻辑 被唤醒后,优先判断是否同步屏障+异步消息组合
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
如果链表头(msg != null && msg.target == null)是同步屏障消息,那就去找下一个异步消息,二者狼狈为奸,阻挡正常普通消息的处理,此时普通消息就算达到when了,也只能乖乖等着。一般同步屏障+异步消息用于UI绘制这种需要立即响应的场景。
3.1.1.1、系统中使用同步屏障的场景
//当我们更新Ui时喜欢调用:
binding.tv.requestLayout();
// ||||其调用路径||||
view->requestLayout()
viewRootImpl->requestLayout()
viewRootImpl->scheduleTraversals()
-----------------------------------------------------------------------------
//from viewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true; //标记位为true
//发送同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//编舞者添加监听
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
//from MessageQueue
//插入同步屏障消息到链表,不走handler插入,直接在MessagQueue里面插入
private int postSyncBarrier() {
long when = SystemClock.uptimeMillis();
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain(); //没有设置target操作
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages; //链表头消息
if (when != 0) { //同步屏障也遵循按处理时间插入,没搞特殊化
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // 插入同步消息
msg.next = p;
prev.next = msg;
} else { //插入到链表头
msg.next = p;
mMessages = msg;
}
return token; //同步屏障的token,用于取消用
}
}
当我们调用View的requestLayout时,没有立马绘制,而是由ViewRootImpl来协助走绘制流程,先发送同步屏障消息,注册Choreographer监听(用户添加监听还可用于屏幕帧率获取,固定脉冲频率60Hz或者120Hz,它向SurfaceFlinger发起请求,获取脉冲Vsync的到来),脉冲信号到来的回调-->FrameDisplayEventReceiver的onVsync()。
//from Choreographer
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
........
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this); //this就是👇的run方法
msg.setAsynchronous(true); //发送异步消息
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime(); //当前时间跟脉冲时间diff
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { //这个日志太常见--丢帧提示
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
//请求下一个垂直同步信号
scheduleVsyncLocked();
return;
}
......
mLastFrameTimeNanos = frameTimeNanos;
}
......
//Choreographer的一些回调
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
// frmo ViewRootImpl 前面mChoreographer.postCallbac设置的回调转到mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障消息
performTraversals(); //接下里就是测量、布局、绘制了
}
}
同步屏障插入了,脉冲信号来临时,onVsync回调异步消息插入,当从消息队列里面取出消息时,如果同步屏障消息在链表头,那么插入的异步消息会很快得到处理,在真正开始执行绘制前,移除同步屏障。
如果消息队列为空 or 也没有同步消息 or 待处理的普通消息还没有到时间,那就该处理IdleHandler了
👇。
3.1.3、IdleHandler的处理
- 处理时机 当MessageQueue为空或者接下来的Message的when还没有到的时候。
- 使用场景 可用于启动优化,针对一些不是启动必须的操作,可以放到首页idleHandler中做延迟处理。
- idler.queueIdle()的返回值 默认是false,就只处理一次后就移除了,如果返回true,可能会处理多次。
看来确实只有在目前没有可处理的Message了才会考虑处理idleHandler。
3.2、发送消息之handler.sendMessage(xx)
//from Handler
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
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;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //这个this就是Handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
用户使用的各种post/postDalay/sendMessage最终都是会走到enqueueMessage方法,在这里给Message设置target,target就是Handler。如果handler构造参数asynchronous传入true,就在这里给Message添加异步标志。
boolean enqueueMessage(Message msg, long when) {
.....
//考虑多线程,这里用了 synchronized
synchronized (this) {
......
msg.markInUse();
msg.when = when;
Message p = mMessages; //mMessages表示接下来要处理的Message,可能是null
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; //前面处在nativePollOnce的epoll_wait那就阻塞了为true
} else {
//要插入的消息比接下来要处理的消息(mMessages)的when还晚
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.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在这里传入的msg不可能是同步屏障消息,因同步屏障消息直接通过postSyncBarrier插到队列里面。(注意postSyncBarrier是私有api,我们用不了,除了反射)
- if (p == null || when == 0 || when < p.when) 表示链表头为null或要处理的比链表头mMessages还要早,那肯定要将msg插入到链表头了。一般mBlocked为true,然后就唤醒了。
-
needWake = mBlocked && p.target == null && msg.isAsynchronous() 能走到这里就说明插入的消息处理时间比队列头的msg晚,一般是不唤醒的,但是有特殊情况(链表头为同步屏障+首个异步消息插入)。
假设当前mBlocked是true,队头的mMessages是同步屏障,要插入的消息为异步消息,neekWake此时为true。 - 1、进入循环,同步屏障消息在链表头且队列中无异步消息,如果要插入的消息是异步消息,就会被唤醒。
- 2、进入循环,同步屏障消息在链表头且队列中有异步消息,即使再插入消息,也不会唤醒。
总结一下插入消息能被唤醒的场景:
1、链表头为null 或者 插入的普通消息/异步消息比链表头消息的处理时间早。
2、链表头为同步屏障+首个异步消息插入。
如当前链表头是同步屏障消息,插入的异步消息按时间处理顺序即使插入到队列中很靠后的位置,也不影响异步消息的处理时效,仍是优先处理的。
//loop取消息时,如果链表头是同步屏障消息,优先从队列里面找异步消息处理。
......
nativePollOnce(ptr, nextPollTimeoutMillis)
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//看到msg没有target(handler),那就表示这个Message为同步屏障消息,配合异步消息使用
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
......
//处理普通消息
......
3.2.1、nativeWake(mPtr)
//from java MessageQueue
private native static void nativeWake(long ptr);
//from NativeMessageQueue
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
// from native的Looper
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
.....
}
nativeWake是在mWakeEventFd.get()里面写入了unit64_t的1。mWakeEventFd.get()的eventfd包含一个由内核维护的64位无符号整型计数器,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值。当这里写入数值时,那epoll_wait就能返回了,这里的inc改成其他值应该也可以,因为我们只关心epoll_wait是否返回不再阻塞。
总结一下:epoll_wait在timeout超时时间到来时或mWakeEventFd.get()有数据可读时会返回,不再阻塞。
3.3、处理消息之handleMessage
用户可能用3种方式去处理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //1
handleCallback(msg);
} else {
if (mCallback != null) { //2
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //3
}
}
Message在获取时,有传入callback就走1,handler构造函数传入了Callback,就走2,默认(mCallback.handleMessage(msg))的值为false,false表示我们还可以继续处理3,如果是返回值为true,一旦被处理过了,那我们重写的handleMessage方法还是没法调用。
四、关于Handler的其他问题
4.1、一般我们固定频率的任务(定时器)怎么实现比较好呢?
- 方式一、handler.postDelayed,但是精度么呵呵呵,handler发送普通的延时消息,通过epoll_wait的超时机制实现,而且前面有Message如果处理不及时会导致精度更差了。
- 方式二、Timer:底层通过synchronize+wait(diff)实现。
- 方式三、AlarmManager,用的少。
- 方式四:固定频率的线程池执行任务。
- 方式五:Choreographer添加监听实现,频繁获取帧率可用它,相当于16ms的定时器。不知道有没得兄弟在实际场景中用过。
4.2、主线程Looper为啥没有ANR?
入口函数ActivityThread的main中开启了Loop.loop(),在queue.next()获取下一个Message时会走底层的epoll机制,进行阻塞等到超时时间到来时唤醒,就跟我们用synchronize+wait(diff)类似,wait是线程阻塞。
ANR机制 ANR大家都知道是应用程序无响应,而且这个应用程序无响应的弹窗不是在我们app进程,而是在AMS所在的进程(SystemServer进程)。
Service TimeOut: 前台20s,后台服务200s。
广播 Timeout: 前台广播10s,后台广播60s
contentProvider Timeout: 10s
InputDispatching Timeout: 5s
这里的超时,前面3大组件都是启动创建超时导致ANR,最后一个是用户操作导致ANR。
先就从最简单的Service启动看起吧。
//通过startService启动service
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
//from ContextImpl 很关键的一个类,四大组件中只要attach上下文就需要它
@Override
public ComponentName startService(Intent service) {
.......
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {
...... //binder调用来了
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
........
return cn;
}
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
我们的app能够调用AMS的方法,肯定要先拿到AMS的binder代理对象,这里通过ActivityManager.getService()就可以搞到,发起binder调用AMS的startService。(AMS是在SystemServer进程,跟我们app不在同一个进程,看到源码里面以I开头的类,基本就当做binder对象类处理),同样AMS想要调用我们app的方法,也要通过binder调用,也要搞到我们app的binder代理对象(IApplicationThread)才行。
AMS有啥用,四大组件的创建启动都归他管,我们可以通过Instrumentation去捕捉四大组件的生命周期方法。
//from AMS
AMS-->startService(XXX)
AMS--->startServiceLocked(XXX)
AMS--->startServiceInnerLocked(XXX)
AMS--->bringUpServiceLocked(XXX)
AMS--->realStartServiceLocked(XXX)
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
//创建前埋入炸弹
bumpServiceExecutingLocked(r, execInFg, "create");
boolean created = false;
try {
....... //创建Service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
.......
} finally {
if (!created) {
//服务创建done 拆除炸弹
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
}
.......
}
Service创建前,肯定有很多准备工作,先判断要启动的service的进程是否存在,不存在还得走Zygote创建进程,然后进程创建后还得向AMS注册,如果进程存在,且ServiceRecord没有值,就表示之前没有创建过service,就走首次创建Service逻辑,Service创建前,就埋入bomb,Service创建完成,拆除bomb。
//from AMS
AMS----bumpServiceExecutingLocked(XXX)
AMS----scheduleServiceTimeoutLocked(XXX)
static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
.......
Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
所谓的埋入炸弹就是通过handler发了一个延时消息消息,延时的值前台服务20s、后台服务200s,这个Handler不是我们app的mH,是AMS进程中的Handler。
再看看真正创建Service的操作
//from ApplicationThread
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
.....
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
......
//真正创建Service的逻辑
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
Service service = null;
try {//先创建上下文ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//获取所在的进程
Application app = packageInfo.makeApplication(false, mInstrumentation);
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//直接反射创建Service实例
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
//加载资源
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
.....
//添加上下文
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try { //binder调用AMS的serviceDoneExecuting
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
......
}
}
Service创建是在我们自己的app进程,自己通过反射构造的Service实例,调用service的onCreate的后,及时binder调用AMS的方法serviceDoneExecuting拆除💣(remove掉那个延时Message)。
//from AMS
AMS---serviceDoneExecuting(XXX)
AMS---serviceDoneExecutingLocked(XXX)
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
if (r.executeNesting <= 0) {
if (r.app != null) {
.......
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//要启动的Service搞完了就移除消息
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
......
}
......
}
.....
}
}
如果说Service创建时间很长,那就处理ActivityManagerService.SERVICE_TIMEOUT_MSG的超时Message。
//from AMS
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.....
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
........
void serviceTimeout(ProcessRecord proc) {
......
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
//from AppErrors
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
......
// Bring up the infamous App Not Responding dialog (infamous--臭名昭著的)
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
}
ANR的弹窗就可以显示出来了。(infamous--臭名昭著的)。
所以ANR跟Loopr.loop()完全是两个不同的功能,ANR需要Looper的支持,没得Looper,ANR不知道何时弹窗。
4.3、BlockCanary如何检测UI卡顿。
先看看Loopr.loop(),仔细看看在message处理前后有啥。
//from Looper
public static void loop() {
......
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
.....
//logger-- 分发message
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
......
msg.target.dispatchMessage(msg);
......
//logger-- message处理完成
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
msg.recycleUnchecked(); //消息进入循环池
}
}
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
从上面看logging.println在msg处理前后都打印日志了,如果能够捕捉到日志打印的时机,基本是就可以知道handleMessage耗时多少了,拿到耗时就可以判断出是否卡顿了,而且贴心的是这个Printer是Looper的成员变量,可以让用户设置自己的Printer。
我们看看blockCanary的自定义Printer以及设置时机
class LooperMonitor implements Printer {
private long mStartTimestamp = 0;
private long mStartThreadTimestamp = 0;
......
@Override
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
if (!mPrintingStarted) {
mStartTimestamp = System.currentTimeMillis();
......
} else {
final long endTime = System.currentTimeMillis();
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
......
}
}
private boolean isBlock(long endTime) {
return endTime - mStartTimestamp > 3000;
}
//设置Looper的Printer
//BlockCanary
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(new LooperMonitor(xxxx));
}
}
BlockCanary自定义了Printer,然后捕捉两次时间diff,diff大于3s就认定有卡顿发生,可以dump出堆栈显示。设置Printer的时机是在入口类BlockCanary的start方法里面。
4.4、池化技术
池化技术采用享元设计模式实现,Message的池化、Glide中的BitmapPool、EventBus中的PendingPost等等。
我们关心的是池的设计,采用啥容器存储、size选取、以及recycle、obtain方法的处理。
Message的池化:存储采用单链表实现,链表长度是50,主要看obtain和recycle方法。
//Handler中Message的池化操作
public final class Message implements Parcelable {
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50; //size最大50
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
if (isInUse()) {
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
sPool、sPoolSync都是静态的,每个Message对象共享,obtain就是从单链表中取出一个Message,单链表没有就new 新的Message,recycle是先重置Message,然后看message池中size是否大于MAX_POOL_SIZE,超了就不加入,单链表的常规操作,简直不要太简单。
Glide中的BitmapPool
//LruBitmapPool默认: 只有1 or 4张的屏幕大小的Bitmap可以复用;
GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<>();
存put 取get 基于有限的size来看是否需要trim。
EventBus采用ArrayList来存储,超了就不添加
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
五、补充
MessageQueue 中还有一些native方法未涉及到,就是基于NativeMessagQueue做些其他的功能实现。是public的Api,目前暂未使用过。如有童鞋知道,可以评论告知。
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
.....
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
}
六、相关推荐
【Android源码解析】Android中高级架构进阶学习——百大框架源码解析Retrofit/OkHttp/Glide/RxJava/EventBus...._哔哩哔哩_bilibili
本文转自 https://juejin.cn/post/7016291737039536142,如有侵权,请联系删除。