为什么Android程序中的Looper.loop()不会造成ANR异常
2016-08-19 17:49
651 查看
为什么Android程序中的Looper.loop()不会造成ANR异常
标签(空格分隔): 菜鸟 android作者:陈小默
我们在学习Handler的时候一定都接触过Looper这个东西,也知道其中的loop方法会有阻塞等待的过程。
那么问题来了:既然主线程被阻塞了,为什么不会造成ANR异常呢?
首先这个问题就是错误的,至少有两个概念没有认清:第一,什么是ANR异常?;第二,Android程序阻塞的作用是什么?
这里先回答第一个问题:什么是ANR异常。
最简单的话说就是:当前的事件没有机会得到处理
当我们每次点击屏幕就会产生一个事件,这个事件由Android操作系统接收,之后再传递给我们的应用程序。但是我们的Android应用要求我们只能有一个线程去处理事件,这个线程就是我们的主线程。
耗时操作是引起ANR的原因吗?
大部分Android新人都有一个误区,认为只要运行时间长的操作就是耗时操作!是这样吗?我们做一个实验
class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById(R.id.sleep)!!.setOnClickListener { view -> Thread.sleep(10000) } } }
当我们点击按钮的时候,主线程休眠10秒,那么这里会抛出异常吗?答案是
有可能
围观群众:卧槽,啥叫有可能?
当你运行这个程序并点击休眠按钮的时候,如果你不点击屏幕就是保证再没有新的事件输入的时候,这时候是不会抛出异常,并且主线程会安安静静的休眠10秒,但是如果你在点击休眠之后又点击应用界面的任意位置,此时新的事件就会产生并且被输入到你的应用,但是你的主线程正在休眠,而Android又不允许在其他线程中处理UI事件,于是新的事件会被阻塞。当事件超过某一个时间限制(一般Activity是5s)仍未被执行的时候,就会抛出ANR异常。
通过这个例子我们可以明白ANR异常只是单纯指事件长时间未响应。
现在我们回答了第一个问题。接下来看第二个问题:Android程序阻塞的作用是什么?
现在打开你们的记事本,在上面输入一段java或者kotlin代码
fun main(args: Array<String>) { println("hello world") }
然后我们去运行这段代码,是不是有了特别了不起的发现,那就是程序运行完之后居然自动退出了。
围观群众:卧槽,程序运行完不退出干啥呀,等过年呀!!!
咳咳,程序运行完成之后退出是程序员的常识问题,可是越是简单的细节就越是容易被忽略。Android程序也是JVM进程呀?它是怎么怎么保证程序不会退出的呢?
围观群众:煞笔呀,只要让程序不结束一直运行就行了呀!!!
对呀,又是常识性问题,想让程序永远不会退出的最好方法就是—循环—还要必须是—死循环—
现在我们看一下一个Android程序是如何被启动的
上源码
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
这是ActivityThread中整个main方法的代码,就这么多
看这个main方法的第39行代码,此时启动了一个死循环用来保证应用程序不会退出。
围观群众:罗里吧嗦一大堆,我还是没听懂!!!
这么跟你说吧,主线程在没有事件需要处理的时候就是处于阻塞的状态。想让主线程活动起来一般有两种方式:一种是系统唤醒主线程,并且将点击事件传递给主线程;第二种是其他线程使用Handler向MessageQueue中存放了一条消息,导致loop被唤醒继续执行。
围观群众:那也就是说应用的UI线程大部分情况下都是“死的”喽?
嗯,就是这样,我们可以看到的界面炫酷的效果都是子线程与Handler的执行结果,比如播放视频,或者View的动画,里面都用到了Handler。
围观群众:哦,那面试的时候被问道这个问题我应该怎么跟面试官说呢?
直接回答:煞笔,你问的问题有问题知道不
围观群众:滚!!!
相关文章推荐
- Android面试:主线程中的Looper.loop()一直无限循环为什么不会造成ANR?(转)
- Android面试:主线程中的Looper.loop()一直无限循环为什么不会造成ANR?(转)
- 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
- Android中为什么主线程不会因为Looper.loop()方法造成阻塞
- Android中为什么主线程不会因为Looper.loop()方法造成阻塞
- 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
- Android中为什么主线程不会因为Looper.loop()方法造成阻塞
- 关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- Android为什么主线程不会因为Looper.loop()里的死循环卡死
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- 关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- 为什么Looper.loop()死循环不会导致ANR
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- android主线成有looper.loop()为什么不被卡死
- Looper.loop死循环为什么不会卡死
- Android的Looper的无限循环为啥不会ANR?