安卓复习---Handler的使用及对应参数的理解,子线程创建Handler,Handler处理异步消息等

参考文档:

https://blog.csdn.net/cshichao/article/details/8787357

http://www.voidcn.com/article/p-ckonfcng-bpx.html

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/1014/6675.html

https://www.cnblogs.com/panhouye/p/6494753.html

https://blog.csdn.net/qq_30379689/article/details/53394061

https://www.cnblogs.com/xiaowangabc/p/3973927.html

Handler作为线程之间传递消息的机制,当它被创建时,就会自动关联当前所在位置的线程和消息队列。Handler通过把Message或Runnable塞进消息队列,等待适当时机处理对应操作。

(1)Handler的创建:

基本的创建如下:

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

但是这样创建,系统会提示你:This Handler class should be static or leaks might occur,大意是指Handler必须是静态类,否则容易引起内存泄露。因为消息队列中等待被被处理的消息持有Handler对应的引用,上述代码创建的Handler是一个匿名内部类,它持有所在外部类的引用(不只是匿名内部类,内部类也会引起这个问题),如果消息队列里的消息长时间未被处理,Handler对象和Handler持有的外部类引用就会一直被持有,导致系统无法及时回收,此时就容易造成内存泄露。

根据参考文档里的内容,解决这个系统提示的方法有如下两种:

1,采用new Callback的方式,本方式系统不会有内存泄露的提示:

private final Handler myHandler = new Handler(new Handler.Callback(){
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    });

但存疑的地方是,有人说这样的写法也有可能发生内存泄露,参考:https://ask.csdn.net/questions/661236

2,采用弱引用的方式,定义一个static的内部类Handler,然后让它持有Activity的弱引用

static class XXHandler extends Handler {
    //XXActivity为Handler所在的Activity
    WeakReference<XXActivity> mActivity;
    
    MyHandler(XXActivity activity) {
        mActivity = new WeakReference<XXActivity>(activity);
    }
 
    @Override
    public void handleMessage(Message msg) {
        XXActivity theActivity = mActivity.get();
        switch (msg.what) {
            //对应的具体操作
        }
    }
};

//具体操作的代码如下
//实例Handler,持有对应Activity的弱引用
XXHandler xHandler = new XXHandler(this)
//在需要操作的方法内执行对应操作
private void doSomething() {
    xHandler.sendEmptyMessage(0x111);
}

使用静态内部类为什么会避免内存泄露呢?static修饰的东西不属于类本身,自然也不会持有类的引用,但系统却要为这些静态的东西开辟额外的内存空间,如果一个应用里含有大量的静态修饰的东西,容易造成卡顿,所以适时释放内存是必要的。

****在子线程中创建Handler****

class MyThread extends Thread {
    @Override
    public void run() {
        //创建Looper对象并关联到子线程
        Looper.prepare();
        //创建Handler对象
        Handler handler = new Handler(Looper.myLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
        //调用loop方法循环遍历消息队列里的消息并发给Handler处理
        Looper.loop();

    }
}

(2)Handler参数的说明:

1,Message:不同线程之间用于传递消息的封装,通过what区分消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。

***new Message和obtainMessage的区别***

Message msg = new Message(),这是直接在内存中申请空间,相对效率更低,如果多次使用new的方式创建Message,也容易造成内存紧张。

obtainMessage方法的源码:

@NonNull
public final Message obtainMessage(int what) {
        return Message.obtain(this, what);
    }
//
public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        return m;
    }
//
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();
    }

通过源码发现,obtain方法里,如果Handler已经有了对应的what标记的Message就不再次创建,直接取出来使用,反之则新建一个Message。

做个测试,代码如下:

private final Handler myHandler = new Handler(new Handler.Callback(){
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case UPDATE_TXT:
                    String times = (String) msg.obj;
                    txtHint.setText("Handler更新TextView,time=" + times);
                    break;
            }
            return false;
        }
    });
