Anroid App内存优化

背景

在移动开发过程中,由于app内存和存储空间有限,而系统分配给每个app的内存是有一定限制的,当app申请的内存超过限制后,就有可能出现内存溢出而导致app崩溃,影响用户体验甚至导致用户卸载app,因此性能优化是非常值得投入的一个事情。一般来说,所有的优化都可以分为以下两种情况:

*   以空间换时间(数据预加载)
*   以时间换时间(1.串行改并行 2.懒加载:先初始化优先级高的,优先级低的延迟加载)

定义

优化处理 应用程序的内存使用、空间占

作用

避免因内存使用/管理不当,导致内存泄漏、 内存溢出、内存占用过大,导致app卡顿甚至崩溃

常见的内存问题 & 解决方案

  • 常见的内存问题如下:
    内存泄露
    内存抖动
    图片Bitmap相关
    代码质量 & 数量
    日常不正确使用

内存泄漏

  • 定义:在程序申请内存后,当该内存不再需要时,无法释放 & 归还给程序
  • 影响:当泄漏的内存达到一定程度,新申请的内存大于系统可分配内存时,则会造出内存溢出(OOM)
  • 发生内存泄漏的本质原因:生命周期长的对象持有生命周期短的对象
  • 常见内存泄漏
    集合类
    Static关键字修饰的成员变量
    非静态内部类 / 匿名类
    资源对象使用后未关闭

图片资源Bitmap相关

  • 为什么需要优化bitmap:Android分配给每个应用的内存有限,而图片非常占用内存,很多情况下,图片所占内存占整个应用内存的大部分
  • 优化方向:
    • 使用完毕后释放资源:Bitmap.recycle, SoftWeafrence
    • 根据分辨率适配&缩放图片: 设置多套图片资源、BitmapFactory.decodeResouce、BitmapOption.inSampleSize
    • 按需选择合适的解码方式
    • 设置图片缓存: 1. 3级缓存 2.SoftReference

内存抖动

简介

  • 定义: 内存大小频繁上下较大范围波动
  • 原因:短时间内程序频繁创建对象 & 垃圾回收
  • 后果:频繁gc导致卡顿,甚至oom

优化方案

尽量避免频繁创建大量、临时的小对象

常用性能优化tips

  • 线程池
    优先采用ThreadPollExecutor, 不要用new Thread(), 好处是

1.线程可重复利用,节省线程的创建和回收开销
2.控制线程的并发数,减少并发问题
3.控制线程状态

  • UI
    1. 合理利用include、merge、viewstube,使用ConstraintLayout, 减少布局层级,提高页面渲染效率
    2. 窗口默认有一个不透明的背景,可以去掉的: getWindow().setBackground(null),或者修改xml
    3. ui局部刷新:用adatper.notifyItemChanged 替换adatper.notifyDataChanged()
    4. RecycleView替代ListView
  • Bitmap

