ANR Application Not Responding
安卓的响应机制,它是针对事件的处理状态来决定的。
出现了ANR
•事件没有机会处理,被阻塞了
•事件正在处理,需要一定时间
如果需要业务需要,等待就得加上Loading,告诉用户当前需要等待。
当然为了很好的体验,等待时间不宜过长,这个时候一些耗时的操作就不能放到主线程(Activity,BroadcastReceiver,Service),需要用到子线程,还有一个就是慎重使用锁
1、那么怎么构建子线程呢?
Thread、Runnable、线程池、Callback
2、现在的问题线程有了,任务完成了,但主线程UI没有相应的更新,那怎样更新呢
Activity.runOnUiThread()、View.post()、Handler.post()、Handler.sendMessage()【系类方法】
3、还有一个线程和UI更新都能完成的,那就是AsyncTask.
4、线程启动了,那怎么去关闭呢,常见的是使用flag标识符。以前可以使用线程的stop()方法,这个已经弃用了,现在可以使用interrupt()方法。
5、Sleep和wait的区别
Sleep CPU让出时间进入休眠,时间到了就会继续向下执行
Wait 配合锁执行任务,收到notify时释放锁向下执行
6、多线程下数据同步(线程安全)
共享变量使用volitale
方法使用锁
7、多线程下的锁
synchronized和Lock【常使用的ReentrantLock】
8、线程池(线程复用、管理可控)
FixedThreadPool(数量可控,长期等待,占资源) 、
SingleThreadExecutor(只有一个,异常后会重新启动一个)、
CachedThreadPool(数量无限,无效后销毁,新任务无闲置时会重新创建)、ScheduledThreadPool(定时任务线程,数量可控,完成任务立即销毁)
9、Activity.runOnUiThread() 核心调用了handler.post()
10、View.post()核心也是handler.post(),但这个更复杂一些,这里涉及到了dispatchAttachedToWindow和mAttachInfo ,有兴趣的可以去深入了解一下;同时这里可以获取view的宽高。
11、接下来是一个重磅的点了------Handler
为什么是重点呢,以上可以看到很多主线程UI更新的方式核心都是handler。那么就得来了解一下handler了。
大家都知道Handler和Message、MessageQueue、Looper、ThreadLock这几个有关系,为了更好的去理解,我们需要一个切入点Message即msg。
为什么要用这个msg呢,在handler的消息接收和处理中msg是个很关键的部件。
其中的target用于指示处理消息的handler,callback用于判断是否Runnable来处理消息,next存取要处理的消息
Ok,好了有了这三个就可以把handler的处理机制串联起来了。
首先需要做一个对应,target对应Looper.loop()和handler.enqueueMessage()、callback对应handler.dispatchMessage()、next对应MessageQueue.enqueueMessage()
我们都知道loop()循环从队列里拿消息,loop是个静态方法,里面有队列的变量,一般情况都会使用静态的,但我们会在很多地方调用这个loop(),那这个时候如果队列变量queue是静态的会出现线程不安全,这也就是当前知识点串联汇总中的线程了。Looper给我们了一个很好解决方案,使用ThreadLock来维护一个Looper,也就是说一个线程只有一个Looper,这样就线程安全了。接下来就是通过ThreadLock来存取Looper以及变量queue了,那么什么时候存储的Looper呢,是Looper的另一个静态方法prepare(),所以一定要记得使用Looper时prepare()和loop()是联合使用的,少一个都不会执行。在loop()里拿到了消息queue.next(),就会交给消息对于的target【Handler】targer.dispatchMessage()来处理了,这个target就是handler.enqueueMessage()在这里进行赋值的,同时把消息压入队列。
好的,我们看了拿消息,那么是怎么把消息压入队列的呢?
接下来就是next,messageQueue 缓存了一个Message对象mMessage,messageQueue.enqueueMessage()会把消息新的消息赋值给mMessage.next,这样就以一个链表的形式把消息存储起来了,上面也说到了一个next()方法,这里循环从缓存mMessage的next获取消息,没有消息时就会阻塞等待。另外Message里也提供了一个静态的方法obtain()方法,在Message里也缓存了一个Message对象 sPool,来维护一个消息。
消息的存取我们了解了,那么接下来我们看看该怎么处理消息了,这个还得看当时我们是如何发送的消息。handler发送消息有两大类sendMessage和post,他们都会调用handler.sendMessageDelayed(),唯一的区别就是post会在调用这个delayed方法之前把Runnable赋值给msg的callback,接下来就是sendMessageAtTime()、enqueueMessage()把消息压入队列了,然后Looper.loop()不断从queue里获取消息,同时调用targer.dispatchMessage(),关键的地方来了,这里根据callback是否为空来判断哪个来执行消息的处理。callback不为空的话由Runnable来处理,当然这里有个handlerCallBack()方法,这里给出了callback.run(),也就是Runnable的run()方法;对应的如果是空的话,那么就会交由handlerMessage()也就是我们熟悉的这个方法来处理。
12、这里的sendMessageDelayed()方法就是消息是否延时的计算,它是把当前时间加上了延时的时间,所有我们看到就是我们设置延时后来执行了
13、MessageQueue一直在阻塞,那为什么没有出现我们今天主要篇章里的ANR呢?
MessageQueue在没有消息时会进入休眠状态,不会消耗CPU的
14、在线程里创建handler并进行UI更新,上面我们提到了Looper.loop()是不断从队列里获取消息的,那么我们就需要这个方法,还有一点我们上面也提到了Looper.prepare()和loop()配合使用
15、handler使用时会造成内存泄露
我们在配合线程或者延时来使用handler时,由于处理消息持有外部类的引用,造成不能及时回收儿引起内存泄露
那么怎么解决这个问题呢,网络上给出了三个步骤
1>、使用静态内部类构建Handler,保证声明周期不和activity一致
2>、使用WeakReference<Activity>来持有外部类的引用
3>、在onDestroy()里清除handler队列里的消息