//即时操作
private void sendNow() {
        Log.e(TAG, "sendNow");
        long times = System.currentTimeMillis();
        Message message = myHandler.obtainMessage(UPDATE_TXT);
        message.obj = "sendNow" + times;
        message.sendToTarget();
    }
//延时操作
private void sendDelay() {
        Log.e(TAG, "sendDelay");
        long times = System.currentTimeMillis();
        Message message = myHandler.obtainMessage(UPDATE_TXT);
        message.obj = "sendDelay" + times;
        myHandler.sendMessageDelayed(message, 30 * 1000);
    }

如果只调用sendNow方法,txtHint直接更新内容;如果只调用sendDelay,则在30秒后更新txtHint;但如果调用sendDelay后,在30秒的延迟时间里调用sendNow呢?此时Handler会将指定的what参数的Message取出,然后取消掉对应的延时操作,直接将此Message转给当前的操作,所以会立即更新txtHint,而延时30秒的操作就地取消。

**********************************************************************************

****Handler处理异步Message****

参考:https://www.jianshu.com/p/d28922aa5da9  https://blog.csdn.net/milllulei/article/details/80927539

通常创建的Handler调用send方法发送的消息都是同步消息,插入到队列中根据触发时间排序。Android系统16ms会刷新一次屏幕,如果主线程的消息过多,在16ms之内没有执行完,必然会造成卡顿或者掉帧。如果要让消息没有延时直接处理呢?此时可以用异步消息,使用异步需要用到一个叫做同步屏幕的东西,目的是让异步消息不用排队等候处理。同步屏障的作用就是一堵墙,先堵住同步消息,让异步消息先执行完后这堵墙消失,接着再执行同步消息。

查看MessageQueue的postSyncBarrier(低版本是enqueueMessage)方法的源码:

private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        //将一个新的同步屏障插入到消息队列
        //屏障的目的就是阻拦其他同步消息的处理,所以不需要唤醒消息队列
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            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) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

从源码看出方法里生成了一个没有target的Message也就是这个消息没有指定Handler对象(同步消息都有指定Handler),然后将这个消息插入到队列前排,然后再结合MessageQueue的next方法:

Message next() {
        ...
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //根据nextPollTimeoutMillis的值调用本地C++代码阻塞线程
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //如果msg是postSyncBarrier方法生成的同步屏障,则while循环直到取出异步消息
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
            ...
        }
    }

从postSyncBarrier方法生成的消息没有target,符合if (msg != null && msg.target == null)的判断条件,形成一个同步屏障阻塞同步消息,while循环取出异步消息进行处理。

如何发送异步消息:

<1>Message.setAsynchronous(true)

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
Message msg = myHandler.obtainMessage(ASYNC_TXT);
msg.setAsynchronous(true);
msg.sendToTarget();

<2>Handler的构造方法,传入 async 参数

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()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
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);
    }

以此方式构造的Handler发送的消息都是异步消息。

使用和移除消息同步屏障

设置一个同步屏障:

//通过映射的方式调用postSyncBarrier设置同步屏障
try {
    Log.d("TAG","插入同步屏障");
    MessageQueue queue=handler.getLooper().getQueue();
    Method method=MessageQueue.class.getDeclaredMethod("postSyncBarrier");
    method.setAccessible(true);
    token= (int) method.invoke(queue);
} catch (Exception e) {
    e.printStackTrace();
}

移除同步障碍:

//通过映射的方式调用removeSyncBarrier移除屏障
try {
    Log.d("TAG","移除屏障");
    MessageQueue queue=handler.getLooper().getQueue();
    Method method=MessageQueue.class.getDeclaredMethod("removeSyncBarrier",int.class);
    method.setAccessible(true);
    method.invoke(queue,token);
} catch (Exception e) {
    e.printStackTrace();
}

---------------------------------------------------------------------------------------------------------------------------------------------

2,Handler:作为线程之间传递消息的即时接口,既可以在消息队列中创建、插入或移除消息,又可以在handleMessage方法中处理消息。

参考:https://blog.csdn.net/bzlj2912009596/article/details/80189760

https://www.cnblogs.com/baiyi168/p/6394628.html

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()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

