What my app does, is to display a system overlay view attached through windowManager.addView()
and WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
flag. This is done with a Service, which manages that view visibility and a few other things.
However, I am receiving crash reports and can't reproduce them. Also the crash stack trace has nothing to do with the package of my app, so I really can't get the root of this problem. The following are two stacktraces which come from different sources, but seem to be related:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.measure(int, int)' on a null object reference
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2388)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2101)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1297)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7011)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
at android.view.Choreographer.doCallbacks(Choreographer.java:590)
at android.view.Choreographer.doFrame(Choreographer.java:560)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
.
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getMeasuredWidth()' on a null object reference
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2484)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2181)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1293)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6599)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5616)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
It seems like the OS (ViewRootImpl) is causing this problem because it owns a null reference to my view. So I can't find a workaround for this.
It seems to happen on all Android versions since 4.4 and my app is Proguarded. These stack traces are obtained from Google Play Store crash reports
Here is how I attach view to system window as an overlay:
private void attachToSystemWindows(boolean overlayNavigationBar) {
final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
final DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
final boolean isNavBarInBottom = isNavBarInBottom();
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
0,
0,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
0x50728,
-3
);
params.gravity = Gravity.TOP;
windowManager.addView(this, params);
}
private boolean isNavBarInBottom() {
final boolean isLargeDevice = getResources().getConfiguration().smallestScreenWidthDp >= 600;
final int orientation = getResources().getConfiguration().orientation;
if (BuildConfig.DEBUG)
Log.d("MeshView", "Is NavBar in bottom: " + (isLargeDevice || orientation == Configuration.ORIENTATION_PORTRAIT));
return isLargeDevice || orientation == Configuration.ORIENTATION_PORTRAIT;
}
And my onMeasure method:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (BuildConfig.DEBUG) Log.d("MeshView", "OnMeasure");
setMeasuredDimension(
Math.max(getSuggestedMinimumWidth(), resolveSize(SIZE_MIN_WIDTH, widthMeasureSpec)),
Math.max(getSuggestedMinimumHeight(), resolveSize(SIZE_MIN_HEIGHT, heightMeasureSpec))
);
}
Amazing. I finally found the root of my problem, but not the reason... I was doing something with my view: I wanted to change its layout params depending on its orientation, so I overrode onConfigurationChanged()
.
Then I used a nasty way to update layout params: detach and then attach it again to WindowManager
, just like this:
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (!(layoutParams instanceof WindowManager.LayoutParams)) return; //Fix for some ClassCastException in Samsung devices
final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
final DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
final boolean isNavBarInBottom = isNavBarInBottom();
layoutParams.width = calculateWindowWidth(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight);
layoutParams.height = calculateWindowHeight(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight);
windowManager.removeView(this);
windowManager.addView(this, layoutParams);
}
I guess something bad happens in the ViewRootImpl
class, causing it to not handle the reattach properly, causing an error.
To fix this, just must go through the proper way: Use the WindowManager.updateViewLayout();
method:
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (BuildConfig.DEBUG) Log.d("MeshView", "OnConfigurationChanged");
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (!(layoutParams instanceof WindowManager.LayoutParams)) return; //Fix for some ClassCastException in Samsung devices
final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
final DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
final boolean isNavBarInBottom = isNavBarInBottom();
layoutParams.width = calculateWindowWidth(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight);
layoutParams.height = calculateWindowHeight(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight);
//windowManager.removeView(this); DON'T DO THIS
//windowManager.addView(this, layoutParams); DON'T DO THIS
windowManager.updateViewLayout(this, layoutParams); //DO THIS INSTEAD
}
This seems to fix my problem. I must thank to the author of this post, which allowed me to find this fix): Android: change LayoutParams of View added by WindowManager
I think you are not passing the correct flag while creating params
.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
0,
0,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
0x50728, // problem might be here.
-3
);
It should be one of these values.
So use it something like this (avoid passing hex values directly):
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight),
0,
0,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, //any flag
PixelFormat.TRANSLUCENT
);
I have also referenced PixelFormat.
Try to change like this and see if that works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With