android.support.v4.app.Fragment.setUserVisibleHint 应用程序恢复时的空指针 [英] android.support.v4.app.Fragment.setUserVisibleHint null pointer on app resuming

查看:20
本文介绍了android.support.v4.app.Fragment.setUserVisibleHint 应用程序恢复时的空指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在片段代码中的应用程序恢复时遇到崩溃.我自己从未见过这种崩溃,但我通过 TestFlight 收到了用户反馈的崩溃报告.我想我缺少一些东西,因为代码在大多数机器上都可以正常工作.任何帮助将不胜感激.

I am getting a crash on the resume of the app in the fragments code. I have never seen this crash myself but I have received crash reports back from users via TestFlight. I guess there is something that I am missing as the code works fine on most machines. Any help would be greatly appreciated.

这是调用堆栈.

java.lang.NullPointerException
android.support.v4.app.Fragment.setUserVisibleHint in Fragment.java on Line 819
android.support.v4.app.FragmentPagerAdapter.setPrimaryItem in FragmentPagerAdapter.java on Line 130
android.support.v4.view.ViewPager.populate in ViewPager.java on Line 1066
android.support.v4.view.ViewPager.populate in ViewPager.java on Line 914
android.support.v4.view.ViewPager.onMeasure in ViewPager.java on Line 1436
android.view.View.measure in View.java on Line 15323
android.view.ViewGroup.measureChildWithMargins in ViewGroup.java on Line 4924
android.widget.LinearLayout.measureChildBeforeLayout in LinearLayout.java on Line 1421
android.widget.LinearLayout.measureVertical in LinearLayout.java on Line 698
android.widget.LinearLayout.onMeasure in LinearLayout.java on Line 579
android.view.View.measure in View.java on Line 15323
android.view.ViewGroup.measureChildWithMargins in ViewGroup.java on Line 4924
android.widget.FrameLayout.onMeasure in FrameLayout.java on Line 315
android.view.View.measure in View.java on Line 15323
android.support.v4.widget.DrawerLayout.onMeasure in DrawerLayout.java on Line 639
android.view.View.measure in View.java on Line 15323
android.view.ViewGroup.measureChildWithMargins in ViewGroup.java on Line 4924
android.widget.FrameLayout.onMeasure in FrameLayout.java on Line 315
android.view.View.measure in View.java on Line 15323
android.view.ViewGroup.measureChildWithMargins in ViewGroup.java on Line 4924
android.widget.LinearLayout.measureChildBeforeLayout in LinearLayout.java on Line 1421
android.widget.LinearLayout.measureVertical in LinearLayout.java on Line 698
android.widget.LinearLayout.onMeasure in LinearLayout.java on Line 579
android.view.View.measure in View.java on Line 15323
android.view.ViewGroup.measureChildWithMargins in ViewGroup.java on Line 4924
android.widget.FrameLayout.onMeasure in FrameLayout.java on Line 315
com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure in PhoneWindow.java on Line 2155
android.view.View.measure in View.java on Line 15323
android.view.ViewRootImpl.performMeasure in ViewRootImpl.java on Line 1854
android.view.ViewRootImpl.measureHierarchy in ViewRootImpl.java on Line 1102
android.view.ViewRootImpl.performTraversals in ViewRootImpl.java on Line 1275
android.view.ViewRootImpl.doTraversal in ViewRootImpl.java on Line 1000
android.view.ViewRootImpl$TraversalRunnable.run in ViewRootImpl.java on Line 4218
android.view.Choreographer$CallbackRecord.run in Choreographer.java on Line 725
android.view.Choreographer.doCallbacks in Choreographer.java on Line 555
android.view.Choreographer.doFrame in Choreographer.java on Line 525
android.view.Choreographer$FrameDisplayEventReceiver.run in Choreographer.java on Line 711
android.os.Handler.handleCallback in Handler.java on Line 615
android.os.Handler.dispatchMessage in Handler.java on Line 92
android.os.Looper.loop in Looper.java on Line 137
android.app.ActivityThread.main in ActivityThread.java on Line 4744
java.lang.reflect.Method.invokeNative(Native Method)
java.lang.reflect.Method.invoke in Method.java on Line 511
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run in ZygoteInit.java on Line 786
com.android.internal.os.ZygoteInit.main in ZygoteInit.java on Line 553
dalvik.system.NativeStart.main(Native Method)