查看源码发现,每个Handler默认配置一个Looper,没有Looper,Handler根本无法存放和处理消息队列里的消息。

Handler里比较重要的方法:

(1)obtainMessage();obtainMessage(int what);obtainMessage(int what, Object obj);obtainMessage(int what, int arg1, int arg2);obtainMessage(int what, int arg1, int arg2, Object obj);

从Handler中判断是否有对应的Message引用,有的话直接取出使用,没有再new创建。方法可以接sendToTarget()方法进行链式调用,sendToTarget方法其实还是调用了sendMessage方法,源码如下:

public void sendToTarget() {
        target.sendMessage(this);
    }

链式调用的代码举例如下:

myHandler.obtainMessage(UPDATE_TXT).sendToTarget();

(2)getLooper(),获取Handler的looper对象。

(3)post(Runnable);postAtTime(Runnable,long);postDelayed(Runnable long);postAtFrontOfQueue(Runnable r);

通过查看源码发现这些方法内部其实还是调用了sendMessage等方法,postAtFrontOfQueue方法里实际调用的是sendMessageAtFrontOfQueue(getPostMessage(r)),表示将runnable插入到消息队列头部。

(4)sendEmptyMessage(int what);sendEmptyMessageAtTime(int what, long uptimeMillis);sendEmptyMessageDelayed(int what, long delayMillis);sendMessage(@NonNull Message msg);

sendMessageAtTime(@NonNull Message msg, long uptimeMillis);sendMessageDelayed(@NonNull Message msg, long delayMillis);

Handler用于发送Message的方法,调用sendEmptyMessage系统会自动构建一个message并且设置what参数然后发送。

//查看Handler的post方法的源码,调用的还是sendMessageDelayed方法
public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
//post方法里调用此方法将Runnable封装为Message供Handler发送
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
//sendMessageDelayed最终还是调用sendMessageAtTime
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
//先判断消息队列是否存在,不存在则抛出异常,存在则enqueueMessage方法将消息存入消息队列里
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);
    }

(5)hasMessages(int what);hasMessages(int what, @Nullable Object object);

检查消息队列是否存在对应的Message,实际应用场景用于判断前一个what的Message是否执行完毕,再进行后续的操作。

(6)removeCallbacks(Runnable r);removeCallbacks(Runnable r, Object token);removeMessages(int what); removeMessages(int what, Object object) ;

删除消息队列中的指定消息,已经在执行的消息不能被删除。

(7)removeCallbacksAndMessages(Object token);删除所有的消息(包括已发送的)和回调,当传的token为null时,直接删除Handler里的所有消息和回调。

原文:Remove any pending posts of callbacks and sent messages whose obj is token. If token is null, all callbacks and messages will be removed.

注意的是,Handler的生命周期与Activity并不一致,如果Activity要被系统销毁,但Activity中的Handler还有消息要处理,此时会造成阻塞Handler无法释放,同时Activity也无法正常释放,造成内存溢出。

此时可以调用Handler.removeCallbacksAndMessages(null),将Handler中所有的消息和回调都删除,避免Handler阻塞造成的内存溢出。

---------------------------------------------------------------------------------------------------------------------------------------------

3,MessageQueue

参考:

https://www.jianshu.com/p/8c829dc15950

https://blog.csdn.net/nmyangmo/article/details/82260616

https://haokan.baidu.com/v?vid=11527397145831332654&pd=bjh&fr=bjhauthor&type=video

一个Handler配置一个Looper,一个Looper维护了一个MessageQueue,MessageQueue(消息队列)如同一个集合存储Handler传入的Message消息,每插入一个Message都会根据message的触发时间进行重新排序,Handler的handleMessage方法处理触发时间最早的message。

一个线程只能有一个Looper和一个MessageQueue,但可以有多个Handler,下面的代码以在子线程中创建Handler为例做说明:

class MyThread extends Thread {
    
    private Handler handler;//与该子线程关联的Handler
    private Handler handler2;//与该子线程关联的Handler

