รับข้อผิดพลาด“ Java.lang.IllegalStateException Activity ถูกทำลาย” เมื่อใช้แท็บกับ ViewPager


110

ฉันมีแอปพลิเคชันที่ประกอบด้วยการใช้ ActionBarSherlock ในโหมดแท็บฉันมี 5 แท็บและจัดการเนื้อหาของแต่ละแท็บโดยใช้ชิ้นส่วน สำหรับ tab2 ฉันมีแฟรกเมนต์ไฟล์ xml ซึ่งมีองค์ประกอบ ViewPager ซึ่งจะมีเพจแฟรกเมนต์บางเพจ เมื่อฉันเริ่มต้นแอปพลิเคชันแอปพลิเคชันฉันสามารถสลับไปมาระหว่างแท็บได้โดยไม่มีปัญหา แต่เมื่อฉันกด tab2 เป็นครั้งที่สองฉันได้รับข้อผิดพลาดดังกล่าวข้างต้น กิจกรรมหลักมีดังนี้:

public class MainActivity extends SherlockFragmentActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActionBar actionBar = getSupportActionBar();

        ActionBar.Tab tab1 = actionBar.newTab().setText("Tab1");
        ActionBar.Tab tab3 = actionBar.newTab().setText("Tab3");
        ActionBar.Tab tab2 = actionBar.newTab().setText("Tab2");
        ActionBar.Tab tab4 = actionBar.newTab().setText("Tab4");
        ActionBar.Tab tab5 = actionBar.newTab().setText("Tab5");

        Fragment fragment1 = new Tab1();
        Fragment fragment3 = new Tab3();
        Fragment fragment2 = new Tab2();
        Fragment fragment5 = new Tab5();
        Fragment fragment4 = new Tab4();

        tab1.setTabListener(new MyTabListener(fragment1));
        tab3.setTabListener(new MyTabListener(fragment3));
        tab2.setTabListener(new MyTabListener(fragment2));
        tab5.setTabListener(new MyTabListener(fragment5));
        tab4.setTabListener(new MyTabListener(fragment4));

        actionBar.addTab(tab1);
        actionBar.addTab(tab2);
        actionBar.addTab(tab3);
        actionBar.addTab(tab4);
        actionBar.addTab(tab5); 

        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }

    class MyTabListener implements ActionBar.TabListener
    {
        Fragment fragment;

        public MyTabListener(Fragment fragment)
        {
            this.fragment = fragment;
        }

        @Override
        public void onTabSelected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {
            ft.replace(R.id.fragment_container,fragment);
        }

        @Override
        public void onTabUnselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }

        @Override
        public void onTabReselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }
    }
}

คลาสแฟรกเมนต์ที่ไม่มี ViewPager มีดังนี้:

public class Tab1 extends Fragment 
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.activity_tab1, container, false);
    }
}

คลาสแฟรกเมนต์ที่มี ViewPager มีดังต่อไปนี้:

public class Tab2 extends Fragment 
{
    ViewPager mViewPager;
    private MyFragmentPagerAdapter mMyFragmentPagerAdapter;  
    private static int NUMBER_OF_PAGES = 5;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        View view =  inflater.inflate(R.layout.activity_tab2, container, false); 
        return view;
    }

    @Override
    public void onViewCreated(View view,Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());  
        mViewPager.setAdapter(mMyFragmentPagerAdapter);  
    }

    private static class MyFragmentPagerAdapter extends FragmentPagerAdapter 
    {    
        public MyFragmentPagerAdapter(FragmentManager fm) 
        {  
             super(fm);  
        }  

        @Override  
        public Fragment getItem(int index) 
        {  
             return PageFragment.newInstance("My Message " + index);
        }  

        @Override  
        public int getCount() 
        {  
             return NUMBER_OF_PAGES;  
        }  
   }
}

จากสิ่งที่ฉันได้อ่านในสถานที่ต่างๆ (และโปรดแก้ไขฉันหากฉันผิด) สิ่งนี้เกิดขึ้นเนื่องจากตัวจัดการส่วนของบัตรผ่านที่สองพยายามที่จะนำเศษจากกิจกรรมที่ไม่มีอยู่กลับมาใช้ใหม่จึงทำให้เกิดข้อผิดพลาด แต่ฉันไม่แน่ใจว่าทำไมสิ่งนี้ถึงเกิดขึ้นที่นี่เนื่องจากฉันไม่ได้ใช้กิจกรรมการแยกส่วน ตาม logcat ข้อผิดพลาดอยู่ในคลาส Tab2 ซึ่งเป็นเมธอด onViewCreated ในบรรทัดที่ระบุว่า mViewPager.setAdapter (mMyFragmentPagerAdapter) ความช่วยเหลือใด ๆ ที่ชื่นชมอย่างมาก ... ขอบคุณ