首先我在activity的OnResume函数中设置了view pager.

First I set up the view pager in the OnResume function of the activity.

private void initialiseViewPager()
{
    mLoginFragment = new WeakReference<LoginFragment>(new LoginFragment());
    Bundle loginBundle = new Bundle();
    loginBundle.putInt("SpinnerIndex", HDMSLiveSession.getInstance().getSpinnerPosition());
    loginBundle.putString("UserName", HDMSLiveSession.getInstance().getUsername());
    loginBundle.putString("Password", HDMSLiveSession.getInstance().getPassword());
    loginBundle.putInt("Mode", HDMSLiveSession.getInstance().getConnectionMode().ordinal());
    loginBundle.putBoolean("LoggedIn", HDMSLiveSession.getInstance().isLoggedIn());
    loginBundle.putBoolean("Connected", HDMSLiveSession.getInstance().isConnected());
    loginBundle.putString("LoginResult", HDMSLiveSession.getInstance().getLoginResult());
    loginBundle.putString("System", HDMSLiveSession.getInstance().getSystem());
    loginBundle.putInt("code", HDMSLiveSession.getInstance().getAccessCode());
    loginBundle.putLong("bytesSent", mCurrentSB);
    loginBundle.putLong("bytesReceived", mCurrentRB);
    loginBundle.putLong("nbytesSent", mNCurrentSB);
    loginBundle.putLong("nbytesReceived", mNCurrentRB);
    loginBundle.putInt("appid", mApp.getApplicationInfo().uid);
    loginBundle.putString(mWebSocketAddressPreference, mConnect.getWebSocketURL());
    loginBundle.putString(mAPIAddressPreference, mLogin.getLiveServerURL());
    loginBundle.putBoolean(mAutoLoginPreference, mAutoLoginEnabled);
    loginBundle.putBoolean(mAutoConnectPreference, mLogin.isAutoConnectEnabled());
    loginBundle.putInt(mAutoReconnectTimePreference, mConnect.getAutoReconnectTime());
    loginBundle.putInt(mMaxAutoReconnectionAttemptsPreference, mConnect.getMaxAutoReconnectionAttempts());
    loginBundle.putInt(mPingResponseTimePreference, mConnect.getPingResponseTime());
    loginBundle.putInt(mAutoPingTimePreference, mConnect.getAutoPingTime());
    loginBundle.putInt(mCurrentPingPreference, mConnect.getCurrentPing());
    loginBundle.putInt(mAutoReconnectAttemptsPreference, mConnect.getAutoReconnectAttempts());
    loginBundle.putInt(mAutoReconnectTotalAttemptsPreference, mConnect.getAutoReconnectTotalAttempts());
    loginBundle.putBoolean(mPlayListMessagePreference, messageSubscriptionContains(mPlayListMessage));
    loginBundle.putBoolean(mAutoPageSwapPreference, mAutoPageSwap);
    loginBundle.putBoolean(mWifiCheckedPreference, mWifiChecked);
    loginBundle.putBoolean(mAutoWebCheckedPreference, mConnect.isAutoWebChecked());
    loginBundle.putBoolean(mGatewayCheckedPreference, mGatewayChecked);
    loginBundle.putBoolean(mDHS1CheckedPreference, mDNS1Checked);
    loginBundle.putBoolean(mHDMSLiveCheckedPreference, mHDMSLiveChecked);
    loginBundle.putBoolean(mGoogleCheckedPreference, mGoogleChecked);
    loginBundle.putBoolean(mHDMSCheckedPreference, mHDMSChecked);
    loginBundle.putBoolean(mParrotCheckedPreference, mParrotChecked);
    loginBundle.putBoolean(mLocalIPCheckedPreference, mLocalIPChecked);
    loginBundle.putString(mLocalIPPreference, mLocalIP);
    loginBundle.putInt(mMaxImagesFromWebPreference, mMaxImagesFromWeb);
    loginBundle.putInt(mMaxPingAttemptsPreference, mConnect.getMaxPingAttempts());
    loginBundle.putInt(mFailedPingsPreference, mConnect.getFailedPings());
    loginBundle.putBoolean("AutoLogin", mLogin.isAutoLogin());
    loginBundle.putBoolean("wasLoggedIn", HDMSLiveSession.getInstance().wasLoggedIn());
    mLoginFragment.get().setArguments(loginBundle);

    mBAUFragment = new WeakReference<BAUFragment>(new BAUFragment());
    Bundle bauBundle = new Bundle();
    bauBundle.putBoolean("jump", mJumpToCurrent);
    bauBundle.putInt("place", mBAUPosition);
    bauBundle.putBoolean(mBAUExpandedPreference, mBAUExpanded);
    mBAUFragment.get().setArguments(bauBundle);
    mPlayerFragment = new WeakReference<PlayerFragment>(new PlayerFragment());

    mListFragment = new WeakReference<ListFragment>(new ListFragment());
    Bundle listBundle = new Bundle();
    listBundle.putInt(mListModePreference, mListMode);
    mListFragment.get().setArguments(listBundle);

    mSearchFragment = new WeakReference<SearchFragment>(new SearchFragment());
    Bundle searchBundle = new Bundle();
    searchBundle.putInt(mSearchModePreference, mSearchMode);
    searchBundle.putString("searchQueryA", mSearchTextA);
    searchBundle.putString("searchQueryS", mSearchTextS);
    searchBundle.putString("titleLast", mTitleLast);
    searchBundle.putString("artistLast", mArtistLast);
    searchBundle.putString("listLast", mListLast);
    searchBundle.putString("yearLast", mYearLast);
    searchBundle.putString("genreLast", mGenreLast);
    mSearchFragment.get().setArguments(searchBundle);

    mVideoFragment = new WeakReference<VideoFragment>(new VideoFragment());
    Bundle videoBundle = new Bundle();
    videoBundle.putInt(mVideoOutputPreference, mVideoOutput);
    mVideoFragment.get().setArguments(videoBundle);

    List<Fragment> fragments = new Vector<Fragment>();
    fragments.add(mVideoFragment.get());
    fragments.add(mPlayerFragment.get());
    fragments.add(mBAUFragment.get());
    fragments.add(mListFragment.get());
    fragments.add(mSearchFragment.get());
    fragments.add(mLoginFragment.get());
    mPagerAdapter  = new ViewPagerAdapter(getSupportFragmentManager(), fragments);

    mViewPager = (ViewPager)findViewById(R.id.contentViewPager);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setOnPageChangeListener(this);
    mViewPager.setVisibility(View.VISIBLE);

    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    if (am.getMemoryClass() >= 32)
        mViewPager.setOffscreenPageLimit(Fragments.Max.ordinal());

    mLastFragment = -1;
    UpdateDisplay();
}

