Android - 《Keeping Your App Responsive》

翻译自Android 官方文档。

保持你的App有响应

在这个世界上,写出能跑赢所有性能测试的代码是可能,但是在重大的周期里,仍然让人感觉缓慢、卡顿或者卡住,或者花太多时间来处理输入。这种能发生在你的App的响应能力的最坏的事情是一个“ANR”Dialog。

在Android系统里,系统守卫依靠在一个周期里确认App没有响应能力,从而显示一个对话框来告诉你,你的App已经停止响应,就像图1显示的那个Dialog那样。

图1:一个显示给用户的ANR窗口


在这时,你的App已经在一个足够的时间周期里不响应,所以系统提供给用户一个退出App的选项。设计响应性好的App,保证系统不现实ANR窗口给用户,这是非常关键的。

 

这篇文档描述Android如果判断一个App是否是无响应,并且提供一个指导,用来帮助你保证你的App一直有响应。

 

什么会触发ANR?

通常,如果一个App不响应用户输入,那么Android系统就会显示一个ANR窗口。例如,一个App的UI 线程阻塞在IO操作上,那么系统就不能处理到来的输入事件。又或许App在UI线程里花费了太多时间来构建一个精细的内存结构,或者在游戏中计算下一步的移动。确认这些计算是有效率的总是很重要的,但即使是最有效率的代码,仍然要花时间来运行。

在一些情景下,在你执行一个尽可能长时间的操作时,你不应该在UI线程做这个工作,而是用创建一个工作线程来代替,这个工作线程能做更多的事情。这能保证UI线程(驱动UI时间循环)能一直运行,并且能阻止系统认定你的代码已经卡死。因为这些线程通常是一个完整的类,所以你能将响应性想成一个类问题(比较这个代码和基础代码的效率,是方法层级关心的事情)。

 

在Android系统里,app的响应性是由Activity Manager和 Window Manager 来监测的。Android将会为检测到以下条件中的一个的特定App显示一个ANR窗口:

·不响应输入事件5秒以内。

·一个广播接收器不能完成处理10秒以内。

 

怎样避免ANR?

Android App通常完全运行在单个线程上,默认是UI线程或者主线程。这意味着你的App在UI线程内做的需要花很长时间完成的任何事情都会触发ANR窗口,因为你的App不会给自己一个机会来处理输入事件或广播Intent。

因此,任何运行在UI线程上的方法应该尽量的在线程上做一些微小的工作。特别是,Activity应该尽可能小的在关键函数:OnCreate、OnResume里做设置。可能长时间的操作,比如网络连接,数据库操作,或者计算成本昂贵的计算,比如改变位图大小应该放在工作者线程内(或者在操作数据库的情况下,通过一个异步请求来操作)。

 

为长时间操作创建工作者线程的最有效的方法是使用AsyncTask 类。简单的扩展自AsyncTask类,并且实现doInBackground() 方法来执行工作。为了发送进度变化给用户,你能调用publishProgress()方法,系统调用onProgressUpdate()回调函数。在你的onProgressUpdate()方法(这个方法运行在UI线程中)实现,你能通知用户。例如:

private classDownloadFilesTask 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);
 
 
你应该会想要创建自己的Thread或者HandlerThread类,虽然它们比AsyncTask更复杂。如果你那么做了,你应该设置那个线程的优先级为“background”,通过调用Process.setThreadPriority()方法和传递THREAD_PRIORITY_BACKGROUND标识。如果你不想要走设置低线程优先级这条路,那么你的线程就会一直减缓你的App的运行速度,因为默认你的线程和UI线程在同一优先级。
 
如果你实现了Thread和HandlerThread,请确认,当UI线程等待工作线程完成时,不会被阻塞,不要在UI线程里调用Thread.wait() , Thread.sleep()。替代UI线程阻塞等待工作者线程完成的方法是,你的主线程应该提供一个Handler给其他线程,其他线程可以用Handler来回传工作者线程完成消息。用这种方法来设计你的App,将会允许你的UI线程保留对输入事件的响应性,并且因此避免因输入事件5秒超时引起的ANR对话框。
 
广播接收器的特殊规则着重于广播接收器会这么做:小,分散大量的后台工作,比如保存一个设置,或者注册一个Notification。所以,就像调用UI线程的其他方法那样, App应该避免在一个广播接收器进行潜在的长时间操作或计算。而是通过工作线程来替代这样的任务。如果一个潜在的长时间的Action需要被用来响应一个Intent 广播,你的应用应该开启一个IntentService 。
 

小技巧:你能通过StrictMode模式来帮助你找到潜在的长时间操作,比如网络连接、数据库操作这种你可能很意外的在你的主线程里做的动作。

 

强化响应能力

通常,在一个App上,100到200毫秒是一个极限,超过这个极限用户会感受到缓慢。同样地,这里有一些额外的小技巧,让你应该做什么来避免ANR,并且让你的App看起来像是响应用户了。

·如果你的App正在后台做一些工作,并且还响应用户输入,显示一个进度条是有用的(比如在你的UI里添加一个进度条控件)。

·特别对于游戏,将计算移动放到工作线程里。

·如果你的App有一个很耗时的初始化设置阶段,考虑显示一个启动画页,或者尽可能快的渲染一个主View,表明加载正在进行并且正在异步填充信息。在其他的Case下,你也总得指示出正在进行中的样子,以免让用户感觉App已经卡死。

 

StrictMode说明

  StrictMode是一个开发工具,用来检测一些你可能意外做的事情,并且将你的注意力带到它们身上,那样你就可以修理它们了。

StrictMode最常用来在你的App的主线程里捕获意外的磁盘或网络访问动作,这个主线程就是UI动作被接收的地方,动画播放的地方。保持磁盘和网络操作远离主线程,将使App更顺畅,更有响应性。通过保持你的App的主线程的响应,你就能阻止ANR窗口显示给用户。、

注意:虽然Android设备的磁盘经常在闪存上,很多设备运行一个并发性有限的文件系统在内存上。下面这个是个经常的情况,几乎所有磁盘访问都很快,但当某一个IO在后台的其他进程里发生意外,就会出奇的慢。如果可能,最好的假设就是这样的事情并不快。

举例代码,从早期启用这样的功能,在你的Application、Activity,或其他App的组件的OnCreate函数:

 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();
 }

你能决定,当一个违规被检测到能发生什么。例如,使用penaltyLog()函数,你能观察到 adb logcat输出,在你用你的App来看发生的违规时。

如果你发现你感觉有问题的违规,这里有各种各样的工具来帮你解析它们:threads, Handler, AsyncTask, IntentService,等等。但是不要感觉强迫要修改StrictMode找到的每个事物。尤其是,很多磁盘访问的Case经常是必要的,在正常的Activity生命周期里。用StrictMode来找你意外做的事情。把网络请求放在UI线程几乎总是有问题的。

注意:StrictMode不是一个安全的机制,并且不保证找到所有磁盘和网络访问。在做binder调用时,在它传递它的状态通过进程边界时,基本上它一直就是最好的机制。尤其是,从JNI开始的磁盘和网络调用不是必定会触发StrictMode模式。未来的Android版本有可能会捕获更多的操作,所以你就不再离开StrictMode模式,它会被启动在分布在Google play的App里。


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值