当Android应用的UI线程被阻塞太久时,就会触发一个”Application Not Responding“(ANR)错误。如果APP运行在前台,系统就会弹出一个提示框,告知用户,用户可以选择继续等待或者强制关掉。
ANR是因为负责更新UI的主线程无法处理用户输入事件或绘制操作,而导致的糟糕体验。
在Android中,程序的响应性是由Activity Manager与Window Manager系统服务来负责监控的,当系统检测到下面的条件之一时会显示ANR的对话框:
synchronized
)而被长时间阻塞。使用
StrictMode
可以帮助你在开发的过程中发现在主线程意外的IO操作。
可以在Application、Activity或者其他应用组件进行配置:
public void onCreate() { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate();}
默认情况下,Android只显示前台ANR弹窗,如果需要允许显示后台ANR弹窗,就要到开发者选项,开启”Show all ANRs“。
使用Traceview去跟踪正在运行的应用,并定位主线程忙碌的位置。
当发生ANR,Android系统会存储日志文件。 日志路径: 旧版系统:
/data/anr/traces.txt
新版系统:/data/anr/anr_*
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_doANR).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }}
上面代码触发了ANR,相关日志:
"main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 flags=1 obj=0x75115ec8 self=0xeb674000 | sysTid=10723 nice=-10 cgrp=default sched=0/0 handle=0xf02fa494 | state=S schedstat=( 384398145 34829357 257 ) utm=26 stm=12 core=2 HZ=100 | stack=0xff10b000-0xff10d000 stackSize=8MB | held mutexes= // Java调用堆栈信息,可以查看调用关系,定位到具体位置 at java.lang.Thread.sleep(Native method) - sleeping on <0x02ed72b7> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:373) - locked <0x02ed72b7> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:314) at com.github.xch168.anrdemo.MainActivity$1.onClick(MainActivity.java:18)// 触发ANR的方法 at android.view.View.performClick(View.java:6597) at android.view.View.performClickInternal(View.java:6574) at android.view.View.access$3100(View.java:778) at android.view.View$PerformClick.run(View.java:25885) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Process.setThreadPriority()
并传递 THREAD_PRIORITY_BACKGROUND
来设置线程的优先级为”background“,不然这个线程仍然会使得你的应用显得卡顿,因为这个线程默认与UI线程有着同样的优先级。IntentService
来响应BroadcastReceiver中的长时间任务。