    public void run() {
        Looper.prepare();//创建该子线程的Looper
        handler = new Handler(){//默认与当前线程关联
            public void handleMessage(android.os.Message msg) {
                Log.d("当前子线程是----->", Thread.currentThread()+"");
            };
        };
        //可以创建多个Handler
        handler2 = new Handler(){//默认与当前线程关联
            public void handleMessage(android.os.Message msg) {
                Log.d("当前子线程是----->", Thread.currentThread()+"");
            };
        };
        Looper.loop();//调用了该方法才能不断循环取出消息
    }
}

从代码中可以看到MyThread里可以生成多个Handler,但Looper和MessageQueue在一个线程中却都只有一个。在MyThread中要正常使用Handler的功能,首先要Looper.perpare()创建适用于MyThread的Looper,同时Handler调用无参的构造方法,(无参构造器默认与当前线程相关联),最后不能忘记Looper.loop(),这样Lopper才能循环轮询消息队列里的消息。

(1)MessageQueue.enqueueMessage,添加Message进消息队列

当Handler调用sendMessage(sendMessageDelayed等)方法时,大致会由sendMessage-->sendMessageDelayed-->sendMessageAtTime-->enqueueMessage,并最终指向MessageQueue.enqueueMessage方法,此方法为将Handler传递过来的消息插入到指定的消息队列里等待处理,源码如下:

boolean enqueueMessage(Message msg, long when) {
        //msg.target就是发送此消息的Handler,如果为空,抛出异常
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //判断是否有相同的Message正被使用,如果是也不再执行下一步
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        synchronized (this) {
            //mQuitting表示此消息已被消息队列放弃,消息被回收
            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;//指定消息的触发时间,但实际处理消息的时间可能更晚
            //mMessagess是消息队列里的第一个元素(头元素)
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
               //如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,
                //此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //如果此消息是延时的消息,则将其添加到队列中,原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,延迟的时间越长,越靠后,
                //这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程。
                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;
    }

整个方法的流程:先判断Message关联的Handler是否为空,而且判断这个Message是否以备使用,如果都正常再进入synchronized中的代码,先判断是否被消息队列放弃了,没有放弃再标记自己正处于被使用的状态,Message是一个链表类型,Message.next表示在它后面的Message对象,mMessagess是消息队列里的第一个元素。

if (p == null || when == 0 || when < p.when) {这句话的意思是,如果mMessagess为空或者传进来的msg不是延时消息或者msg的延时期间比mMessagess的还要短,将msg作为消息队列里第一个元素,原先的mMessagess放在msg的后面(msg.next = p),needWake = mBlocked判断Looper的获取消息的线程是否阻塞,如果阻塞则立即唤醒去取消息。

在else的分支里,for循环里,循环取出前一个和后一个Message元素(prev和p),如果后一个元素p为空或者msg的when比p的延时时间when靠前,则退出循环,将msg插入到前一个消息prev的后面(next),将后一个消息p插入到msg的后面(next)。

所以MessageQueue的enqueueMessage方法作用:插入Message消息到消息队列里;唤醒Looper中的等待线程处理消息。

(2)MessageQueue.next,取出消息,源码:

Message next() {
        //如果Looper已退出并被释放,则return null,
        //如果系统尝试重启一个退出后不支持重启的Looper,就可能发生这样的情况
        final long ptr = mPtr;
        //mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
        //阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
           //如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
           //如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
           //如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                //尝试检索下一条消息,如果找到就返回之。
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
                    //如果msg被一个屏障阻止运行,在消息队列里找到下一个异步类型消息续接上运行
                    //同时所有同步消息都将忽略(平常发送的一般都是同步消息)
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //Next message is not ready.  Set a timeout to wake up when it is ready.
                        //下一条消息未准备好,设置超时时间当它准备就绪时可以被唤醒
                        //当前时间还未到msg的触发时间,则设置阻塞时间nextPollTimeoutMillis
                        //进入下次循环时调用nativePollOnce方法进行阻塞直到触发时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息,mBlocked=false表示不进行阻塞
                        mBlocked = false;
                        //msg被取出等待处理
                        if (prevMsg != null) {
                            //如果有前一个消息,把msg的next链接为prevMsg的next
                            prevMsg.next = msg.next;
                        } else {
                            //如果没有前一个消息,说明这个msg是队列头部消息,它被处理后,它的next就是队列的头部消息
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();//标记msg正被使用中
                        return msg;
                    }
                } else {
                    //没有msg,则一直阻塞线程,直到被主动唤醒,此时只会处理不紧急的任务(IdleHandler)
                    nextPollTimeoutMillis = -1;
                }
                // Process the quit message now that all pending messages have been handled.
                //所有被挂起的message都被处理完后,如果msg已从队列中移除则处理掉
                if (mQuitting) {
                    dispose();
                    return null;
                }
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                //第一次运行非紧急任务,获取当前需要处理的非紧急任务队列的(集合)的长度
                //非紧急任务仅在消息队列为空或或头部消息(可能是屏障)被延后处理时运行
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    //没有Message且没有非紧急任务时,设置阻塞,跳出本次循环
                    mBlocked = true;
                    continue;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            //运行非紧急任务,只在第一次迭代时到达此代码块
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; //释放对handler的引用
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                //遍历判断这些非紧急任务是否需要保存,不需要则释放
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;//重设非紧急任务数量为0
            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            //当调起一个非紧急任务时,一个新的message可能已经被传递进来了,此时返回查找等待被处理的message而不再继续等待
            nextPollTimeoutMillis = 0;
        }
    }

