Android面试__Android异常与性能优化

本文深入探讨了Android应用中常见的性能问题,包括ANR、OOM现象的产生原因及其解决方案,并详细介绍了内存管理、冷启动优化等方面的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android异常与性能优化

ANR

什么是ANR

Application Not Responding
Activity:5s
BroadcastReceiver:10s
Service:20S

造成ANR的主要原因

应用程序的响应性是由Activity Manager和WindowManager系统服务监视的

  1. 主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞
  2. 主线程中存在耗时的计算

造成anr的主要原因-Android中哪些操作是在主线程呢?
1. Activity的所有生命周期回调都是执行在主线程的
2. Service默认是执行在主线程的
3. BroadcastReceiver的onReceive()回调是执行在主线程的
4. 没有使用子线程的looper的Handler的handleMessage,post(Runnable)是执行在主线程的。
5. AsyncTask的回调中除了doInBackground,其他都是执行在主线程的

如何解决ANR

  • 使用AsyncTask处理耗时IO操作
  • 使用Thread或者HandlerThread提高优先级
  • 使用handler来处理工作线程的耗时任务
  • Activity的onCreate()和onResume()回调中尽量避免耗时的代码

OOM

什么是OOM

当前占用的内存加上我们申请的内存资源超过了DVM的最大内存限制就会抛出Out Of Memory异常。

一些容易混淆的概念

内存溢出(OOM)/内存抖动(频繁的gc,积累到一定程度,也会造成OOM)/内存泄漏(垃圾对象没有被引用,却还持有未被回收对象的引用,严重到一定程度,会造成OOM)

如何解决OOM

  • 有关bitmap

    • 图片显示(例如ListView滑动停止时才去加载)
    • 及时释放内存(bitmap产生两块内存,一块是Java内存区域,一块是C内存区域,前者通过GC回收,C的应由调用recycle()回收)
    • 图片压缩
    • inBitmap属性(提高分配与释放效率,复用之前bitmap占用的堆内存,即有成百上千的图片,也只会占用屏幕能放下图片的内存)
    • 捕获异常(是Error,不是Exception)
  • 其他方法

    • listview:convertview/lru
    • 避免在onDraw方法里面执行对象的创建,否则容易造成内存抖动
    • 谨慎使用多进程,业务多的时候才使用,否则容易造成开发难度

Bitmap

recycle

bitmap产生两块内存,一块是Java内存区域,一块是C内存区域,前者通过GC回收,C的应由调用recycle()回收

LRU

内部用一个HashMap实现,trimToSize():删除最老最不常用的对象,同时把最新的加入HashMap中,LRU的核心方法

计算inSampleSize

`// 根据maxWidth, maxHeight计算最合适的inSampleSize
public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        if (width > height) {
            inSampleSize = Math.round((float)height / (float)reqHeight);
        } else {
            inSampleSize = Math.round((float)width / (float)reqWidth);
        }
    }
    return inSampleSize;
}`  

缩略图

//缩略图
public static Bitmap thumbnail(String path,
                               int maxWidth, int    maxHeight, boolean autoRotate) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    // 获取这个图片的宽和高信息到options中, 此时返回bm为空
    Bitmap bitmap = BitmapFactory.decodeFile(path, options);
    options.inJustDecodeBounds = false;
    // 计算缩放比
    int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
    options.inSampleSize = sampleSize;
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    options.inPurgeable = true;
    options.inInputShareable = true;
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
    }
    bitmap = BitmapFactory.decodeFile(path, options);
    return bitmap;
}

三级缓存

保存:网络-本地-内存

读取:内存-本地-网络

UI卡顿

UI卡顿原理

60fps -> 16ms(Android每隔16ms绘制UI,达到60fps的要求)

overdraw(每个像素区域被绘制多次,布局中有太多重叠的部分,eg子父类有自己的background)

原因分析

  1. 人为在UI线程中做轻微耗时操作,导致UI线程卡顿
  2. 布局Layout过于复杂,无法在16ms内完成渲染
  3. 同一时间动画执行的次数过多,导致CPU或GPU负载过重
  4. View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或者GPU负载过重
  5. View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染
  6. 内存频繁触发GC过多,导致暂时阻塞渲染操作
  7. 冗余资源以及逻辑等导致加载和执行缓慢
  8. ANR

总结

  1. 布局优化。尽量使用include、merge标签、尽量不要多层嵌套、使用gone代替invisible、使用weight替代长和宽、item若存在复杂的嵌套时使用自定义View替代。
  2. 列表以Adapter优化,列表停止滑动时,才去加载
  3. 背景和图片等内存分配优化,减少不必要的背景设置,图片要压缩
  4. 避免ANR

