编者按:本文内容源自CSDN知识库专家,中科院NIPC国家重点实验室Android工程师,微信公众号“IT面试题汇”作者,刘朋,4月14日在CSDN Android学习群中的分享。以下为内容摘录。
这是我在长期的面试活动中和日常开发中总结的,大家也正好检验一下自己,如果面试遇到会怎么回答。
首先,会分享Android的进阶知识点,包括Activity的四种LaunchMode模式、Android的ANR详解、Android如何避免oom。第二部分会分享Android的高级知识点,比如Android的context。
Activity有四种模式:
1.standard 模式:这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。
2.singleTop 模式:如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。
3.singleTask 模式:如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
4.ANR:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。所以一个流畅的合理的应用程序中不能出现anr,而让用户每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要,这样系统不会显示ANR给用户。
引发ANR的原因有很多,在Android里,应用程序的响应性是由Activity Manager和WindowManager系统服务监视的 。当它监测到以下情况中的一个时,Android就会针对特定的应用程序显示ANR:
1.在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)
2.BroadcastReceiver在10秒内没有执行完毕
如何避免ANR?分享四点经验:
1.运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等。
2.应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。
3.避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。
4.通常100到200毫秒就会让人察觉程序反应慢,为了更加提升响应,如果程序正在后台处理用户的输入,建议使用让用户得知进度,比如使用ProgressBar控件,程序启动时可以选择加上欢迎界面,避免让用户察觉卡顿,使用Systrace和TraceView找出影响响应的问题。
有时候,我们还会遇到oom问题。避免oom,首先要尽量减少新分配出来的对象占用内存的大小,尽量使用更加轻量的对象。
1.使用更加轻量的数据结构,比如使用ArrayMap和sparseMap代替hashmap。
2.避免在Android里面使用Enum,因为Android官方培训课程提到过“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”
3.减小Bitmap对象的内存占用。Bitmap是一个极容易消耗内存的大胖子,减小创建出来的Bitmap的内存占用是很重要的,通常来说有下面2个措施:第一种是inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。 第二种是decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
第二,大多数对象的复用,最终实施的方案都是利用对象池技术,要么是在编写代码的时候显式的在程序里面去创建对象池,然后处理好复用的实现逻辑,要么就是利用系统框架既有的某些复用特性达到减少对象的重复创建,从而减少内存的分配与回收。
在Android上面最常用的一个缓存算法是LRU(Least Recently Use),可以基于它来实现。在ListView与GridView等显示大量图片的控件里面需要使用LRU的机制来缓存处理好的Bitmap。当加载一张图片是一件相对容易的事情,但是大家也遇到过加载一大堆图片的需求,而且还要求这些图片实现复用,比如滑动,来回滑动的时候如果销毁了是不合理的,需要做缓存。这种情况处理不好经常造成oom。
使用内存缓存技术可以很好的解决这个问题,它可以让组件快速地重新加载和处理图片。下面我们就来看一看如何使用内存缓存技术来对图片进行缓存,从而让你的应用程序在加载很多图片的时候可以提高响应速度和流畅性。内存缓存对占用内存大的图片,提供了快速访问的通道。LruCache就是核心类。
LruCache的主要原理就是,把最近使用图片对象的强引用存储在LinkedHashMap这个数据结构中。前三个字母Lru代表的是最近最少使用算法,不同地方名字不一样,在操作系统这门课经常出现。LruCache这个类会把把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
关于避免oom,另外一个角度是避免内存泄漏。内存对象的泄漏,会导致一些不再使用的对象无法及时释放,这样一方面占用了宝贵的内存空间,很容易导致后续需要分配内存的时候,空闲空间不足而出现OOM。在这里我们需要留意三点:
1.注意Activity的泄漏
2.考虑使用Application Context而不是Activity Context
3.注意临时Bitmap对象的及时回收
Android的context
Android程序不像Java程序一样,随便创建一个类,写个main()方法就能跑了,而是要有一个完整的Android工程环境。这个环境不少书里面也叫上下文。在Android这个环境里面,有四大组件,这些组件不是new出来的,而是要有它们各自的上下文环境,也就是我们这里讨论的Context。很多情况,只有拿到了context的引用,也就是Android的环境,资源入口,才可以做很多事情,比如最简单的弹出Toast。
Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”,那么这个“上下文”到底是指什么意思呢?
我们可以理解为语境,在程序中,我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。微信聊天中,此时的“环境”是指聊天的界面以及相关的数据请求与传输,Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。
一个Activity就是一个Context,一个Service也是一个Context。Android程序员把“场景”抽象为Context类,他们认为用户和操作系统的每一次交互都是一个场景,比如打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,比如后台运行的服务(Service)。一个应用程序可以认为是一个工作环境,用户在这个环境中会切换到不同的场景,这就像一个前台秘书,她可能需要接待客人,可能要打印文件,还可能要接听客户电话,而这些就称之为不同的场景,前台秘书可以称之为一个应用程序。
最后,我们在上文提到,context的使用经常会引发内存泄漏,这主要是因为静态Ui控件引起的,或单例引起的内存泄露。我们使用饿汉式初始化单例,AppSettings我们需要持有一个Context作为成员变量,sInstance作为静态对象,其生命周期要长于普通的对象,其中也包含Activity,当我们进行屏幕旋转,默认情况下,系统会销毁当前Activity,然后当前的Activity被一个单例持有,导致垃圾回收器无法进行回收,进而产生了内存泄露。
点击下载讲义
申请加入Android学习群,请加群主。申请好友,请一定注明“Android”。