从源码看出next的方法大体的流程如下:

1,首次进入方法或消息队列里的消息都被执行完时,nextPollTimeoutMillis赋值为-1,然后处理非紧急任务IdleHandler,再次进入循环时,线程被阻塞直到被外部主动唤醒(插入消息后根据消息类型决定是否需要唤醒)。

2,如果消息队列里的头部消息是消息屏障,while循环里找出异步消息然后执行,同时放弃后面的同步消息。

3,如果取出的msg消息还未到触发时间,则赋值nextPollTimeoutMillis = 延时的时间,阻塞线程直到msg的触发时间唤醒线程处理msg消息。

4,如果消息需要即时处理或触发时间已到,取出此消息给Looper处理。

*********************************************************************************************************************************

分析enqueueMessage和next方法的源码得出结论:

消息的入列和出列是生产--消费模式,Looper调用loop方法在对应的线程中不断地用next取出消息,而Handler关联的线程通过enqueueMessage插入消息到消息队列里,同时为了防止多个线程对消息队列进行操作,enqueueMessage和next方法都使用了synchronized (this)同步锁机制,this就是MessageQueue对象,确保操作的唯一性。

问题1:Handler.sendMessageDelayed是如何实现延迟的?

从next方法里得出,如果取出的消息还未到触发时间,计算并赋值nextPollTimeoutMillis,阻塞线程直到消息的触发时间,这段时间内调用nativePollOnce(ptr, nextPollTimeoutMillis)这个本地C++方法,调用C++底层并最终会通过Linux的epoll监听文件描述符的写入事件来实现延迟处理消息。

问题2:Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?

此问题解答来自:https://www.jianshu.com/p/8c829dc15950

APP的入口ActivityThread的main方法源码:

public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主线程里Looper通过loop开启死循环不断地接收和处理消息,如果没有这个死循环,主线程处理完手头的事情就退出,app也跟着崩溃。

主线程的Looper负责从消息队列中读取消息,如果队列里没有消息则阻塞进入休眠状态,如果子线程传入消息则唤醒主线程进行读取操作,读取完毕再次进入阻塞休眠状态。主线程不进行耗时操作,耗时操作都交于子线程处理,主线程的Looper循环会尽量减小对cpu性能的消耗。

https://blog.csdn.net/weixin_36289667/article/details/107960145里抄录这么一段话做理解

