自定义键盘实现onDraw方法后耗电与内存占用过高问题求助
自定义KeyboardView onDraw导致高电池消耗与内存占用问题排查与优化
我看了你的代码,能明显发现几个导致性能问题的关键点,咱们一步步来拆解和优化:
原问题代码片段
@Override public void onDraw(Canvas canvas) { super.onDraw(canvas); healA = ddKeys_Master.autohealing; phealA = ddKeys_Master.autophealing; Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); //get all your keys and draw whatever you want List<Keyboard.Key> keys = getKeyboard().getKeys(); for (Keyboard.Key key : keys) { if (key.label != null) { paint.setColor(Color.parseColor("#66cccc")); switch (key.codes[0]) { case 888: if (healA && phealA) { if (ddKeys_Master.healcount >= ddKeys_Master.autoHealCount) { paint.setColor(Color.parseColor("#ef0404")); //red canvas.drawCircle(17 * scale, 17 * scale, 7 * scale, paint); } else if (ddKeys_Master.healcount >= (ddKeys_Master.autoHealCount * 3 / 4)) { paint.setColor(Color.parseColor("#ef6904")); //orange canvas.drawCircle(17 * scale, 17 * scale, 7 * scale, paint); } // ... 剩余重复的颜色判断与绘制逻辑 } else if (healA) { // ... 重复的healcount判断逻辑 } else if (phealA) { // ... 重复的phealcount判断逻辑 } } } } }
核心性能问题分析
- 频繁创建Paint对象:每次
onDraw都会new一个Paint,而onDraw可能每秒被调用几十次,这会产生大量临时对象,频繁触发GC,直接拉高内存占用和CPU消耗,进而增加电池消耗。 - 重复解析颜色字符串:
Color.parseColor()是一个开销不小的操作,你每次绘制都重复解析相同的十六进制颜色,完全是没必要的计算浪费。 - 冗余的绘制逻辑:大量重复的if-else判断和
drawCircle调用,没有做代码复用,增加了CPU的计算量,延长了onDraw的执行时间。 - 无节制的重绘触发:如果你的逻辑中没有控制
invalidate()的调用时机,比如数据没变化也频繁触发重绘,会让onDraw一直重复执行,加剧性能问题。
优化后的代码示例
// 把Paint和颜色常量定义为类成员变量,只初始化一次 private Paint circlePaint; private final int COLOR_RED = Color.parseColor("#ef0404"); private final int COLOR_ORANGE = Color.parseColor("#ef6904"); private final int COLOR_YELLOW = Color.parseColor("#efeb04"); private final int COLOR_LIGHT_GREEN = Color.parseColor("#09f247"); private final int COLOR_GREEN = Color.parseColor("#17a55e"); private final int COLOR_DEFAULT = Color.parseColor("#66cccc"); // 在构造方法或者onInitialize中初始化Paint @Override public void onInitialize() { super.onInitialize(); circlePaint = new Paint(); circlePaint.setTextAlign(Paint.Align.CENTER); circlePaint.setAntiAlias(true); // 开启抗锯齿,提升绘制效果同时不增加太多开销 } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); healA = ddKeys_Master.autohealing; phealA = ddKeys_Master.autophealing; List<Keyboard.Key> keys = getKeyboard().getKeys(); for (Keyboard.Key key : keys) { if (key.label != null && key.codes[0] == 888) { // 只处理目标key,减少循环内判断 // 复用颜色判断逻辑 if (healA) { drawHealCircle(canvas, 17 * scale, 17 * scale, 7 * scale); } if (phealA) { drawPhealCircle(canvas, 37 * scale, 17 * scale, 7 * scale); } } } } // 提取重复的颜色判断逻辑为单独方法 private void drawHealCircle(Canvas canvas, float x, float y, float radius) { int color; if (ddKeys_Master.healcount >= ddKeys_Master.autoHealCount) { color = COLOR_RED; } else if (ddKeys_Master.healcount >= (ddKeys_Master.autoHealCount * 3 / 4)) { color = COLOR_ORANGE; } else if (ddKeys_Master.healcount >= (ddKeys_Master.autoHealCount / 2)) { color = COLOR_YELLOW; } else if (ddKeys_Master.healcount >= (ddKeys_Master.autoHealCount / 4)) { color = COLOR_LIGHT_GREEN; } else { color = COLOR_GREEN; } circlePaint.setColor(color); canvas.drawCircle(x, y, radius, circlePaint); } private void drawPhealCircle(Canvas canvas, float x, float y, float radius) { int color; if (ddKeys_Master.phealcount >= ddKeys_Master.autoPhealCount) { color = COLOR_RED; } else if (ddKeys_Master.phealcount >= (ddKeys_Master.autoPhealCount * 3 / 4)) { color = COLOR_ORANGE; } else if (ddKeys_Master.phealcount >= (ddKeys_Master.autoPhealCount / 2)) { color = COLOR_YELLOW; } else if (ddKeys_Master.phealcount >= (ddKeys_Master.autoPhealCount / 4)) { color = COLOR_LIGHT_GREEN; } else { color = COLOR_GREEN; } circlePaint.setColor(color); canvas.drawCircle(x, y, radius, circlePaint); }
额外优化建议
- 控制重绘时机:只有当
healcount、phealcount、healA、phealA这些数据发生变化时,才调用invalidate()触发重绘,避免无意义的重复绘制。 - 减少不必要的变量读取:如果
ddKeys_Master中的字段是频繁读取的,可以考虑在数据变化时缓存到本地成员变量,减少跨对象访问的开销。 - 开启硬件加速:确保你的View开启了硬件加速(默认是开启的),可以提升Canvas绘制的性能。
内容的提问来源于stack exchange,提问作者kalaLokia
相关产品推荐
相关产品推荐

