Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cant find the cause of my crash with this stack trace

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))
    );
}
like image 343
BamsBamx Avatar asked Apr 17 '16 13:04

BamsBamx


2 Answers

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

like image 80
BamsBamx Avatar answered Oct 28 '22 17:10

BamsBamx


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.

like image 28
Rohit Arya Avatar answered Oct 28 '22 16:10

Rohit Arya