Android问题:java.lang.UnsatisfiedLinkError: No implementation found for异常解决方法

ViewPager2设置Adapter报错IllegalArgumentException

1.问题出现场景

首页是由ViewPager2+Fragment实现,而第二个Fragment中又嵌套了ViewPager2+Fragment,当在首页跳转到其他页面后,再按返回键,则程序抛出异常,位置是第二个Fragment在设置adapter的时候报:

java.lang.IllegalArgumentException
        at androidx.core.util.Preconditions.checkArgument(Preconditions.java:36)
        at androidx.viewpager2.adapter.FragmentStateAdapter.onAttachedToRecyclerView(FragmentStateAdapter.java:132)
        at androidx.recyclerview.widget.RecyclerView.setAdapterInternal(RecyclerView.java:1209)
        at androidx.recyclerview.widget.RecyclerView.setAdapter(RecyclerView.java:1161)
        at androidx.viewpager2.widget.ViewPager2.setAdapter(ViewPager2.java:461)
        at com.lihao.wanandroid.ui.navigation.NavigationFragment.initView(NavigationFragment.kt:63)
        at com.lihao.jetpackcore.base.BaseVmFragment.onViewCreated(BaseVmFragment.kt:51)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:332)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
        at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
        at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577)
        at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2722)
        at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:346)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1188)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
        at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

2.解决方案

  • 原因: 原因是adapter是一个成员变量,在Fragment销毁viewPager时,adapter并没有被释放,依旧持有之前的ViewPager中的RecyclerView对象,当Fragment重新创建时,新的viewPager不能和adapter进行绑定,所以抛出异常。
  • 解决办法:在调用viewpager.setAdapter()之前将adapter重新赋值即可,也可以设置成局部变量。

3. 问题分析

既然问题是在viewPager.setAdapter()时报的错,我们就从setAdapter()函数开始分析,以下是setAdapter()中的代码:

    public void setAdapter(@Nullable @SuppressWarnings("rawtypes") Adapter adapter) {
        final Adapter<?> currentAdapter = mRecyclerView.getAdapter();
        mAccessibilityProvider.onDetachAdapter(currentAdapter);
        unregisterCurrentItemDataSetTracker(currentAdapter);
        mRecyclerView.setAdapter(adapter);
        mCurrentItem = 0;
        restorePendingState();
        mAccessibilityProvider.onAttachAdapter(adapter);
        registerCurrentItemDataSetTracker(adapter);
    }

从异常信息中可以看到报错的位置是RecyclerView.setAdapter(),然后又到了RecyclerView.setAdapterInternal(),这是因为RecyclerView.setAdapter()有调用了RecyclerView.setAdapterInternal()函数,通过注释我们知道这个函数是用来重新设置adapter并添加监听器,然后在调用onAttachedToRecyclerView()时抛出异常,那我们直接看setAdapterInternal()函数:

    private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        if (!compatibleWithPrevious || removeAndRecycleViews) {
            removeAndRecycleViews();
        }
        mAdapterHelper.reset();
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }
        if (mLayout != null) {
            mLayout.onAdapterChanged(oldAdapter, mAdapter);
        }
        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
        mState.mStructureChanged = true;
    }

函数中首先判断了adapter是否为空,然后调用onAttachedToRecyclerView(),将recyclerView自身作为参数传递,然后进到onAttachedToRecyclerView中看到是一个空函数,这是因为我们看到的是Adapter基类下的实现,而我们传进来的是子类FragmentStateAdapter,那我们进到FragmentStateAdapter中查看onAttachedToRecyclerView()的实现:

    @CallSuper
    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        checkArgument(mFragmentMaxLifecycleEnforcer == null);
        mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
        mFragmentMaxLifecycleEnforcer.register(recyclerView);
    }

方法只有三行,首先是调用了checkArgument()函数,然后创建FragmentMaxLifecycleEnforcer对象,最后调用register(),而异常信息中最顶端报错的函数就是checkArgument(),也就是这个函数抛出了IllegalArgumentException异常,越来越接近真相了,那我们进去看一看:

    public static void checkArgument(boolean expression) {
        if (!expression) {
            throw new IllegalArgumentException();
        }
    }

🤯,没想到这里面更简单,只是判断了一下expression,如果是false则跑出异常,而这个值是由刚才的方法传进来的mFragmentMaxLifecycleEnforcer == null,也就是说我的mFragmentMaxLifecycleEnforcer对象不为空,那么这个FragmentMaxLifecycleEnforcer类到底是个什么东西呢?然后我找到了对他的注释:

Pauses (STARTED) all Fragments that are attached and not a primary item.
Keeps primary item Fragment RESUMED.
翻译过来为:暂停(启动)所有附加的而不是主要项的片段。保持主项目片段恢复。

看的不是太懂,但是大致可以看出应该是一个管理生命周期的,那我们就不管他了,还是分析一下他为什么不为空吧。
回想了一下报错的场景,因为使用了Navigation管理Fragment的跳转,而Navigation存在一个问题就是当从AFragment跳转到BFragment时,AFragment会被销毁,当从BFragment返回到AFragment会重新走onCreateView(),也就是说viewpager会重新设置setAdapter(),而我的adapter设置的是成员变量,也就是虽然AFragment走了onDestoryView(),但是对象并没有被销毁,那么adapter也没有被重新创建,所以setAdapter()设置的还是之前的adapter,那么也找到原因所在了,也就是说将adapter设置为局部变量,在调用viewPager.setAdapter()重新赋值即可,运行了一下果然没有问题了。

不过你可能会产生疑问🤔️,为什么设置之前的adapter就会报错呢,为了找到这个原因我又查看了FragmentStateAdapter的源码,发现和onAttachedToRecyclerView对应的还有一个方法onDetachedFromRecyclerView(),没错这个方法就是用来释放mFragmentMaxLifecycleEnforcer对象的,将其赋为null的,可以看他的源码:

    @CallSuper
    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
        mFragmentMaxLifecycleEnforcer = null;
    }

那么这个方法又是在哪里被调用的呢,在setAdapterInternal(),也就是上面我们分析的那个函数,这个函数中通过判断mAdapter是否为空,然后进行重制mAdapter,如果非空,则会进行释放,然后重新将传进来的adapter赋给mAdapter,那为什么我们的没有释放呢,那是因为它本来就是null,不需要释放。这所以会产生这样的问题,是因为Navigation的机制,在打开AFragment的时候,设置adapter时通过onAttachedToRecyclerView进行了绑定,而在AFragment跳转到BFragment的时候,AFragment会走onDestoryView(),所以viewPager已经被释放了,当BFragment返回到AFragment,viewpager已经不再是之前的不是一个对象了,所以此时新的Viewpager并没有设置Adapter,所以没有能释放FramengStateFragment中的mFragmentMaxLifecycleEnforcer对象,导致viewpager和FragemntStateFragment绑定失败抛出异常。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351