03-04 12:01:05.468: E/AndroidRuntime(2474): FATAL EXCEPTION: main
03-04 12:01:05.468: E/AndroidRuntime(2474): java.lang.IllegalStateException: Activity has been destroyed
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1342)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:578)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:433)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.example.tabs.Tab2.onViewCreated(Tab2.java:31)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:925)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.handleCallback(Handler.java:587)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.dispatchMessage(Handler.java:92)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Looper.loop(Looper.java:123)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.app.ActivityThread.main(ActivityThread.java:3687)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invokeNative(Native Method)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invoke(Method.java:507)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at dalvik.system.NativeStart.main(Native Method)

ดังนั้นฉันคิดว่าฉันอาจพบปัญหา เมื่อดูตัวแปร mMyFragmentPagerAdapter (คลาส Tab2) ผ่านดีบักเกอร์ eclipse ฉันเห็นว่ามันมีตัวแปร FragmentManager ซึ่งเมื่อคลิกที่ Tab2 เป็นครั้งแรกจะมีฟิลด์ที่เรียกว่า mActivity ซึ่งชี้ไปที่ MainActivity แต่เมื่อเปลี่ยนจาก tab2 เป็นบางตัว แท็บอื่นและดูที่ mActivity อีกครั้งมันมีค่าเป็นโมฆะซึ่งอาจอธิบายได้ว่าเหตุใดจึงทำให้เกิดข้อผิดพลาดกิจกรรมจึงถูกทำลาย
Yulric Sequeira

คำตอบ:


284

