安卓知识

主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

https://www.jianshu.com/p/cfe50b8b0a41

https://www.zhihu.com/question/34652589 *

主要有3个疑惑:
1.Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
3.Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?

自己总结一下:
(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

首先线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出。
但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。
真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:

public static void main(String[] args) {
        ....

        //创建Looper和MessageQueue对象,用于处理主线程的消息
        Looper.prepareMainLooper();

        //创建ActivityThread对象
        ActivityThread thread = new ActivityThread(); 

        //建立Binder通道 (创建新线程)
        thread.attach(false);

        Looper.loop(); //消息循环运行
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

(3) Activity的生命周期是怎么实现在死循环体外能够执行起来的?
ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:
在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。
比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;
再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。



 Handler机制:

ANR:

通常在如下两种情况下会弹出ANR对话框:

  1. 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
  2. BroadcastReceiver在10s内无法结束.

由于Android主线程做耗时操作超过5秒没有响应会产生ANR,所以 Android发布了一套更新UI的机制,我们通常称之为Handler机制。

回答的最主要的点就是下面的这一个环形的框图,要能够清晰的表达出来,后面具体的源码细节才讲得有意义,不然前面一个整体框架都错了,后面怎么说都没用。


Handler机制中,Thread、Handler、Message、Message Queue和Looper。

  • Handler:负责发送和处理消息。
  • Message:用来携带需要的数据。
  • MessageQueue:消息队列,队列里面的内容就是Message。
  • Looper:消息轮巡器,负责不停的从MessageQueue中取Message。

 

Handler的工作原理

  在使用Handler之前必须要调用Looper.prepare()这句代码,这句代码的作用是将Looper与当前的线程进行绑定,在实例化Handler的时候,通过Looper.myLooper()获取Looper,然后再获得Looper中的MessageQueue。在子线程中调用Handler的sendMessage方法就是将Message放入MessageQueue中,然后调用Looper.loop()方法来从MessageQueue中取出Message,在取到Message的时候,执行 msg.target.dispatchMessage(msg);这句代码,这句代码就是从当前的Message中取出Handler然后执行Handler的handleMessage方法。

Handler机制的流程为Thread处理耗时逻辑,把处理结果通过Message传递给Handler,Handler在构造方法中通过Looper.myLooper()获取到对应的Looper,而Looper的构造函数中又创建了MessageQueue,因此一个Looper也就对应了一个MessageQueue。
然后通过Looper来确定把Message放置在哪个Message Queue中,然后通过Looper循环把Message从Message Queue里取出更新到对应的线程中(Message Queue遵循先进先出原则)。
我们自己创建的线程,默认是没有消息队列(Message Queue)和消息循环(Looper)的,想要让一个线程具有消息处理机制,我们需要在线程中先调用Looper.perapre()来创建一个Looper对象,然后调用Looper.loop()进入消息循环。

当我们创建了Handler对象时,指定Handler与哪个具有Looper的线程关联,这个线程就成了目标线程。
每个Handler关联着一个Looper对象,Looper对象在哪个线程中创建就属于哪个线程,调用Message的sendToTarget方法的时候系统会将该message发送到Looper所关联消息队列中,当Looper轮询到该message的时候会调用相应的message的处理方法,包括callback,handleMessage等等。

问题:

1.子线程中创建Handler要先调用一下Looper.prepare(),为什么在主线程中创建Handler之前就不用调用Looper.prepare() 呢? 

其实主线程中创建Handler同样需要调用Looper.prepare()方法,只是这个方法系统已经帮我们调用了。
查找资料发现,Android程序的入口中,系统就默认帮我们调用了Looper.prepare()方法。 
Android程序的入口在ActivityThread中的main()方法

2.Handler明明是在子线程中发的消息怎么会跑到主线程中了呢?

  1. Handler在sendMessage时会将自己设置给Message的target变量即将自己与发送的消息绑定。
  2. Handler的sendMessage是将Message放入MessageQueue中。

3.Handler的发送消息handleMessage又是怎么接收到的呢?

      Looper.loop();这句代码的本质就是调用了Handler中的dispatchMessage(msg)方法,在这个方法里:

  1. 使用Handler的sendMessage方法,最后在handleMessage(Message msg)方法中来处理消息。
  2. 使用Handler的post方法,最后在Runnable的run方法中来处理,代码如下

https://blog.csdn.net/fnhfire_7030/article/details/79518819 *

在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。



什么是ThreadLocal

ThreadLocal 为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。当使用ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

虽然在不同的线程中对同一个mThreadLocal中的值进行了更改,但最后仍可以正确拿到当前线程中mThreadLocal中的值。由此我们可以得出结论ThreadLocal.set方法设置的值是与当前线程进行绑定了的。

https://blog.csdn.net/fnhfire_7030/article/details/79518819



Android中子线程真的不能更新UI吗?

极端情况下使可以的,例如在onCreate方法中创建的子线程访问UI是一种极端的情况.

ViewRootImpl的创建在onResume方法回调之后,而我们一开篇是在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃一样能跑起来,而之后修改了程序,让线程休眠了200毫秒后,程序就崩了。很明显200毫秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程。

https://blog.csdn.net/xyh269/article/details/52728861



二、Android 性能优化:

Android性能优化分为:App启动优化、响应优化、布局优化、内存优化、省电优化、网络优化
App启动优化:Android程序启动分为:热启动、温启动和冷启动。启动优化针对的就是冷启动,其中冷启动涉及的一个是Application的OnCreate()方法,
另外一个就是首屏Activity的渲染
响应优化:Android 产生ANR的情况为:Activity超过5秒无响应、BroadcastReceiver超过10秒无响应、Service超过20秒无响应
布局优化:布局中尽量减少嵌套关系,这里有一个工具H-Viewer可以看到布局的嵌套关系
内存优化:内存优化有分为三个问题: 
问题一:GC优化,这里要注意的就是使用过的资源要及时进行回收,这里就要灵活运用强引用、软引用和弱引用 
问题二:图片优化,由于Android虚拟机一般内存只有16-32M的内存,而高清图片一张就要占4-5M,如果不对图片进行处理,那么很容易就会产生OOM,在
使用图片的时候要对图片进行必要的压缩 
问题三:在自定义控件的时候,要尽量避免重复绘制,在onDraw()方法中尽量避免创建变量(推荐测试工具为手机开发者选项中的 调试GPU过度绘制)
省电优化:Android中耗电最多的三个功能为:手机屏幕、手机网络、GPS。这里我们要考虑的主要是手机网络和GPS。在写项目的时候,要尽量少的进行网
络请求,在GPS功能使用结束后,最好把GPS功能关闭。(推荐工具:wakelock)
网络优化: Android网络优化推荐工具为Android Studio内置的Monitor,其中主要包括两个优化方向: 
问题一:当处理图片的时候,最好和后台协商好几种尺寸的图片,在进行网络请求的时候,把对应的尺寸当作参数传递 
问题二:在Wifi网络下,要进行一定的缓存策略,这样在4G网络和无网络的时候客户体验会更好些。



https://blog.csdn.net/qq_27053103/article/details/79564062

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

https://blog.csdn.net/CHITTY1993/article/details/79889432

 

https://blog.csdn.net/huangqili1314/article/details/79824830

https://blog.csdn.net/leepenghom2010/article/details/80204710

https://blog.csdn.net/ClAndEllen/article/details/79257663

http://blog.jobbole.com/84618/

https://blog.csdn.net/woyaowenzi/article/details/9273839


引用application context并不会导致内存泄漏。引用activity的context才会导致内存泄漏,当你的引用对象的生命周期超过了当前activity的生命周期时,系统回收你的activity时发现还有一个context的强引用,而不去销毁这个activity,导致内存泄漏,在开发过程中,需要非常重视这点。













































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值