• 现象描述
  • 初步分析
  • 找寻过程
  • 最终结果


现象描述

  1. 日历控件,之前反馈工作良好,现在出现故障
  2. 结合初步分析1.发现:targetVerion加上之后,点击上下月切换,则出现,只有选中日更新,而其他日则未刷新;去除targetVersion后则恢复正常。

初步分析

  1. 因为之前工作良好,所以可以对比代码,找出可能的错误点,然后发现是Manifest.xml添加targetVersion之后不能正常工作
  2. 根据搜寻结果1,推测这里的应用不支持targetVersion为21的情况,但是支持10的情况,通过去除targetVersion,然后逐步增大minSdkVersion直到出现异常,发现到14时,则出现异常。说明在非开启兼容模式下,14的API较之前版本发生变化。但是具体是哪个API的变化导致的,情况仍然不明。
  3. 再次结合现象描述2,推测,因为没有刷新,可能是onDraw未调用,经过验证,在非兼容模式下(API Level 14+),onDraw确实未调用,而可能引起重绘的原因是是invalidate()的调用。

找寻过程

  1. 根据初步分析1,查找targetVersion的作用:发现target具有控制应用向前兼容的能力,如果平台版本高于targetVersion,则启用兼容模式;如果平台版本不高于,则不启用;(关键发现)如果未设置targetVersion,则值同minSdkVersion相同。
  2. 根据初步分析2和搜寻结果1,在grepcode.com中对比API Level 14和能找到的临近低版本的View.invalidate(),发现代码发生变化,经过分析可能是新增的skipInvalidate()导致invalidate()没有达到预期结果。
  3. 然后搜寻skipInvalidate(),在StackOverflow()中找到一个问答。该问答中说明了原因,从Android 3.0中,使用了硬件加速机制,如果A视图位于B视图之上,则当调用过B.invalidate()后,则不再调用A.invalidate()。日历控件中的情况正式如此,在更新试图的方法中,容纳日期格子的Layout呗调用invalidate(),那么日期格子的invalidate()就不会重复调用,进而出现日期未更新的情况。

最终结果

根本原因是,项目中原本使用的控件是在Android2.x版本中开发的,那是还没有硬件加速机制。现在添加targetSDKVersion(19)后,意味着我们的代码能够在Android4.4版本上正常运行。但实际上并没有测试,所以才出现bug。如果再修改targetSdkVersion后进行测试就能在早起发现该问题。总之,为了杜绝这种错误再次发生,需要:
1. 建立完整的测试流程,保证修改后立即测试,保证修改的正确性。
2. 现在项目中使用的技术还停留在2.x上,需要逐步对项目进行适当的技术更新,以满足用户平台更新(大部分用户使用4.x)的需要。