备战秋招 - 阿里巴巴面试真题:- 给你一个 Demo- 你如何快速定位 ANR?
文末还有大厂面试专题资料包免费分享~接下来是正文:
一、前期基础知识储备
####1.ANR 错误定义
在 Android 上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作“应用程序无响应”(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。因此,在程序里对响应性能的设计很重要,这样,系统不会显示 ANR 给用户。
默认情况下,在 Android 中 Activity 的最长执行时间是 5 秒(主要类型),BroadcastReceiver 的最长执行时间的则是 10 秒,ServiceTimeout 的最长执行时间是 20 秒(少数类型)。超出就会提示应用程序无响应(ANR 错误)。
####2.ANR 错误出现原因
只有当应用程序的 UI 线程响应超时才会引起 ANR 超时产生的原因包括:
①当前事件没有机会处理,例如 UI 线程正在响应另外的事件,当前事件被某个事件给阻塞掉了;②当前事件正在处理 但是由于耗时太长没有能及时的完成。其他原因:③在 BroadcastReceiver 里做耗时的操作或计算;④CPU 使用过高;⑤发生了死锁;⑥耗时操作的动画需要大量的计算工作,可能导致 CPU 负载过重。
二、ANR 定位方式及优化
####1.ANR 错误定位
如果开发机器上出现 ANR 问题时,系统会生成一个 traces.txt 的文件放在/data/anr 下,最新的 ANR 信息在最开始部分。通过 adb 命令将其导出到本地,输入以下字符:
$adb pull data/anr/traces.txt .
####2.供选的优化 ANR 问题的方式:
1)为了执行一个长时间的耗时操作而创建一个工作线程最方便高效的方式是使用 AsyncTask,只需要继承 AsyncTask 并实现 doInBackground()方法来执行任务即可。为了把任务执行的进度呈现给用户,你可以执行 publishProgress()方法,这个方法会触发 onProgressUpdate()的回调方法。在 onProgressUpdate()的回调方法中(它执行在 UI 线程),你可以执行通知用户进度的操作,例如:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {// Do the long-running work in hereprotected Long doInBackground(URL... urls) {int count = urls.length;long totalSize = 0;for (int i = 0; i < count; i++) {totalSize += Downloader.downloadFile(urls[i]);publishProgress((int) ((i / (float) count) * 100));// Escape early if cancel() is calledif (isCancelled()) break;}return totalSize;}
// This is called each time you call publishProgress()protected void onProgressUpdate(Integer... progress) {setProgressPercent(progress[0]);}
// This is called when doInBackground() is finishedprotected void onPostExecute(Long result) {showNotification("
Downloaded " + result + " bytes");}
2)如果你实现了 Thread 或者 HandlerThread,请确保你的 UI 线程不会因为等待工作线程的某个任务而去执行 Thread.wait()或者 Thread.sleep()。UI 线程不应该去等待工作线程完成某个任务,你的 UI 线程应该提供一个 Handler 给其他工作线程,这样工作线程能够通过这个 Handler 在任务结束的时候通知 UI 线程。例如:
继承 Thread 类
new Thread(new Runnable() {@Overridepublic void run() {/**耗时操作/handler.post(new Runnable() {@Overridepublic void run() {/*更新 UI*/}});}}).start();
实现 Runnable 接口
class PrimeRun implements Runnable {long minPrime;PrimeRun(long minPrime) {this.minPrime = minPrime;}
public void run() {// compute primes larger than minPrime. . .}}
PrimeRun p = new PrimeRun(143);new Thread(p).start();
使用 HandlerThread
// 启动一个名为 new_thread 的子线程 HandlerThread thread = new HandlerThread("new_thread");thread.start();
// 取 new_thread 赋值给 ServiceHandlerprivate ServiceHandler mServiceHandler;mServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);
private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}
@Overridepublic void handleMessage(Message msg) {//默认 Handler 的 handleMessage 方法是运行在主线程中的,如果传入一个工作线程的 Looper,则改变 HandleMessage 方法执行的所在线程}}
评论