ActivityThread的 main 方法的主要作用就是做消息循环,一旦退出消息循环,主线程运行完毕,那么应用也就退出了。Android是事件驱动的,在Looper.loop()中循环分发事件,而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度,所以可想而知它的存活周期和 Activity 也是一致的。当没有事件需要处理时,主线程就会阻塞;当子线程往消息队列发送消息,并且往管道文件写数据时,主线程就被唤醒。真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长,导致掉帧,甚至发生ANR,looper.loop() 本身不会导致应用卡死。

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

---------------------------------------------------------------------------------------------------------------------------------------------

4,Looper

参考:https://blog.csdn.net/linghaidong/article/details/54709303
作为安卓线程间消息通信的机制,Looper管理消息队列,实现多线程间消息的互通,用户无需考虑互斥加锁的问题。Looper循环遍历消息队列中的消息,取出消息交于Handler处理。

ActivityThread类
public static void main(String[] args) {
        ...
        //在ActivityThread的main方法里创建主线程的Looper
        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"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //主线程的Looper调用loop方法循环遍历消息队列,当有消息时分发给对应Handler处理,没有消息时阻塞处于休眠状态,等待被唤醒
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
Activity类
public void recreate() {
        if (mParent != null) {
            throw new IllegalStateException("Can only be called on top-level activity");
        }
        //判断Activity的Looper是不是主线程的Looper,不是抛出异常
        if (Looper.myLooper() != mMainThread.getLooper()) {
            throw new IllegalStateException("Must be called from main thread");
        }
        mMainThread.scheduleRelaunchActivity(mToken);
    }

主线程ActivityThread在main方法里调用Looper.prepareMainLooper()初始化一个专供主线程使用的Looper,不停循环遍历消息队列,有消息时交给对应Handler处理,没有消息时阻塞处于休眠,等待被唤醒。查看Activity的源代码recreate方法里会判断当前Activity的looper是不是主线程的looper,如果不是会抛出异常,说明Activity用的是主线程的looper。整个app运行中的组件,Activity,service等等运行时的各种操作都是在主线程中调用looper循环遍历消息队列操作更新的,所以耗时操作不要在主线程里操作就是防止ANR。

Looper的prepare方法源码:

//本地化存储机制,一个线程只能对应一个
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static void prepare() {
        prepare(true);
    }

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));
    }
//Looper绑定当前所在线程,生成属于自己的消息队列
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

参考:https://blog.csdn.net/sun927/article/details/51031268 https://blog.csdn.net/ly502541243/article/details/87475229

为什么一个线程只能有一个Looper?

ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅能访问与其线程相关联的值,一个线程修改ThreadLocal对象对其他线程没有影响。ThreadLocal保证每个线程都有自己的Looper,同时为了确保唯一性,在prepare方法里会先判断ThreadLocal对象是否已创建,如果已创建则会抛出异常提示"一个线程只能生成一个Looper",sThreadLocal.set(new Looper(quitAllowed))绑定当前线程,生成属于自己的消息队列。

所以在子线程中创建Handler时为什么要调用Looper.prepare()?

子线程里没有Looper,子线程中创建Handler不调用Looper.prepare,报错如下:

Uncaught handler: thread Thread-8 exiting due to uncaught exception 

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 

调用Looper.prepare,会创建一个新的消息队列,并绑定这个子线程。调用Looper.loop方法开始循环遍历消息队列里的消息。

public static void loop() {
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
            msg.recycleUnchecked();
        }
    }

查看loop方法的源码,looper开启一个for循环,不停遍历当前的消息队列,只要找到了消息,就调用Handler的dispatchMessage方法处理消息,然后消息msg进行自我回收。

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

这个方法大意如此:1,如果msg是通过post传入的Runnable类型,因为post最终调用的还是send方法,runnable当作Message的Callback使用,所以msg.callback!= null,直接运行runnable的run方法;2,if (mCallback != null)表示如果构造Handler时有传入Callback参数,则调用Handler的callback处理消息;3,如果前面两种情况都不触发,即通过Handler mHandler = new Handler() { public boolean handleMessage(@NonNull Message msg) {} }创建Handler,此时将消息msg传回Handler的handleMessage方法中供开发者自行处理。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值