6

android.view.WindowInsets is added in API level 20.

I import android.view.WindowInsets in my CustomLayout and override onApplyWindowInsets(WindowInsets insets), but ClassNotFoundException occurs in some phones, whose api level are from 14 to 21. What is the reason?

Occurred on: Rooted Nexus 5, Android 4.4.2

Stack trace:

Fatal Exception: java.lang.NoClassDefFoundError: android/view/WindowInsets
   at java.lang.Class.getDeclaredMethods(Class.java)
   at java.lang.Class.getDeclaredMethods(Class.java:656)
   at android.view.ViewDebug.getExportedPropertyMethods(ViewDebug.java:960)
   at android.view.ViewDebug.exportMethods(ViewDebug.java:1047)
   at android.view.ViewDebug.dumpViewProperties(ViewDebug.java:997)
   at android.view.ViewDebug.dumpViewProperties(ViewDebug.java:983)
   at android.view.ViewDebug.dumpView(ViewDebug.java:900)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:855)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dumpViewHierarchy(ViewDebug.java:867)
   at android.view.ViewDebug.dump(ViewDebug.java:793)
   at android.view.ViewDebug.dispatchCommand(ViewDebug.java:416)
   at android.view.ViewRootImpl$W.executeCommand(ViewRootImpl.java:6258)
   at android.view.IWindow$Stub.onTransact(IWindow.java:65)
   at android.os.Binder.execTransact(Binder.java:404)
   at dalvik.system.NativeStart.run(NativeStart.java)
Caused by java.lang.ClassNotFoundException: Didn't find class "android.view.WindowInsets" on path: DexPathList[[zip file "/data/app/***-1.apk"],nativeLibraryDirectories=[/data/app-lib/***-1, /vendor/lib, /system/lib]]
   at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
   at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
   at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
   at java.lang.Class.getDeclaredMethods(Class.java)
   at java.lang.Class.getDeclaredMethods(Class.java:656)
   at android.view.ViewDebug.getExportedPropertyMethods(ViewDebug.java:960)
   at android.view.ViewDebug.exportMethods(ViewDebug.java:1047)
   at android.view.ViewDebug.dumpViewProperties(ViewDebug.java:997)
   at android.view.ViewDebug.dumpViewProperties(ViewDebug.java:983)
   at android.view.ViewDebug.dumpView(ViewDebug
Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
方人也
  • 151
  • 3
  • 10
  • 1
    If WindowInsets requires minimum API level 20 then how can it be found on API level 14 to 19? – Sharjeel Jan 27 '16 at 03:37
  • It occurs only in some phones, the other phones(API level 14 to 21) we test are just ok. How to explain it? – 方人也 Jan 27 '16 at 05:05
  • There's no need to explain. If Android added a function in API level 20 you should not be running that on API level lower than 20. – Sharjeel Jan 27 '16 at 05:47
  • 1
    I understand what you said,but the function I override is called by the system. API level lower than 20 system would never call it. What I confused is that it occurs even on api level 20~21, but not on all api level lower than 20. – 方人也 Jan 27 '16 at 06:25

3 Answers3

7

What happens

The system traverses all public methods of a view and encounters the overridden onApplyWindowInsets with WindowInsets parameter. This type does not exist in the system hence the crash.

Lollipop introduced the View.onApplyWindowInsets method but it also introduced the OnApplyWindowInsetsListener, which if set, is invoked instead of the aforementioned method.

When does it happen

I've had reports of this on Samsung devices running Android 4.4.

It can be triggered by dumping view hierarchy.

What to do about it

So far this doesn't solve anything. To the rescue comes support-v4 library:

public class SampleView extends View {
    public SampleView(final Context context) {
        this(context, null);
    }

    public SampleView(final Context context, @Nullable final AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SampleView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        ViewCompat.setOnApplyWindowInsetsListener(this, new android.support.v4.view.OnApplyWindowInsetsListener() {
            @Override
            public WindowInsetsCompat onApplyWindowInsets(final View v, final WindowInsetsCompat insets) {
                // Do whatever you needed to do in the first place...
                return insets.consumeSystemWindowInsets();
            }
        });
    }
}

Use the above in your common constructor. WindowInsetsCompat is provided by the support-v4 library so it's always present, it does not expose any non-existent future classes directly on the view, and the code is effective only since Lollipop (where actual WindowInsets were introduced).

Why is this happening

Beats me.

Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
0

You cannot use android.view.WindowInsets in lower API versions if it was added in API level 20.

Jas
  • 3,207
  • 2
  • 15
  • 45
  • It occurs only in some phones, the other phones(API level 14 to 21) we test are just ok. How to explain it? – 方人也 Jan 27 '16 at 05:08
0

i meet this too because i use Viewpager2

in viewpager2 use windoeinsets but only use @RequiresApi, it make compile pass , but runtime fail.

i downgrade viewpager2 from beta-01 to stable 1.0.0 so i fix this