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
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".