如何修复 ViewPager class 膨胀异常,该异常仅可在已发布的 Google Play 商店版本中重现

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

我不久前将一个应用程序从较旧的 AppCompat 转换为 AndroidX。我在我的开发环境中测试了我的更新,一切看起来都很好。然而令我惊讶的是,从 Google Play Store 全新安装的应用程序在启动后立即崩溃。即使使用干净擦除的冷启动模拟器,无论是调试版本还是发布版本,我都无法重现崩溃。

这是整个应用程序的源代码:https://github.com/gdgfresno/androidify-yourself (README.md 指的是 AndroidX 之前的状态,不要介意)和商店列表 https://play.google.com/store/apps/details?id=com.valleydevfest.androidify

当我使用商店分布式应用重现崩溃时,我在调用堆栈上遇到此崩溃:

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) 

问题是执行运行时出于某种原因搜索了 AndroidX 之前的 ViewPager android.support.v4.view.ViewPager 但是应该是 androidx.viewpager.widget.ViewPager.

这就是我的布局 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>

就像我的代码一样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()));

崩溃发生在 final View rootView = inflater.inflate(R.layout.fragment_main, container, false); 行。我清除了 gradle 缓存 (rm -rf $HOME/.gradle/cache)。我擦除模拟器,卸载应用程序,重建应用程序。当一切都是 androidx 时,它从哪里得到 android.support.v4.view.ViewPager

我还在 build.gradle 中包含了 implementation 'androidx.fragment:fragment:1.2.5' 包,尽管这不是成功编译所必需的。提交项目问题 https://github.com/gdgfresno/androidify-yourself/issues/2

我继续按照本指南和我的常识将应用程序使用的 ViewPager 转换为 ViewPager2:https://developer.android.com/training/animation/vp2-migration

这是提交:https://github.com/gdgfresno/androidify-yourself/commit/df74fc3f0f2d98c3ae59969db11d3678d57a923a 显然,这消除了所有歧义和底层框架拉出一个不恰当的旧ViewPager“魔术帽中的兔子”的机会。