[译]不要在UI主线程中进行耗时的操做

原文: Why Ice Cream Sandwich Crashes your Apphtml

问题

自Android Ice Cream Sandwich发布后, 这个问题就开始在StackOverflow弥散开来:java

个人应用在Android2.x上运行良好,可是在3.x 和4.x系统上老是强退,是什么致使的?android

这是一个很棒的问题,毕竟开发者老是但愿基于旧版本系统开发的应用在新版本的Android系统仍能兼容。在我看来,问题的缘由可能多种多样。 但大多数时候,缘由很是简单:你把一个可能很是耗时的操做放进了UI线程。数据库

什么是UI线程?

应用的主UI线程的概念及其重要性是每一个Android开发者都应理解。当一个应用启动,系统会为应用建立一个名为“main”的主线程。这个主线程(也就是UI主线程)主要负责把事件分发给合适的view或者widget, 所以它很是重要。它也是你的应用和应用的UI交互的线程。例如,若是你点击了屏幕上的一个按钮,UI线程会把点击时间交给view处理,view接到事件后会设置它的pressed状态,而后向事件队列中发送一个invalidate请求。 UI线程会依次读取队列而且告诉view去重绘本身。网络

除非你的Android应用实现的很是合理,不然这个单线程模型会使性能变得极低。在极端状况下,若是UI线程负责整个应用中的全部操做,进行耗时的操做好比发送网络请求,或者数据库查询等都会致使用户界面的阻塞。这些操做在未完成以前,全部的时间包括绘制和触屏事件都不会被派发。从用户的角度来看,程序彷佛是卡死了。app

在这些状况下,即时的反馈至关重要。研究代表0.1s是用户感受系统是否流畅的临界值。任何比临界值更慢的都被认为延迟(Miller 1968; Card et al. 1991)。虽然1秒看起来没什么影响,但在GooglePlay中,即使是十分之一秒也多是好评和差评的区别。更糟糕的是,若是UI线程被阻塞5秒以上,用户会收到“程序未响应”(ANR)的提示对话框,而且会强制退出。异步

为何Android会使应用崩溃

应用在2.x系统运行良好,在3.0及以上平台上崩溃的主要缘由在于,3.0以上平台在处理UI线程资源滥用上更加严格。好比说,3.0平台检测到UI线程中有网络请求时,会抛出NetworkOnMainThreadExceptionwill的异常:性能

E/AndroidRuntime(673): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.ExampleActivity}: android.os.NetworkOnMainThreadException网站

Android developer网站文档中也对此进行了很好的解释:线程

当应用试图在主线程中进行网络操做,NetworkOnMainThreadException会被抛出。只有在运行Honeycomb SDK及更高的版本中会被抛出。更早版本的SDK容许在主事件循环线程中进行网络操做,可是很是很是不鼓励这么作。

列出一些ICS和Honeycomb不容许在UI线程中进行的操做:

  • 打开套接字链接 (i.e. new Socket()).
  • HTTP 请求 (i.e. HTTPClient and HTTPUrlConnection).
  • 试图链接远程的 MYSQL 数据库.
  • 下载文件 (i.e.Downloader.downloadFile()).

若是你要在UI线程中进行某些操做,必定要把它们打包到一个工做线程中。其中最简单的方式是使用AsyncTask, 它容许你在你的用户界面中进行一些异步的操做。AsyncTask会把阻塞操做放到工做线程中,并把结果返回到UI线程,而你不须要处理任何与线程相关的工做。

结论

我决定写这篇主题的念头来源于我在StackOerflow和其它论坛上无数次看到了这个问题。问题的主要来源是在UI线程进行了耗时的操做。为了确保用户界面保持流畅,有必要把执行套接字链接、HTTP请求、文件下载和其余的耗时操做放到一个单独的线程中。最简单的方法就是把操做打包到AsyncTask中,它会帮助你启动新的线程并让他们与你的用户界面异步交互。

有帮助的连接

这些资料可能会帮助你熟悉AsyncTask

AsyncTask documentation
Multithreading For Performance

相关文章
相关标签/搜索