内存泄漏

Java内存泄漏基础知识

内存泄漏其实就是该被释放的对象,没有被释放。
1. Java内存的分配策略
- 静态存储区(也叫方法区)。存储静态数据,全局变量等
- 栈区。方法的局部变量,容量有限
- 堆区。存储new出来的对象、数组,gc会回收

  1. Java是如何管理内存的
    不再使用的,用gc回收

  2. Java中的内存泄漏
    内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存控件的浪费。

Android内存泄漏

  1. 单例

    因为单例的static属性,造成它的生命周期与App一样长,如果单例中持有不需要再使用对象的引用,那么该对象将无法被回收,造成内存泄漏。例如单例的方法中传入Context,修改方法是使用AppcationContext。

  2. 匿名内部类

    非静态内部类会持有外部类的引用,若该内部类中有static的成员,会导致跟App生命周期一样长。正确的做法是改为静态内部类

  3. handler

    非静态内部类

  4. 避免使用static变量

    导致跟App生命周期一样长。在类设计的时候,要充分考虑是否设置为static

  5. 资源未关闭造成的内存泄漏

  6. AsyncTask造成的内存泄漏

    与handler的原因类似

内存管理

内存管理机制概述

  1. 分配机制
  2. 回收机制

Android内存管理机制

  1. 分配机制

    最大限度的让更多进程存活在内存中

  2. 回收机制

    根据进程优先级进行回收

内存管理机制的特点

开发的目标:

  1. 更少的占用内存
  2. 在合适的时候,合理的释放系统资源
  3. 在系统内存紧张的情况下,能释放掉大部分不重要的资源,来为Android系统提供可用的内存。
  4. 能够很合理的在特殊生命周期中,保存或者还原重要数据,以致于系统能够正确的重新回复该应用

内存优化方法

  1. 当Service完成任务后,尽量停止它。(能用IntentService急用)
  2. 当UI不可用的时候,释放掉一些只有UI使用的资源
  3. 在系统内存紧张的时候,尽可能多的释放掉一些非重要的资源
  4. 避免滥用Bitmap导致的内存浪费
  5. 使用针对内存优化过的数据容器和数据结构(如避免使用枚举,ArrayMap代替HashMap)
  6. 避免使用依赖注入框架
  7. 使用ZIP对齐的APK
  8. 使用多进程,如使用WebView、地图、推送,开启单独的进程

内存溢出 VS 内存泄漏

内存泄漏积累会导致内存溢出。内存泄漏可以使用LeakCanary

冷启动优化

什么是冷启动

  1. 定义

    冷启动就是在启动应用前,系统中没有该应用的任何进程信息

  2. 冷启动/热启动的区别

    热启动:用户使用返回键退出应用,然后马上又重新启动应用。不会走Application,冷启动会走。

  3. 冷启动时间的计算

    这个时间值从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止

冷启动流程

  • Zygote进程中fork创建出一个新的进程
  • 创建和初始化Application类、创建MainActivity类
  • inflate布局、当onCreate/onStart/onResume方法都走完
  • contentView的measure/layout/draw显示在界面上

Application的构造方法 –> attachBaseContext() –> onCreate() –> Activity的构造方法 –> onCreate() –>配置主题中背景等属性 –> onStart() –> onResume() –> 测量布局绘制显示在界面上

如何对冷启动的时间进行优化

  1. 减少Application onCreate()方法的工作量,减少第三方类库的初始化工作,懒加载
  2. 不要让Application参与业务的操作
  3. 不在Application进行耗时操作
  4. 不要以静态变量的方式在Application中保存数据
  5. 减少布局的复杂性,尽量ViewStub/mainThread

其他优化

android不用静态变量存储数据

  1. 静态变量等数据由于进程已经被杀死而被初始化
  2. 使用其他数据传输方式:文件/sp/contentProvider

有关sp的安全问题

  1. 不能跨进程同步
  2. 存储SP的文件过大问题(存储配置型的数据),可能造成卡顿

内存对象序列化

序列化:将对象的状态信息转换为可以存储或传输的形式的过程
1. Serializeble:会产生大量的临时变量,引起频繁的gc,影响UI性能
2. Parcelable:性能比Serializeble高,不能使用在要将数据存储在磁盘上的情况

避免在UI线程中做繁重的操作

老生常谈啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值