欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发中避免应用无响应的方法(Application Not Responding、ANR)

程序员文章站 2023-02-02 12:39:26
app里发生的最糟糕的事是弹出应用无响应”application not responding” (anr) 对话框.本课讲的是如何保持应用响应,避免anr。 什么触...

app里发生的最糟糕的事是弹出应用无响应”application not responding” (anr) 对话框.本课讲的是如何保持应用响应,避免anr。

什么触发anr

通常,系统会在应用无法对用户输入响应时显示anr。比如,如果一个应用在i/o操作上阻塞了(频繁请求网络)ui线程,系统无法处理用户输入事件。或者,在ui线程中,app花了大量时间在构建复杂的类,或在游戏中计算下一个动作。保证这些操作高效是很重要的,但最高效的代码也需要花费时间。

在任何情况下,都不要在ui线程执行耗时任务,取而代之的是创建 一个工作线程,在这个线程里操作。这可以保持ui线程运行,阻止系统因为代码卡住而结束应用。
在android里,activity manager和window manager系统服务监控着应用的响应能力。android会在检测到以下情形中之一时,弹出anr对话框:

1.未在5秒内对用户输入事件响应
2.broadcastreceiver未在10秒内执行完

如何避免anr

android应用默认运行在单线程里,叫ui线程或主线程。这意味着,你的应用所有工作都在ui线程里,如果花费很长时间才能完成,会触发anr,因为此时应用无法操控输入事件或广播。

因此,ui 线程里的任何方法都应该尽可能地做轻量的工作,特别是activity在生命周期方法,像oncreate(),onresume().潜在的耗时操作,像网络,数据库,或昂贵的计算(像改变图片大小)应该在工作线程里完成(或者在数据库操作案例里,通过一个异步请求)。

最高效的方法是为耗时操作使用asynctask类创建工作线程。继承asynctask实现doinbackground()方法来执行工作。要发送进度给用户,调用 publishprogress(),会触发onprogressupdate(),例子:

复制代码 代码如下:

private class downloadfilestask extends asynctask<url, integer, long> {
    // do the long-running work in here
    protected 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 called
            if (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 finished
    protected void onpostexecute(long result) {
        shownotification("downloaded " + result + " bytes");
    }
}

执行这个工作线程,只需要创建一个实例,调用 execute():

复制代码 代码如下:
new downloadfilestask().execute(url1, url2, url3);

尽管比asynctask更复杂,你可能还是想创建自己的线程或者handlerthread类,如果这么做,你应该调用process.setthreadpriority(thread_priority_background) 设置线程优先线为”background”.如果没有,线程仍然会拖慢应用,因为它跟ui线程优先级相同。

如果你实现thread或handlerthread,确保ui线程没有因为等待工作线程执行完而阻塞。不要调用thread.wait()或thread.sleep(),而是提供一个handler,供任务执行完后回调。如此设计,ui线程会保持响应,避免出现anr对话框。

特别强调broadcastreceiver的执行时间,意味着你要:分散工作到后台线程里,像保存设置或者注册notification。执行密集任务(intensive tasks),应该用intentservice。

提示:你可以用strictmode帮你找到在ui线程上潜在的耗时操作