Android 异常与性能优化

ANR(Application NotResponding)

如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发 “应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框,为用户提供强制退出应用的选项。

出现以下任何情况时,系统都会针对应用触发 ANR:

  • 当 activity 位于前台时,应用在 5 秒钟内未响应输入事件或 BroadcastReceiver(如按键或屏幕轻触事件)。

  • 虽然前台没有 activity,BroadcastReceiver 用了相当长的时间仍未执行完毕。

不同的组件发生 ANR 的时间不一样,Activity 是 5 秒,BroadCastReceiver 是 10 秒,Service 是 20 秒(均为前台)。

造成 ANR 的主要原因

  • 主线程被 IO 操作(从 4.0 之后网络 IO 不允许在主线程中)阻塞。

  • 主线程中存在耗时的计算

  • 主线程中错误的操作,比如 Thread.wait 或者 Thread.sleep 等

Android 在主线程中的操作

  • Activity 的所有生命周期回调都是执行在主线程的

  • Service 默认是执行在主线程的

  • BroadcastReceiver 的 onReceive 回调是执行在主线程的

  • 没有使用子线程的 looper 的 Handler 的 handleMessage,post (Runnable) 是执行在主线程的

  • AsyncTask 的回调中除了 doInBackground,其他都是执行在主线程

如何解决 ANR

  • 使用 AsyncTask 处理耗时 IO 操作

  • 使用 Thread 或者 HandlerThread 时,调用 Process.setThreadPriority (Process.THREAD_PRIORITY_BACKGROUND) 设置优先级,否则仍然会降低程序响应,因为默认 Thread 的优先级和主线程相同

  • 使用 Handler 处理工作线程的耗时任务,而不是使用 Thread.wait () 或者 Thread.sleep () 来阻塞主线程。

  • Activity 的 onCreate 和 onResume 回调中尽量避免耗时的代码。 BroadcastReceiver 中 onReceive 代码也要尽量减少耗时,建议使用 IntentService 处理。

OOM

当前占用的内存加上我们申请的内存资源超过了 Dalvik 虚拟机的最大内存限制就会抛出的 Out of memory 异常。

OOM 的产生

  • 已使用内存 + 新申请内存 > 可分配内存

  • OOM 几乎覆盖所有的内存区域,通常指堆内存

  • Native Heap 在物理内存不够时也会抛 OOM

解决 OOM

有关 bitmap

  • 加载合适尺寸的图片

  • 及时释放内存

  • 图片压缩

  • inBitmap 属性

  • 捕获异常

其他方法

  • listview:convertview/lru

  • 避免在 onDraw 方法里面执行对象的创建

  • 谨慎使用多进程

内存泄漏

检测工具:

  • Memory Profile

  • LeakCanary

  • MAT

内存泄漏 VS 内存溢出

  • 内存溢出:超出了虚拟机分配的内存

  • 内存泄漏:某个不再使用对象由于被其他实例引用

为什么会内存泄漏

该被回收的对象不能被回收

常见内存泄漏

  • 单例

    长生命周期对象持有了短生命周期的引用导致短生命周期对象需要被回收的时候无法回收

    解决:需要使用 context 时使用 getApplicationContext()

  • Handler

    非静态内部类,持有外部类 XXXActivity 的引用,延时发送消息导致

    解决:

    1. Activity 中避免使用 Handle 非静态内部类,声明为静态的

    2. Handler 中通过 WeakReference 弱引用方式来引用 Activity

    3. 在 Activity 销毁时移除 message:removeCallbacksAndMessages

  • 线程引起

    匿名内部类创建的 AsycTask、Runnable 会持有当前外部类 Activity 隐式的引用,Activity 销毁前 AsycTask、Runnable 内部耗时任务未完成导致 Activity 无法回收

    解决:和 Handler 类似,使用静态内部类、销毁时取消任务

  • Webview

  • 其他

    • 非静态内部类创建静态实例引起

      解决:把内部类修改为静态的

    • 非静态匿名内部类引起

      解决:将匿名内部类设置为静态的

    • 注册 / 反注册未成对使用引起

      注册广播接受器、EventBus 等,记得解绑

    • 资源对象没有关闭引起

      在这些资源不使用的时候,记得调用相应的类似 close()、destroy()、recycler()、release()等方法释放。

    • 集合对象没有及时清理引起

      通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用

如何计算图片占用内存的大小

运行时获取 Bitmap 大小的方法

Bitmap

// 图片占用内存大小的理论需求值
public final int getByteCount() {
if (mRecycled) {
return 0;
}
// int result permits bitmaps up to 46,340 x 46,340
return getRowBytes() * getHeight();
}

// 图片实际占用的内存大小
public final int getAllocationByteCount() {
if (mRecycled) {
return 0;
}
return nativeGetAllocationByteCount(mNativePtr);
}

图片内存体积优化

  • 跟文件存储格式无关

  • 使用 inSampleSize 采样:大图 -> 小图

  • 使用矩阵变换来放大图片:小图 -> 大图

  • 使用 RGB_565 来加载不透明图片

  • 使用 9-patch 图片做背景

  • 不使用图片

    • 优先使用 VectorDrawable

    • 时间和技术允许的前提下使用代码编写动画

可靠便捷的VPN服务 100 G 月流量,5台设备可用,一年仅需99元!
请我一杯咖啡吧!
八亩山啊 微信 微信
八亩山啊 支付宝 支付宝
八亩山啊 比特币 比特币
  • 本文作者: 八亩山啊
  • 本文链接: https://mumo.fun/posts/23/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!