简介
开发过程中,部分用户(华为 NXT-AL10,华为 MHA AL00等)反馈只要弹框就会崩溃,通过反馈用户的协助,找到日志如下:
| java.lang.NullPointerException | 
解决
当在自定义Drawable时,需要注意,如果此Drawable有可能会被当做背景设置为Window的背景,类似于getWindow().setBackgroundDrawable(BlankDrawable),则必须在除了正常的绘制逻辑之外,需要重写Drawable#getConstantState())方法,返回一个不会空的ConstantState对象
解析
从上往下
首先,根据日志,找到最后的崩溃的位置BackdropFrameRenderer#onResourcesLoaded():
| public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback { | 
再往上查DecorView#onWindowDragResizeStart():
| 
 | 
可以发现传递到BackdropFrameRenderer中的resizingBackgroundDrawable实际是一个DecorView中的局部变量mResizingBackgroundDrawable,此时,查找这个局部变量的来源,包括了两个地方:
| public void setWindowBackground(Drawable drawable) { | 
OK,从最终崩溃向上已经到头了,因为无法确定,到底哪里地方会调用public方法,所以,从触发的地方查查看。
从下而上
由于触发地方是项目自定义的一个BottomSheetDialog,初始化代码:
| public BottomSheetDialog(Context context, int style) { | 
通过getWindow().setBackgroundDrawable(BlankDrawable.getInstance())往下跟,发现会调用到PhoneWindow的setBackgroundDrawable(drawable)方法:
| 
 | 
而在这个方法中会把这个drawable设置给DecorView。OK,对上了。
这个时候,查看下之前使用自定义的Drawable代码:
| /** | 
此方法并没有getConstantState()方法,在起父类Drawable中:
| /** | 
查看默认为null。
总结一下,就是在某些特殊情况,特殊机型(问我怎么特殊?我怎么知道,问华为去,┑( ̄Д  ̄)┍),会在某些情况触发了分屏的机制,导致调用到startDragResizing()从而触发了BackdropFrameRenderer中的onResourcesLoaded()而由于自定义的BlankDrawable并没有重写父类getConstantState()方法,导致了NPE
综上,修复代码可简单自定义一个内部类实现ConstantState:
| /** |