一句话总结:
内存抖动就像在派对上狂造垃圾,清洁工(GC)来回疯跑扫垃圾,结果大家卡在原地等打扫,派对卡成PPT!
一、什么是内存抖动?
- 官方解释: 频繁创建大量临时对象 → 触发GC疯狂回收 → 导致UI卡顿。
- 人话翻译: 你一边疯狂造垃圾(new对象),一边逼着清洁工(GC)不停地扫,清洁工扫得越勤,大家(主线程)越得站着等,App就卡成狗!
二、经典作死案例(看看你中招没?)
案例1:在onDraw()里疯狂new对象(自杀式代码)
override fun onDraw(canvas: Canvas) {
val paint = Paint() // 每次绘制都new新Paint!找死!
paint.color = Color.RED
canvas.drawCircle(100f, 100f, 50f, paint)
}
后果: 屏幕每秒刷新60次 → 每秒创建60个Paint对象 → GC直接抽风!
案例2:ListView/RecyclerView适配器里瞎搞
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = View.inflate(context, R.layout.item, null) // 不复用convertView!
val text = "Item " + position + "次" // 疯狂拼接字符串!
view.textView.text = text
return view
}
后果: 快速滑动列表时 → 每秒生成几百个View和String → 内存炸出烟花!
三、如何避免内存抖动?(保命指南)
1. 口诀:能重用就别new!(对象池大法好)
- 优化onDraw():
// 提前初始化,全局只一个Paint!
private val paint = Paint().apply {
color = Color.RED
}
override fun onDraw(canvas: Canvas) {
canvas.drawCircle(100f, 100f, 50f, paint) // 爽!不再new对象!
}
- 优化适配器:复用ConvertView!
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: View.inflate(context, R.layout.item, null) // 复用!
view.textView.text = "Item $position" // 用模板字符串,别拼接!
return view
}
2. 别在循环里搞事情!
作死代码:
fun calculateSum(list: List<Int>): Int {
var sum = 0
for (i in 0 until list.size) {
val item = list[i] // 每次循环都访问list[i],其实可以优化!
sum += item
}
return sum
}
优化代码:
fun calculateSum(list: List<Int>): Int {
var sum = 0
for (item in list) { // 直接用迭代,避免多次get()
sum += item
}
return sum
}
3. 字符串拼接用StringBuilder!
作死代码:
var result = ""
for (i in 0..10000) {
result += i.toString() // 每次拼接都new新String!
}
保命代码:
val builder = StringBuilder()
for (i in 0..10000) {
builder.append(i)
}
val result = builder.toString()
四、查内存抖动的神器(Android Profiler)
- 打开Android Studio → 底部 Profiler → 点 Memory
- 操作你的App → 看内存曲线是否像“锯齿山”(频繁上升下降)
- 点击 Record 抓取内存分配 → 找出哪个类在疯狂创建对象!
五、总结
- 内存抖动的本质: 临时对象太多 → GC加班 → 主线程罚站!
- 保命口诀:
“重用对象是王道,循环里面别瞎闹,字符串用Builder搞,onDraw()里别new爆!”