之前开发中碰到这样一个bug,部分机型会有崩溃现象,错误内容如下:
E/AndroidRuntime( 3579): FATAL EXCEPTION: main
E/AndroidRuntime( 3579): java.lang.NullPointerException E/AndroidRuntime( 3579): at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:431)
E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462)
很奇怪,明明没有什么特别的地方,但是在Android4.3的机器上就是会有崩溃~~~
问题分析
在stackoverflow上发现有人碰到过相同的问题。
- 问题原因
RelativeLayout的onMeasure方法中有这样一段代码:
if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
width += mPaddingRight;
if (mLayoutParams != null && mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
width = Math.max(width, getSuggestedMinimumWidth());
width = resolveSize(width, widthMeasureSpec);
看起来没有问题,但是对比Android 4.3源码,
if (mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
这也就是为什么会在Android4.3手机上出现崩溃的原因之一。再深入探究一下mLayoutParams这个参数为什么会是null。首先看代码的使用方法:
contentView = (ViewGroup) LayoutInflater.
rom(context).inflate(R.layout.select_text_oprate_window, null);
这句代码的意思是,从xml中实例化View视图,但是父视图为空,所以从xml实例化的View视图没有办法attach到View层次树中。那么这个和mLayoutParams有什么关系??~~~
首先看看mLayoutParams这个参数表示什么意思:
/** * The layout parameters associated with this view and used by the parent * {@link android.view.ViewGroup} to determine how this view should be * laid out. * {@hide} */
protected ViewGroup.LayoutParams mLayoutParams;
从注释中可以看出,这个参数表示子View布局的期望值,实际值由父View,或者说整个View树决定。
/** * Get the LayoutParams associated with this view. All views should have * layout parameters. These supply parameters to the parent of this * view specifying how it should be arranged. There are many subclasses of * ViewGroup.LayoutParams, and these correspond to the different subclasses * of ViewGroup that are responsible for arranging their children. * * This method may return null if this View is not attached to a parent * ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)} * was not invoked successfully. When a View is attached to a parent * ViewGroup, this method must not return null. * * @return The LayoutParams associated with this view, or null if no * parameters have been set yet */
@ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_")
public ViewGroup.LayoutParams getLayoutParams() {
return mLayoutParams;
}
在来看这个函数的get方法。注释里面写了两条:
- mLayoutParams这个参数必须设置;
- 当View视图attach到父视图(View层次树),mLayoutParams这个参数肯定不会为null。
问题解决
回到项目代码,
contentView = (ViewGroup) LayoutInflater.
rom(context).inflate(R.layout.select_text_oprate_window, null);
首先,这种inflate方法没有父布局,所以也没有attach这一说。再者,项目代码中没有显示调用contentView.setLayoutParams(params)方法。按照源码中的说法,这个参数可能报NullPointerException。(为什么是可能??!!)
解决方法:
- 采用一般建议的inflate方法
contentView = (ViewGroup) LayoutInflater.
rom(context).inflate(R.layout.select_text_oprate_window, parent,false);
- 显示setLayoutParams
contentView = (ViewGroup) LayoutInflater.
from(context).inflate(R.layout.select_text_oprate_window, null);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
contentView.setLayoutParams(params);