Android内存优化

1、内存优化概述

我们最常见的内存问题有以下几种:

  • 内存抖动:短时间内频繁分配和回收内存,直观表现是内存曲线呈现锯齿装
  • 内存泄漏:仍然持有实际上已经没用的对象导致它不能被回收
  • 内存溢出:可用内存不足,会导致程序崩溃

我们排查内存问题,可以使用以下一些工具:

  1. Android Studio的profiler
  2. Memory Analyzer(MAT)
  3. LeakCanary开源库

2、内存抖动实战

模拟内存抖动

class MainActivity : AppCompatActivity() {

    private val handler = Handler(Looper.getMainLooper())
    private val runnable = object: Runnable {
        override fun run() {
            val arr = Array(10000) { "$it" }
            handler.postDelayed(this, 30)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        handler.post(runnable)
    }
}

查看profiler

在这里插入图片描述

查看占用内存比较多的对象,看它们分配的调用栈

3、内存泄漏实战

模拟内存泄漏

interface Callback {
    fun callback()
}

object CallbackManager {

    val callbacks : MutableList<Callback> = ArrayList()

}

class LeakActivity : AppCompatActivity(), Callback {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_leak)

        val bitmap = BitmapFactory.decodeResource(resources, R.drawable.girl)
        findViewById<ImageView>(R.id.imageView).setImageBitmap(bitmap)
        CallbackManager.callbacks.add(this)
    }

    override fun callback() {

    }
}

不断地进入这个界面,然后看内存变化情况如下图

在这里插入图片描述

内存不断地增长,这时候如果record了

在这里插入图片描述

这时候可以初步确定出现了内存泄漏,也可以通过profiler初步排查,但是这样不太准确。我们可以通过dump来查看,也就是点这个按钮

在这里插入图片描述

可以生成一个Heap Dump记录,如下

在这里插入图片描述

点击进去就可以很明显地看出来是LeakActivity泄漏了

在这里插入图片描述

也可以导出.hprof文件,使用MAT查看,不过需要经过转换

hprof-conv /Users/qianyuelin/develop/source/mine/Performance/memory.hprof /Users/qianyuelin/develop/source/mine/Performance/converted.hprof

4、Bitmap大图检测

Bitmap是造成很多内存问题的罪魁祸首,不过随着各种开源库如Glide等的不断完善,现在已经好很多了。但是Bitmap对于内存优化来说还是不可忽视。如果加载一个Bitmap到比它宽高要小的View上,那么就可能存在内存浪费。我们可以通过ARTHook来检测这种情况.

首先添加依赖

implementation 'me.weishu:epic:0.11.0'

添加代码

class App: Application() {

    override fun onCreate() {
        super.onCreate()
        DexposedBridge.hookAllConstructors(ImageView::class.java, object : XC_MethodHook() {
            override fun afterHookedMethod(param: MethodHookParam?) {
                DexposedBridge.findAndHookMethod(ImageView::class.java, "setImageBitmap", Bitmap::class.java, ImageHook())
            }
        })
    }

    class ImageHook : XC_MethodHook() {
        override fun afterHookedMethod(param: MethodHookParam) {
            super.afterHookedMethod(param)

            val bitmap = param.args[0] as Bitmap
            val imageView = param.thisObject as ImageView

            if(imageView.width <= 0) {
                imageView.post { check(bitmap, imageView) }
            } else {
                check(bitmap, imageView)
            }
        }

        private fun check(bitmap: Bitmap, imageView: ImageView) {
            if(imageView.width < bitmap.width && imageView.height < bitmap.height) {
                Log.w("BigImage", "View size: ${imageView.width} * ${imageView.height}, image size: ${bitmap.width} * ${bitmap.height}", Throwable())
            }
        }
    }
}

然后加载一个大图片到一个小的ImageView,可以看到如下log

2021-06-25 03:10:33.977 28343-28343/com.sahooz.performance W/BigImage: View size: 275 * 275, image size: 2343 * 3520
    java.lang.Throwable
        at com.sahooz.performance.App$ImageHook.check(App.kt:37)
        at com.sahooz.performance.App$ImageHook.access$check(App.kt:21)
        at com.sahooz.performance.App$ImageHook$afterHookedMethod$1.run(App.kt:29)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:236)
        at android.app.ActivityThread.main(ActivityThread.java:7876)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

5、线上内存监控方案

  • 方案一:在特定场景下(比如说内存占用超过80%),Debug.dumpHprofData()生成文件,在合适的时机(比如说Wi-Fi连接的时候)回传服务器,然后开发者手动导入内存分析工具来分析。
  • 方案二:修改LeakCanary,带到线上

6、参考

本文主要参考了慕课网这个课程

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值