androidandroid-viewpagergoogle-playandroidxinflate-exception

How to fix ViewPager class inflating exception which is reproducable only with the released Google Play Store version


I converted an application not so long ago from older AppCompat to AndroidX. I tested my updates in my developer environment and all looked good. However to my surprise the clean install of the app from the Google Play Store crashes right after start. I cannot reproduce the crash even with a cleanly wiped cold booted emulator neither with a debug build or a release build.

Here is the source code of the whole app: https://github.com/gdgfresno/androidify-yourself (the README.md refers to the pre AndroidX state, don't mind that) and the Store listing https://play.google.com/store/apps/details?id=com.valleydevfest.androidify

When I reproduce the crash with the store distributed app I get this crash on the call stack:

2020-07-30 11:02:28.417 31599-31599/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.valleydevfest.androidify, PID: 31599
    android.view.InflateException: Binary XML file line #30 in com.valleydevfest.androidify:layout/fragment_main: Binary XML file line #30 in com.valleydevfest.androidify:layout/fragment_main: Error inflating class android.support.v4.view.ViewPager
    Caused by: android.view.InflateException: Binary XML file line #30 in com.valleydevfest.androidify:layout/fragment_main: Error inflating class android.support.v4.view.ViewPager
    Caused by: java.lang.ClassNotFoundException: android.support.v4.view.ViewPager
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:454)
        at android.view.LayoutInflater.createView(LayoutInflater.java:815)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1126)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
        at com.valleydevfest.androidify.PlaceholderFragment.onCreateView(PlaceholderFragment.java:48)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1514)
        at android.app.Activity.performStart(Activity.java:7838)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3398)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2109)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7682)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.lang.ClassNotFoundException: android.support.v4.view.ViewPager
        at java.lang.Class.classForName(Native Method) 
        at java.lang.Class.forName(Class.java:454) 
        at android.view.LayoutInflater.createView(LayoutInflater.java:815) 
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006) 
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961) 
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123) 
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084) 
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1126) 
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:682) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:534) 
        at com.valleydevfest.androidify.PlaceholderFragment.onCreateView(PlaceholderFragment.java:48) 
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600) 
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881) 
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238) 
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303) 
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439) 
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079) 
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869) 
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824) 
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727) 
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663) 
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613) 
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246) 
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542) 
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201) 
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1514) 
        at android.app.Activity.performStart(Activity.java:7838) 
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3398) 
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) 
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2109) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7682) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 

The problem is that the execution runtime for some reason searches for the pre-AndroidX ViewPager android.support.v4.view.ViewPager however that should be androidx.viewpager.widget.ViewPager.

THis is what in my layout https://github.com/gdgfresno/androidify-yourself/blob/master/src/main/res/layout/fragment_main.xml

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_weight="90"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:padding="1dp"
        android:orientation="vertical"
        android:background="@drawable/border">

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewPagerHead"
            android:layout_weight="30"
            android:layout_width="match_parent"
            android:layout_height="0dp" />

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewPagerBody"
            android:layout_weight="30"
            android:layout_width="match_parent"
            android:layout_height="0dp" />

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewPagerLegs"
            android:layout_weight="30"
            android:layout_width="match_parent"
            android:layout_height="0dp" />
    </androidx.appcompat.widget.LinearLayoutCompat>

just as in my code https://github.com/gdgfresno/androidify-yourself/blob/master/src/main/java/com/valleydevfest/androidify/PlaceholderFragment.java

...
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
...
        final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        mViewPagerHead = rootView.findViewById(R.id.viewPagerHead);
        mViewPagerBody = rootView.findViewById(R.id.viewPagerBody);
        mViewPagerLegs = rootView.findViewById(R.id.viewPagerLegs);

        FragmentManager fm = getActivity().getSupportFragmentManager();
        mViewPagerHead.setAdapter(new AndroidifyViewPagerAdapter(fm, AndroidDrawables.getHeads()));
        mViewPagerBody.setAdapter(new AndroidifyViewPagerAdapter(fm, AndroidDrawables.getBodies()));
        mViewPagerLegs.setAdapter(new AndroidifyViewPagerAdapter(fm, AndroidDrawables.getLegs()));

The crash happens at the final View rootView = inflater.inflate(R.layout.fragment_main, container, false); line. I wiped my gradle cache (rm -rf $HOME/.gradle/cache). I wiped the emulators, I uninstalled apps, I rebuilt the app. Where does it get the android.support.v4.view.ViewPager from when everything is androidx?

I also included the implementation 'androidx.fragment:fragment:1.2.5' package in my build.gradle, although that was not needed for a successful compilation. Project issue filed https://github.com/gdgfresno/androidify-yourself/issues/2


Solution

  • I went ahead and converted ViewPager used by the app to ViewPager2 following this guide and my common sense: https://developer.android.com/training/animation/vp2-migration

    Here is the commit: https://github.com/gdgfresno/androidify-yourself/commit/df74fc3f0f2d98c3ae59969db11d3678d57a923a Apparently this removed all ambiguity and chance for the underlying frameworks to pull out an inappropriately old ViewPager "rabbit from the magic hat".