web-dev-qa-db-ja.com

2番目の時間のフラグメントに移動した場合にのみ、Java.IllegalStateExceptionエラー、No Activityが発生する

非常に不可解なバグが発生し、作業を開始する方法すらわかりません。

私は1つのアクティビティを持つ単純なアプリを持っています。ビューはフラグメントで実装されています。フラグメントの1つには内部にViewPagerがあります。そのため、v4サポートライブラリのgetChildFragmentManagerクラスを使用することにしました。また、ActionBarSherlockを使用する必要があり、v4ライブラリのv11には同梱されていないため、問題が発生しました。

ABSのv4サポートライブラリをv11ライブラリに置き換えることでこれを修正しました。ViewPagerを含め、すべてがコンパイルされ、動作しているように見えました。

ここに奇妙な部分があります:

ViewPagerを含むフラグメントが初めて開いたとき、正しく機能します。しかし、2回目に移動すると、アプリがクラッシュし、役に立たないスタックトレースが表示されます。デバッグの結果、問題がgetChildFragmentManagerによって返されたFragmentManagerにあることがわかりました。 No Activityエラーがスローされます。

誰かがこれを引き起こしている可能性がある考えを持っていますか?

あなたが関連すると思うコードを投稿します。

ありがとう、デビッド

35
user1743524

私は jeremyvillalobos answer (これは非常に役に立ちました)のリンクをたどって この回避策 に移動しました。

public class CustomFragment extends Fragment {
    private static final Field sChildFragmentManagerField;

    static {
        Field f = null;
        try {
            f = Fragment.class.getDeclaredField("mChildFragmentManager");
            f.setAccessible(true);
        } catch (NoSuchFieldException e) {
            Log.e(LOGTAG, "Error getting mChildFragmentManager field", e);
        }
        sChildFragmentManagerField = f;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        if (sChildFragmentManagerField != null) {
            try {
                sChildFragmentManagerField.set(this, null);
            } catch (Exception e) {
                Log.e(LOGTAG, "Error setting mChildFragmentManager field", e);
            }
        }
    }

    ...
}

フラグメントを再インスタンス化する必要がなく、私にとってはうまくいきます。

46
lopisan

これは次の場所で報告されたバグのようです

https://code.google.com/p/Android/issues/detail?id=42601

変数

FragmentManagerImpl mChildFragmentManager;

Fragment.Javaでは、デタッチ時にnullに設定されていません。したがって、次回フラグメントが読み込まれるとき、変数はまだ最後の親を指しています。

そのスレッドで説明したように、回避策はフラグメントを再インスタンス化することです。

私の場合、ActionBarタブでフラグメントを切り替えていました。問題のあるフラグメントはフラグメントをネストしており、ファイルローダーフラグメントに戻ったときにアプリをクラッシュさせていました。したがって、これは回避策コードです:

class MainTabsListener implements ActionBar.TabListener {
    public Fragment fragment;
    public int TabPosition;

    public MainTabsListener(Fragment fragment, int tab_position) {
        this.fragment = fragment;
        TabPosition = tab_position;
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        CurrentFragment = fragment;
        CurrentTabSelectedPos = TabPosition;

        /**
         * This is a work-around for Issue 42601
         * https://code.google.com/p/Android/issues/detail?id=42601
         * 
         * The method getChildFragmentManager() does not clear up
         * when the Fragment is detached.
         */
        if( fragment instanceof FileLoaderFragment ){
            fragment = reinstatiateFileLoaderFragment();
        }

        ft.replace(R.id.fragment_container, fragment);

    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(fragment);
    }

}
23

悲しいことに、それはサポートv4のバグですが、まだあります:(

Navigation Drawerなどで他のフラグメントを選択すると、サブフラグメントを持つフラグメントが切り離されます。そのため、これらのサブフラグメントのfragmentManager(getChildFragmentManager())は存在しなくなりました。これらのフラグメントが戻るときにエラーが発生しました。爆弾!

明らかに、サポートv4はonDetach()のmChildFragmentManagerをクリーンアップする必要がありますが、クリーンアップしなかったため、自分自身に依存する必要があります。サブフラグメントを持つフラグメントの次のコードなど:

@Override
    public void onDetach() {
        try {
            Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
            childFragmentManager.setAccessible(true);
            childFragmentManager.set(this, null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        super.onDetach();
    }

すべてが大丈夫です、良い一日を:)

4
Kevin Liu

私は同じ問題を抱えています。

アクティビティでは、transaction.replace(...)でフラグメントを切り替えるために3つのボタンがあります

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);

このフラグメントの1つには、カスタムFragmentPagerAdapterを持つViewPageが含まれています。したがって、以下のネストされたフラグメントに対してgetChildFragmentManager()を実行する必要があります。

コンストラクタはここにあります:

public LignePagerAdapter(Fragment ligneMessageTabletFragment) {
        super(ligneMessageTabletFragment.getChildFragmentManager());
    }

だから私は同じエラーがあります:このフラグメントの最初のショーは動作しませんが、他のフラグメントを表示してこれに戻ると、この例外が発生します:

 02-26 11:57:50.798:D/ACRA(776):トーストの待機+ワーカーが終了しました。アプリケーションを強制終了しますか? true 
 02-26 11:57:50.798:E/AndroidRuntime(776):致命的な例外:main 
 02-26 11:57:50.798:E/AndroidRuntime(776):Java.lang .IllegalStateException:アクティビティなし
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1075)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1070)
 02-26 11:57:50.798:E/AndroidRuntime(776):Android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.Java:1861)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support .v4.app.Fragment.performActivityCreated(Fragment.Java:1474)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager .Java:931)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1088)
 0 2-26 11:57:50.798:E/AndroidRuntime(776):at Android.support.v4.app.BackStackRecord.run(BackStackRecord.Java:682)
 02-26 11:57:50.798:E/AndroidRuntime(776):Android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.Java:1444)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.support .v4.app.FragmentManagerImpl $ 1.run(FragmentManager.Java:429)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.os.Handler.handleCallback(Handler.Java: 587)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.os.Handler.dispatchMessage(Handler.Java:92)
 02-26 11:57: 50.798:E/AndroidRuntime(776):at Android.os.Looper.loop(Looper.Java:132)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Android.app。 ActivityThread.main(ActivityThread.Java:4126)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Java.lang.reflect.Method.invokeNative(Native Method)
 02-26 11:57:50.798:E/AndroidRuntime(776):at Java.lang.reflect.Method.invoke(Me thod.Java:491)