สิ่งนี้ดูเหมือนจะเป็นข้อบกพร่องในการสนับสนุนที่เพิ่มใหม่สำหรับชิ้นส่วนที่ซ้อนกัน โดยพื้นฐานแล้วเด็กFragmentManagerจะมีสภาพภายในที่แตกเมื่อไม่ได้อยู่ในกิจกรรม วิธีแก้ปัญหาระยะสั้นที่แก้ไขให้ฉันคือการเพิ่มสิ่งต่อไปนี้ในonDetach()ทุกFragmentสิ่งที่คุณเรียกร้องgetChildFragmentManager():

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

    try {
        Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
        childFragmentManager.setAccessible(true);
        childFragmentManager.set(this, null);

    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

21
หากคุณดูการใช้งาน Fragment คุณจะเห็นว่าเมื่อย้ายไปยังสถานะแยกออกมันจะรีเซ็ตสถานะภายใน อย่างไรก็ตามจะไม่รีเซ็ต mChildFragmentManager (นี่คือข้อบกพร่องในไลบรารีการสนับสนุนเวอร์ชันปัจจุบัน) สิ่งนี้ทำให้ไม่สามารถแนบตัวจัดการส่วนย่อยอีกครั้งเมื่อมีการแนบ Fragment อีกครั้งทำให้เกิดข้อยกเว้นที่คุณเห็น
Marcus Forsell Stahre

14
คุณต้องล้อเล่นฉัน ดีใจที่คุณพบโพสต์สิ่งนี้มี แต่ความเศร้าโศก
secureboot

8
ข้อบกพร่องนี้กำลังถูกติดตามในเครื่องมือติดตามปัญหาของ Android Open Source: code.google.com/p/android/issues/detail?id=42601
Kristopher Johnson

3
เกลือเล็กน้อย แต่ล้มเหลวในการใช้เวอร์ชัน support-v4 หรือเวอร์ชัน android.app ดังนั้นข้อบกพร่องไม่เพียง แต่เกิดขึ้นในไลบรารีการสนับสนุน
gbero

6
ดูเหมือนว่าได้รับการแก้ไขแล้วในไลบรารีสนับสนุนเวอร์ชัน 24.0.0 วิธีการ 'performDetach ()' ของ 'android.support.v4.app.Fragment' ทำ 'mChildFragmentManager = null;'
Emerson Dallagnol

13

ฉันมีปัญหาเดียวกัน วิธีแก้ปัญหาเดียวที่ฉันพบคือการแทนที่ส่วนย่อยด้วยอินสแตนซ์ใหม่ทุกครั้งที่มีการเปลี่ยนแปลงแท็บ

ft.replace(R.id.fragment_container, Fragment.instantiate(PlayerMainActivity.this, fragment.getClass().getName()));

ไม่ใช่วิธีแก้ปัญหาที่แท้จริง แต่ฉันไม่พบวิธีนำอินสแตนซ์แฟรกเมนต์ก่อนหน้านี้กลับมาใช้ใหม่ ...


1
ว้าวขอบคุณมาก ... ที่แก้ไขปัญหาเป็นไปได้ไหมที่จะให้คำอธิบายว่าทำไมถึงใช้งานได้ฉันหัวแตกพยายามแก้ไขข้อผิดพลาดโดยใช้ FragmentManager
Yulric Sequeira

1
คุณวิเคราะห์รอยความจำของคุณแล้วหรือยัง? เนื่องจากวิธีแก้ปัญหานี้อาจเพิ่มขึ้นอย่างน้อยฉันก็สงสัย
nmxprime

ไม่รู้ว่าทำไมถึงใช้งานได้.. ไม่รู้ว่ามันทำงานยังไง แต่มันใช้งานได้ lol

7

ฉันพบปัญหาเดียวกันเมื่อโทรไปsuper.onCreate()ที่ส่วนท้ายของวิธีการของฉัน เหตุผล: attachActivity()ถูกเรียกใน onCreate () ของ FragmentActivity เมื่อลบล้างonCreate()และตัวอย่างเช่นการสร้างแท็บตัวจัดการแท็บจะพยายามเปลี่ยนไปใช้แฟรกเมนต์ในขณะที่ไม่มีกิจกรรมที่แนบมากับ FragmentManager

วิธีง่ายๆ: ย้ายสายไปที่ super.onCreate()ที่ส่วนหัวของตัวฟังก์ชัน

โดยทั่วไปดูเหมือนว่ามีสาเหตุหลายประการที่อาจทำให้ปัญหานี้เกิดขึ้น นี่เป็นเพียงอีกหนึ่ง ...

มัทธีอัส


ขอบคุณมากคุณช่วยวันของฉัน! ฉันมีปัญหาเดียวกันและยังไม่สามารถแก้ไขได้หลังจากผ่านไปหนึ่งสัปดาห์ (ฉันดีใจมากที่คุณเขียนคำตอบนี้ !!!
JonasPTFL

4

ต้องการเพิ่มว่าปัญหาของฉันอยู่ในกิจกรรมที่ฉันพยายามสร้างFragmentTransactionใน onCreate ก่อนที่ฉันจะโทรsuper.onCreate()ไป ฉันเพิ่งย้ายsuper.onCreate()ไปที่ด้านบนสุดของฟังก์ชันและทำงานได้ดี


2

ฉันมีข้อผิดพลาดนี้เนื่องจากฉันใช้ LocalBroadcastManager และฉันทำ:

unregisterReceiver(intentReloadFragmentReceiver);

แทน:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);

1
นี่ไม่ได้แก้ปัญหาของฉัน แต่ขอบคุณที่ช่วยแก้ไขข้อผิดพลาดในการใช้ BroadcastReceivers
nmxprime

จะเกิดอะไรขึ้นถ้าบริบทกิจกรรมของคุณเป็นโมฆะ ดังนั้น 'สิ่งนี้' จะไม่ถูกต้อง
IgorGanapolsky

เมื่อบริบทของกิจกรรมสามารถเป็นโมฆะ ฉันคิดว่าไม่เคย
Malachiasz

2

ฉันพบปัญหาเดียวกันและพบในภายหลังว่าฉันไม่ได้รับสายsuper.onCreate( savedInstanceState );ในonCreate()FragmentActivity


1

ฉันได้รับข้อผิดพลาดเดียวกันนี้เมื่อพยายามเข้าถึงเด็กFragmentManagerก่อนที่ส่วนย่อยจะเริ่มต้นอย่างสมบูรณ์ (เช่นแนบกับกิจกรรมหรืออย่างน้อยก็onCreateView()เรียกว่า) มิฉะนั้นการFragmentManagerเริ่มต้นด้วยnullกิจกรรมที่ทำให้เกิดข้อยกเว้นดังกล่าว


1

ฉันบังคับให้ส่วนย่อยที่มีส่วนย่อยเป็น NULL ใน onPause และมันจะแก้ไขปัญหาของฉัน

fragment = null;

1

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่คำตอบที่แนะนำไม่ได้ผลในตอนท้ายของฉัน ขอฝากไว้ที่นี่เผื่อว่าจะมีคนเห็นว่ามีประโยชน์

สิ่งที่ฉันทำคือ:

@Override
public void onResume() {
    super.onResume();
    // add all fragments
    FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
    for(Fragment fragment : fragmentPages){
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.add(R.id.contentPanel, fragment, tag);
        if(fragmentPages.indexOf(fragment) != currentPosition){
            fragmentTransaction.hide(fragment);
        } else {
            lastTag = tag;
        }
    }
    fragmentTransaction.commit();
}

จากนั้นใน:

@Override
public void onPause() {
    super.onPause();
    // remove all attached fragments
    for(Fragment fragment: fragmentPages){
        getChildFragmentManager().beginTransaction().remove(fragment).commit();
    }
}

0

ฉันมีปัญหานี้และไม่พบวิธีแก้ปัญหาที่นี่ดังนั้นฉันจึงต้องการแบ่งปันวิธีแก้ปัญหาของฉันในกรณีที่มีคนอื่นพบปัญหานี้อีกครั้ง

ฉันมีรหัสนี้:

public void finishAction() {
  onDestroy();
  finish();
}

และแก้ไขปัญหาโดยการลบบรรทัด "onDestroy ();"

public void finishAction() {
  finish();
}

เหตุผลที่ฉันเขียนโค้ดเริ่มต้น: ฉันรู้ว่าเมื่อคุณดำเนินการ "เสร็จสิ้น ()" กิจกรรมจะเรียก "onDestroy ()" แต่ฉันใช้เธรดและต้องการให้แน่ใจว่าเธรดทั้งหมดถูกทำลายก่อนที่จะเริ่มกิจกรรมถัดไป และดูเหมือนว่า "เสร็จสิ้น ()" จะไม่เกิดขึ้นทันที ฉันต้องการประมวลผล / ลด "บิตแมป" จำนวนมากและแสดง "บิตแมป" ขนาดใหญ่และฉันกำลังปรับปรุงการใช้หน่วยความจำในแอปของฉัน

ตอนนี้ฉันจะฆ่าเธรดโดยใช้วิธีการอื่นและฉันจะใช้วิธีนี้จาก "onDestroy ();" และเมื่อฉันคิดว่าฉันต้องฆ่าทุกหัวข้อ

public void finishAction() {
  onDestroyThreads();
  finish();
}

0

ผมมีปัญหานี้และรู้ว่ามันเป็นเพราะผมเรียกsetContentView(int id)สองครั้งในของฉันActivity'sonCreate


0

อันนี้ทำให้ฉันคลั่งไคล้ Xamarin

ฉันพบสิ่งนี้ด้วยการใช้งาน ViewPager สำหรับ TabLayout WITHIN Fragment ซึ่งถูกนำไปใช้ใน DrawerLayout:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

ดังนั้นคุณต้องใช้รหัสต่อไปนี้ในของคุณDrawerFragment ระวังการเลือก FragmentManager-Path ที่ถูกต้อง เนื่องจากคุณอาจมีการอ้างอิง FragmentManager สองแบบ:

  1. Android.Support.V4.App.FragmentManager
  2. Android.App.FragmentManager

-> เลือกอันที่คุณใช้ หากคุณต้องการใช้ ChildFragmentManager คุณต้องใช้การประกาศคลาสAndroid.App.FragmentManagerสำหรับ ViewPager ของคุณ!

Android.Support.V4.App.FragmentManager

ใช้วิธีการต่อไปนี้ในส่วน "หลัก" ของคุณ - ในตัวอย่างนี้: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/support/v4/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

Android.App.FragmentManager

public class TabViewPager: Android.Support.V13.App.FragmentPagerAdapter {}

นั่นหมายความว่าคุณต้องเริ่ม ViewPager ด้วย Android.App.FragmentManager

ใช้วิธีการต่อไปนี้ในส่วน "หลัก" ของคุณ - ในตัวอย่างนี้: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

0

ข้อบกพร่องได้รับการแก้ไขแล้วใน androidx เวอร์ชันล่าสุด และวิธีแก้ปัญหาที่มีชื่อเสียงจะทำให้เกิดความผิดพลาดในขณะนี้ ดังนั้นเราไม่จำเป็นต้องใช้ตอนนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.