所有片段都被保留,除非它是具有小内存堆的设备.目前有 6 个碎片,当内存中只有 4 个时发生崩溃.所以我知道片段的数量不是问题.我确实尝试将 View Pagers 设置保留为默认值,唯一的区别是速度,因为应用程序需要在用户滑动时加载片段.当应用暂停时,所有的 Fragment 都会被销毁.

All fragments are retained unless it is a device with a small memory heap. Currently there are 6 fragments, the crash was occuring when there where only 4 in memory. So I know that the number of fragments isn't the problem. I did try leaving the View Pagers settings at the default, the only difference is speed as the app needs to load fragments in when the user swipes. All the fragments are destroyed when the app is paused.

@Override
protected void onSaveInstanceState(Bundle outState)
{
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    if (mLoginFragment.get() != null)
        ft.remove(mLoginFragment.get());
    if (mPlayerFragment.get() != null)
        ft.remove(mPlayerFragment.get());
    if (mBAUFragment.get() != null)
        ft.remove(mBAUFragment.get());
    if (mListFragment.get() != null)
        ft.remove(mListFragment.get());
    if (mSearchFragment.get() != null)
        ft.remove(mSearchFragment.get());
    if (mVideoFragment.get() != null)
        ft.remove(mVideoFragment.get());
    ft.commit();

    mLoginFragment = new WeakReference<LoginFragment>(null);
    mPlayerFragment = new WeakReference<PlayerFragment>(null);
    mBAUFragment = new WeakReference<BAUFragment>(null);
    mListFragment = new WeakReference<ListFragment>(null);
    mSearchFragment = new WeakReference<SearchFragment>(null);
    mVideoFragment = new WeakReference<VideoFragment>(null);
    mPagerAdapter = null;
    mViewPager = null;

    mFragmentsLoaded = 0;
    mLastFragment = -1;
}