02-26 11:57:50.798:E/AndroidRuntime(776):at com.Android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.Java:844)
 02-26 11:57:50.798:E/AndroidRuntime(776):at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:602)
 02-26 11:57: 50.798:E/AndroidRuntime(776):dalvik.system.NativeStart.main(Native Method)
 02-26 11:57:52.818:I/dalvikvm(776):threadid = 4:シグナル3に反応
 02-26 11:57:52.818:I/dalvikvm(776):スタックトレースを「/data/anr/traces.txt」に書き込みました
 

したがって、フラグメントの同じインスタンスを配置する代わりに、これを再作成して問題を修正できますが、効率が悪いようです。

transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
2
Chtiboss

あなたのエラーはAndroid.view.InflateExceptionですか?

もしそうなら、あなたは動的にフラグメントを膨らませる必要があります、XMLレイアウトを使用しないでください。

また、XMLレイアウトからフラグメントトランザクションに定義されているフラグメントをターゲットにしないでください。

2
dmnlk

@lopisan回答を参照してください:

私は彼の解決策を長い間使用しています。

しかし、私はこれを行うより良い方法があると思います!

MChildFragmentManager.mActivityがnullの場合、mChildFragmentManagerをnullに設定します。 performActivityCreatedメソッドの場合。

@Override
void performActivityCreated(Bundle savedInstanceState) {
    if (getFragmentManagerActivity(mChildFragmentManager) == null) {
        setChildFragmentManager(this, null);
    }
    super.performActivityCreated(savedInstanceState);
}


public static FragmentActivity getFragmentManagerActivity(FragmentManager fragmentManager) {
    FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
    return fm.mActivity;
}



private static final Field sChildFragmentManagerField;
static {
    /**
     * BUG : causing a Java.IllegalStateException error, No Activity, only
     * when navigating to Fragment for the SECOND time
     * http://stackoverflow.com /questions/15207305/getting-the-error-Java-lang-illegalstateexception-activity-has-been-destroyed
     * http://stackoverflow.com/questions/14929907/causing-a-Java-illegalstateexception-error-no-activity-only-when-navigating-to
     */
    Field f = null;
    try {
        f = Fragment.class.getDeclaredField("mChildFragmentManager");
        f.setAccessible(true);
    } catch (NoSuchFieldException e) {
        Log.e(TAG, "Error getting mChildFragmentManager field", e);
    }
    sChildFragmentManagerField = f;
}

public static void setChildFragmentManager(Fragment fragment, FragmentManager fragmentManager) {
    if (sChildFragmentManagerField != null) {
        try {
            sChildFragmentManagerField.set(fragment, fragmentManager);
        } catch (Exception e) {
            Log.e(TAG, "Error setting mChildFragmentManager field", e);
        }
    }
}
0
林奕忠

BottomNavigationViewスイッチを使用して同じバグでスタックし、フラグメントを置き換えるとアプリがクラッシュします。通常の速度では、ユーザーが高速スイッチを実行するとクラッシュしていたと言って、それは完全にうまく機能していました

IllegalStateExceptionアクティビティなし

ログ

私が持っている前に

fragmentA = new FragmentA();
fragmentB = new FragmentB();
fragmentC = new FragmentC();

mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
        switch (item.getItemId()) {
   case R.id.fragmentA:
         replaceFragment(fragmentA);
   case R.id.fragmentB:
        repalceFragment(fragmentB);
   case R.id.fragmentC:
        repalceFragment(fragmentC);
   }
};

このフラグメントの事前作成とそれらを置換に使用することが問題を引き起こしていると思いますが、今度は毎回再作成するようにしました。それはうまく働き始めました

mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
        switch (item.getItemId()) {
   case R.id.fragmentA:
        fragment = new FragmentA();
         replaceFragment(fragment);
   case R.id.fragmentB:
        fragment = new FragmentB();
        repalceFragment(fragment);
   case R.id.fragmentC:
        fragment = new FragmentC();
        repalceFragment(fragment);
   }
};

 public void repalceFragment(Fragment fragment) {
    if (!this.isFinishing()) {
        if (!fragment.isAdded()) {
            if (!this.mDisplayedFragment.isRemoving()) {
                getFragmentManager().beginTransaction().replace(R.id.layout_fragment_container, fragment).commit();
                this.mDisplayedFragment = fragment;
            }
        }
    }
}

一部の人にとって役立つことを願って、防御的なコーディングをやりすぎましたが、フラグメントの高速切り替えの問題を解決しました

0
Sandeep Tengale