对于安卓知识点关联汇总 第一篇(ANR)

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队列里的消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值