这是我的视图寻呼机代码.

Here is my view pager code.

package com.hdms.manager.Fragments;

/**
 * Created by bradj on 8/10/13.
 *
 */
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.view.View;

import java.util.List;

public class ViewPagerAdapter extends FragmentPagerAdapter
{
    private final List<Fragment> mFragments;
    FragmentManager mFragmentManager;

    public ViewPagerAdapter(FragmentManager aFragmentManager, List<Fragment> aFragments)
    {
        super(aFragmentManager);

        mFragmentManager = aFragmentManager;
        mFragments = aFragments;
    }

    @Override
    public Fragment getItem(int aPosition)
    {
        return mFragments.get(aPosition);
    }

    @Override
    public long getItemId(int aPosition)
    {
        return aPosition;
    }

    @Override
    public void destroyItem(android.view.ViewGroup aContainer, int aPosition, java.lang.Object aObject)
    {
        if (aPosition <= getCount() && aObject != null)
        {
            FragmentTransaction trans = mFragmentManager.beginTransaction();
            trans.remove((Fragment) aObject);
            trans.commit();
        }
    }

    @Override
    public int getCount()
    {
        return mFragments.size();
    }
}

推荐答案

终于!我现在能够可靠地重现此错误!

Finally! I'm now able to reliably recreate this error!

要重新创建错误,请关闭活动/应用程序,然后快速重新打开带有片段的页面.您可能需要尝试几次,因为在我的测试中,我必须在大约 30 毫秒内重新打开应用程序.对于不同速度的设备,此时间可能会更慢或更快.

To recreate error, close activity/app, and quickly reopen page with fragment. You may have to try a few times because in my tests I had to reopen the app within about 30ms. This time may be slower or faster for different speed devices.

问题是我只显式地创建了一次片段(使用 new),并保留了对该实例的引用,以便我可以重用它.解决此问题的一个简单方法是始终返回 FragmentPagerAdapter.getItem(...) 的 Fragment 的 new 实例,如下所示.

The problem was that I only explicitly created the Fragment (using new) once, and kept a reference to that instance so that I could reuse it. One simple fix to this problem is to always return a new instance of the Fragment the FragmentPagerAdapter.getItem(...), as shown below.

public class ViewPagerAdapter extends FragmentPagerAdapter {
    ...

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0: return mMyFragment; // Error. Has the edge-case crash.
            case 1: return new MyFragment(); // Works.
            default: return new MyDefaultFragment();
        }
    }
}

对于 OP 的具体情况,使用 List<Fragment> 来保存引用可能与上述问题相同.

For the OP's specific case, using the List<Fragment> to hold references is likely the same problem case as above.

ps - 根本问题可能与 Fragment 生命周期有关,并在它被销毁时尝试再次使用它.

ps - The root problem likely has something to do with the Fragment lifecycle and trying to use it again while it's being destroyed.

pps - 另一种重新创建错误的方法是在足够多的选项卡之间快速切换,以便片段想要被销毁以从缓存中释放一些内存,然后快速返回它.默认情况下,FragmentPagerAdapter 只缓存一个 Fragment 到左"和右".因此,根据您的缓存限制,您必须至少拥有三个选项卡才能以这种方式重新创建错误.

pps - Another way to recreate error is to quickly switch between enough tabs so that the Fragment wants to be destroyed to free some memory from cache, then quickly go back to it. By default, the FragmentPagerAdapter only caches one Fragment to the "left" and "right". So, depending on your cache limit, you will have to have at least three tabs to recreate the error this way.

ppps - 此解决方案修复了 android.app.Fragment.setUserVisibleHint(Fragment.java:997) 的 NullPointerException,并且也适用于 android.support.v4.app.Fragment.setUserVisibleHint.

ppps - This solution fixes the NullPointerException for android.app.Fragment.setUserVisibleHint(Fragment.java:997) and should also work for android.support.v4.app.Fragment.setUserVisibleHint.

这篇关于android.support.v4.app.Fragment.setUserVisibleHint 应用程序恢复时的空指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