Android 面试题之性能优化篇
提示:以下是本篇文章正文内容
===================================================================
不考虑使用其他第三方性能分析工具的话,我们可以直接使用 ddms 中的工具,其实 ddms 工具已经非常的强大了。ddms 中有 traceview、heap、allocation tracker 等工具都可以帮助我们分析应用的方法执行时间效率和内存使用情况。
Traceview 是 Android 平台特有的数据采集和分析工具,它主要用于分析 Android 中应用程序的 hotspot(瓶颈)。
heap 工具可以帮助我们检查代码中是否存在会造成内存泄漏的地方
allocation tracker 是内存分配跟踪工具
Android 的虚拟机是基于寄存器的 Dalvik,它的最大堆大小一般是 16M,有的机器为 24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现 OutOfMemory 的错误。
资源释放问题:
程序代码的问题,长期保持某些资源,如 Context、Cursor、IO 流的引用,资源得不到释放造成内存泄露。
对象内存过大问题
保存了多个耗用内存过大的对象(如 Bitmap、XML 文件),造成内存超出限制。
static 关键字的使用问题
static 是 Java 中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用 static 修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context 的情况最多),这时就要谨慎对待了。
应该尽量避免 static 成员变量引用资源耗费过多的实例,比如 Context。
Context 尽量使用 ApplicationContext,因为 Application 的 Context 的生命周期比较长,引用它不会出现内存泄露的问题。
使用 WeakReference 代替强引用。比如可以使用 WeakReference mContextRef;
线程产生内存泄露的主要原因在于线程生命周期的不可控。
有些人喜欢用 Android 提供的 AsyncTask,但事实上 AsyncTask 的问题更加严重,Thread 只有在 run 函数不结束时才出现这种内存泄露问题,然而 AsyncTask 内部的实现机制是运用了 ThreadPoolExcutor,该类产生的 Thread 对象的生命周期是不确定的,是应用程序无法控制的,因此如果 AsyncTask 作为 Activity 的内部类,就更容易出现内存泄露的问题。
**针对这种线程导致的内存泄露问题的解
决方案:**
将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。
在线程内部采用弱引用保存 Context 引用。
1、图片过大导致 OOM
2、界面切换导致 OOM
3、查询数据库没有关闭游标
4、构造 Adapter 时,没有使用缓存的 convertView
5、Bitmap 对象不再使用时调用 recycle()释放内存
Android 中用 bitmap 时很容易内存溢出
解决方法:
方法 1: 等比例缩小图片
方法 2:对图片采用软引用,及时地进行 recyle()操作
方法 3:使用加载图片框架处理图片,如专业处理加载图片的 ImageLoader 图片加载框架。还有我们学的 XUtils 的 BitMapUtils 来做处理。
1、自 定 义 一 个 Application , 比 如 叫 MyApplication 继 承 Application 实 现 UncaughtExceptionHandler。
2、覆写 UncaughtExceptionHandler 的 onCreate 和 uncaughtException 方法。
3.在 AndroidManifest 中配置该 Application
第二种方式:Crashlytics
在 Android 上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要,这样,系统不会显示 ANR 给用户。
ANR 一般有三种类型:
1:KeyDispatchTimeout(5 seconds) --主要类型:按键或触摸事件在特定时间内无响应
2:BroadcastTimeout(10 seconds):BroadcastReceiver 在特定时间内无法处理完成
3:ServiceTimeout(20 seconds) --小概率类型:Service 在特定的时间内无法处理完成
超时的原因一般有两种:
(1)当前的事件没有机会得到处理(UI 线程正在处理前一个事件没有及时完成或者 looper 被某种原因阻塞住)
(2)当前的事件正在处理,但没有及时完成 UI 线程尽量只做跟 UI 相关的工作,耗时的工作(数据库操作,I/O,连接网络或者其他可能阻碍 UI 线程的操作)放入单独的线程处理,尽量用 Handler 来处理 UI thread 和 thread 之间的交互。
10.多线程间通信和多进程之间通信有什么不同,分别怎么实现?
一、进程间的通信方式
管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
信号量(semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列( messagequeue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号 (sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
共享内存(shared memory ):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
套接字(socket ) :套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
二、线程间的通信方式
锁机制:包括互斥锁、条件变量、读写锁
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
Dalvik 虚拟机运行在 Linux 操作系统之上。Linux 操作系统并没有纯粹的线程概念,只要两个进程共享一个地址空间,那么就可以认为它们是同一个进程的两个线程。Linux 系统提供了两个 fork 和 clone 调用,其中,前者是用来创建进程的,而后者是用来创建线程的。
评论