1.使用BitmapFactory.options 对图片压缩读取, inSampleSize:缩放比例,把图片载入内存之前,先计算合适的缩放比例。

  1. 对像素要求不高的情况下,Config由ARGB_8888改为 ARGB_565。
  2. 及时将bitmap置为null。
  3. 显示本地图片时,用tinypng压缩之后再使用
  4. 许多地方不需要存内存缓存,比如闪屏广告图,app启动之后就不会再使用了,可以加载的时候 memoryCache(false)
  5. 许多地方不需要磁盘缓存,比如发布动态,从图库中选图,不需要再存一份磁盘缓存了,本身那些图片都是本地图片。直接 diskCache(false)
  • 广播
    优先使用LocalBroadcastManger, 好处是:安全性,性能,运行效率更高

  • 在onActivity的onDestroy方法中,对资源和引用进行清除(例如imageview.setImageDrawable(null), editText.clearTextWatchers()),取消网络请求。

  • 耗时操作尽量使用Intentservice而不是service

  • 用SparseArray 替换key为int的HashMap 好处:SpaceArray更节省内存(SparseIntArray、SparseBooleanArray、SparseLongArray,可以支持存储<Integer,Integer>、<Integer,Boolean>、<Integer,Long>),HashMap对于key为int类型时,每次put值的时候会将int包装成Integer,涉及装箱

  • ObjectPool

    通过对象池技术达到重复利用,减少重复对象的创建

  • Job Schedule

    将不紧急的任务交给jobSchedule来处理,选择合适的时间,合适的网络,再一起进行

  • Anroid避免使用enum, 建议用annotation(参考View.setVisibility)

  • 使用StringBuffer或者StringBuilder,避免大量字符串的拼接

  • onLowMemory、onTrimMemory 释放资源

  • 使用Parceable代替Seriable传递序列化数据

  • 在性能敏感的代码中避免创建对象, 例如:onMeasure、onLayout、ondraw

  • WebView优化

    • WebView第一次创建比较耗时,可以预先创建WebView,提前将其内核初始化(webview首次初始化需200ms,第二次只需要20ms)
    • 使用WebView缓存池,用到WebView的地方都从缓存池取,缓存池中没有缓存再创建,注意内存泄漏问题
    • 本地预置html和css,WebView创建的时候先预加载本地html,之后通过js脚本填充内容部分
    • 提前从cdn中请求部分落地html。缓存到本地,点击详情时,只需从缓存中加载即可
    • WebView初始化完成,立刻loadUrl,无需等待框架onCreate或者OnResume结束
    • WebView初始完成后到页面首屏绘制完成之间,尽量减少UI线程的其他操作,繁忙的UI线程会拖慢WebView.loadUrl的速度
  • 启一个后台线程,定时去监控实时的内存使用情况,如果内存紧急了,直接清空Glide或者fresco的内存缓存

启动优化

  • 通过AsyncLayoutInflater异步加载布局
  • 闪屏页优化
  • MultipDex优化(1.新启动一个进程去执行MultiDex优化,创建一个临时文件,作为判断MultiDex是否加载完的条件 2.启动LoadDexActivity去加载MultiDex(LoadDexActivity在单独进程,加载完会删除临时文件) 3.开启while循环,直到临时文件不存在跳出循环,进入Application的onCreate方法 )
  • 在Application的onCreate方法中预创建预创建SplashActivity和MainActivity
  • 数据预加载(对象第一次创建的时候,java虚拟机首先检查类对应的Class
    对象是否已经加载。如果没有加载,jvm会根据类名查找.class文件,将其Class对象载入。同一个类第二次new的时候就不需要加载类对象,而是直接实例化,创建时间就缩短了)
  • 第三方库懒加载(延迟 或者MessageQueue.addIdleHandler)
  • 线程优化
  • 系统调用优化

避免内存泄漏

  • 优先使用Application 的Context而不是Activity Context
  • 内部类引用导致activity泄漏,尤其是Handler(采用静态内部类+弱引用解决)
  • Handler泄漏(弱饮用,onDestory中removeCallbacksAndMessages(null), 使用LifeCycleHandler)
  • 定时轮询任务
  • Cursor,Sqlite,File,IO等资源使用完记得关闭
  • 动画销毁的时候,停止动画
  • 各种监听器(例如广播接收者 ,EditText的addTextChangeListener)及时注销,静态集合及时清空

定位性能问题

  • LeakCancary(继承displayLeakService,上报埋点)
  • BlockCancary 监听主线程耗时方法
  • 通过Aspect J等方式插桩(监控setContentView耗时,主线程耗时)
  • Systrace 和 函数插桩
  • TraceView
  • 替换looper的Printer(涉及到字符串拼接,推荐Debug模式下使用)
  • Profile
  • Mat
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,104评论 1 32
  • 应用启动类型 冷启动 冷启动指的是:用户在 Launcher 程序里点击应用图标时,会通知ActivityMana...
    JunL_Dev阅读 1,883评论 4 5
  • View 自定义View中在onDraw()方法中可以设置padding吗?答案是不能,设置padding后,Vi...
    ElvenShi阅读 1,852评论 0 0
  • 所有知识点已整理成app app下载地址 J2EE 部分: 1.Switch能否用string做参数? 在 Jav...
    侯蛋蛋_阅读 2,442评论 1 4
  • 关键词: 开发, iOS, tableView, 使用SnapKit+FDTemplateLayoutCell时c...
    旺仔Milk阅读 303评论 0 0