nhận ngoại lệ, IllegalStateException: không thể thực hiện hành động này sau khi onSaveInstanceState tựa


355

Tôi có một ứng dụng Live Android và từ thị trường tôi đã nhận được theo dõi stack và tôi không biết tại sao nó lại xảy ra vì nó không xảy ra trong mã ứng dụng nhưng nó bị gây ra bởi một số hoặc sự kiện khác từ ứng dụng (giả định)

Tôi không sử dụng Fragment, vẫn có một tài liệu tham khảo về FragmentManager. Nếu bất kỳ cơ quan nào có thể đưa ra một số ánh sáng trên một số sự kiện ẩn để tránh loại vấn đề này:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  

Bạn đã tìm thấy một giải pháp chưa? Có cùng một vấn đề ở đây: stackoverflow.com/questions/7575921/ từ
nhaarman


2
@phlebas Không có bạn. Các mối quan tâm của bạn đối thoại, và điều này không. Dòng trên cùng của kết hợp theo dõi ngăn xếp của bạn là không đủ. Phần còn lại rất khác nhau. Tôi nói điều này bởi vì tôi vừa mới xem xét vấn đề của bạn và thật không may cho tôi.
themightyjon

Bạn có sử dụng một Chủ đề hoặc AsynTask trong hoạt động đó không?
Jose Castro

21
Tôi thảo luận về lỗi này trong bài viết trên blog của tôi ... bạn nên đọc nó. :)
Alex Lockwood

Câu trả lời:


455

Đây là lỗi ngu ngốc nhất mà tôi gặp phải cho đến nay. Tôi đã có một Fragmentứng dụng hoạt động hoàn hảo cho API <11Force Closingtrên API> 11 .

Tôi thực sự không thể tìm ra những gì họ đã thay đổi trong Activityvòng đời trong cuộc gọi đến saveInstance, nhưng tôi ở đây là cách tôi giải quyết điều này:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

Tôi chỉ không thực hiện cuộc gọi đến .super()và mọi thứ hoạt động tốt. Tôi hy vọng điều này sẽ giúp bạn tiết kiệm thời gian.

EDIT: sau một số nghiên cứu thêm, đây là một lỗi đã biết trong gói hỗ trợ.

Nếu bạn cần lưu ví dụ và thêm một cái gì đó vào outState Bundle bạn có thể sử dụng như sau:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2: điều này cũng có thể xảy ra nếu bạn đang cố gắng thực hiện một giao dịch sau khi bạn Activityđã ở chế độ nền. Để tránh điều này bạn nên sử dụngcommitAllowingStateLoss()

EDIT3: Các giải pháp trên đã khắc phục các sự cố trong các thư viện support.v4 ban đầu từ những gì tôi có thể nhớ. Nhưng nếu bạn vẫn có vấn đề với điều này thì bạn PHẢI đọc blog của @AlexLockwood : Giao dịch mảnh và mất trạng thái hoạt động

Tóm tắt từ bài đăng trên blog (nhưng tôi thực sự khuyên bạn nên đọc nó):

  • KHÔNG BAO GIỜ commit() giao dịch sauonPause() trên Honeycomb và onStop()sau Honeycomb
  • Hãy cẩn thận khi thực hiện các giao dịch bên trong Activitycác phương thức vòng đời. Sử dụng onCreate() , onResumeFragments()onPostResume()
  • Tránh thực hiện các giao dịch bên trong các phương thức gọi lại không đồng bộ
  • commitAllowingStateLoss()Chỉ sử dụng như là phương sách cuối cùng

97
Bạn nên sử dụng commit ALLowingStateLoss () thay vì commit ()
meh

7
Vì vậy, việc không gọi super in onSaveInstanceState sẽ ngăn FragmentManager có thể lưu trạng thái của tất cả các mảnh và khôi phục chúng. Điều đó có thể gây ra vấn đề với xoay vòng. Ngoài ra, tôi vừa thử một thứ khác về việc bỏ rác vào gói và điều đó không có gì khác biệt đối với tôi. Không chắc nó sẽ như thế nào - lỗi mà bạn tham chiếu trong gói hỗ trợ là NullPulumException và dường như không giống như IllegalStateException này ...
themightyjon

56
@meh commitAllowingStateLoss()chỉ tránh ngoại lệ. Nó không bảo vệ ứng dụng của bạn khỏi mất trạng thái tình cờ. Xem bài đăng trên blog này .
Alex Lockwood

2
@AlexLockwood vì vậy từ bài đăng trên blog đó, chúng ta có thể biết rằng chúng ta nên thực hiện tất cả các cuộc gọi mạng bên trong đoạn (và hiển thị một số tiến trình tạm thời nếu cần) và đó là cách duy nhất chúng ta có thể tránh ngoại lệ này khi có thể xảy ra vì cam kết đang được gọi sau một số cuộc gọi phương thức không đồng bộ.
meh

1
Tôi không nhận được điểm đầu tiên: "KHÔNG BAO GIỜ cam kết () giao dịch sau ... onStop () trên post-Honeycomb". Điều gì xảy ra nếu tôi cần một nút để kích hoạt một đoạn được thay thế bằng một đoạn khác? Tôi có nên đặt một boolean để kiểm tra xem hoạt động đã kết thúc trênStop chưa và nếu có, hãy gọi commit ALLowingStateLoss thay thế? Ngoài ra, nếu tôi có một mảnh trong một mảnh, mà tôi cần thay thế khi nhấp vào nút thì sao?
nhà phát triển Android

76

Tìm kiếm trong mã nguồn Android về nguyên nhân gây ra sự cố này cho thấy cờ mStateSatted trong FragmentManagerImpllớp (ví dụ có sẵn trong Hoạt động) có giá trị đúng. Nó được đặt thành true khi ngăn xếp phía sau được lưu (saveAllState) trong cuộc gọi từ Activity#onSaveInstanceState. Sau đó, các cuộc gọi từ ActivityThread không đặt lại cờ này bằng các phương thức đặt lại có sẵn từ FragmentManagerImpl#noteStateNotSaved()dispatch() .

Cách tôi nhìn thấy nó có một số bản sửa lỗi có sẵn, tùy thuộc vào ứng dụng của bạn đang làm gì và sử dụng:

Cách tốt

Trước bất cứ điều gì khác: tôi sẽ quảng cáo bài viết của Alex Lockwood . Sau đó, từ những gì tôi đã làm cho đến nay:

  1. Đối với các phân đoạn và hoạt động không cần giữ bất kỳ thông tin trạng thái nào, hãy gọi commit ALLowStateLoss . Lấy từ tài liệu:

    Cho phép cam kết được thực thi sau khi trạng thái của một hoạt động được lưu. Điều này rất nguy hiểm vì cam kết có thể bị mất nếu sau đó hoạt động cần được khôi phục từ trạng thái của nó, do đó, điều này chỉ nên được sử dụng cho các trường hợp trạng thái UI thay đổi bất ngờ đối với người dùng. Tôi đoán điều này là ổn để sử dụng nếu đoạn đang hiển thị thông tin chỉ đọc. Hoặc ngay cả khi chúng hiển thị thông tin có thể chỉnh sửa, hãy sử dụng các phương thức gọi lại để giữ lại thông tin đã chỉnh sửa.

  2. Ngay sau khi giao dịch được cam kết (bạn vừa gọi commit()), hãy gọi đến FragmentManager.executePendingTransactions().

Những cách không được đề xuất:

  1. Như Ovidiu Latcu đã đề cập ở trên, đừng gọi super.onSaveInstanceState(). Nhưng điều này có nghĩa là bạn sẽ mất toàn bộ trạng thái hoạt động của mình cùng với trạng thái phân mảnh.

  2. Ghi đè onBackPressedvà trong đó chỉ gọi finish(). Điều này sẽ ổn nếu ứng dụng của bạn không sử dụng API Fragment; như trong super.onBackPressedđó có một cuộc gọi đến FragmentManager#popBackStackImmediate().

  3. Nếu bạn đang sử dụng cả API Fragment và trạng thái hoạt động của bạn là quan trọng / quan trọng, thì bạn có thể thử gọi bằng API phản chiếu FragmentManagerImpl#noteStateNotSaved(). Nhưng đây là một hack, hoặc người ta có thể nói đó là một cách giải quyết. Tôi không thích nó, nhưng trong trường hợp của tôi, nó hoàn toàn có thể chấp nhận được vì tôi có một mã từ một ứng dụng cũ sử dụng mã không dùng nữa ( TabActivityvà ngầm LocalActivityManager).

Dưới đây là mã sử dụng sự phản chiếu:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

Chúc mừng!


Điều này dường như xảy ra với ActionBarSherlock quá, dưới Gingerbread, vì vậy các trường hợp kiểm tra xây dựng Id dường như tranh luận ... :(
t0mm13b

Ngoài ra, bạn cũng nên chỉ ra - điều đó cũng không phù hợp với việc sử dụng ABS :)
t0mm13b

@ t0mm13b: Đoạn mã trên là viết tắt của dự án của tôi vì nó không sử dụng cả các đoạn, cũng không hỗ trợ.FragmentActivity. Nó chạy trên android.app.Activity và vì sự không nhất quán kích hoạt Ngoại lệ là do FragmentManager (API cấp 11 trở lên), đó là lý do tại sao kiểm tra ... Nếu bạn tin rằng nguồn gốc của tội ác cũng giống bạn để loại bỏ kiểm tra. ABS là một câu chuyện khác nhau khi nó chạy trên gói tương thích và việc triển khai hỗ trợ.FragmentActivity có thể sử dụng cùng một triển khai của FragmentManager và voila: cùng một vấn đề.
súng

@ t0mm13b: để thêm hơn 600 ký tự là không đủ, trước tiên bạn phải điều tra xem điều gì thực sự gây ra điều này cho bạn. Bạn cũng phải nhận ra rằng ở trên là một hack xấu xí và tôi không chịu bất kỳ trách nhiệm nào nếu nó chạy hay không (đối với tôi là giải pháp tốt nhất khi xem xét các trường hợp). Nếu bạn phải sử dụng nó, hãy kiểm tra kỹ mã nguồn compat để đặt tên biến vì chúng có thể khác với gói tiêu chuẩn. Tôi hy vọng vấn đề này sẽ được giải quyết bằng các phiên bản tiếp theo của gói tương thích, nhưng từ trải nghiệm Android, có rất ít cơ hội xảy ra ...
gunar

Uhhh ... Đây chính xác là vấn đề tương tự như trong báo cáo lỗi này , đó là điểm của câu hỏi này của OP. Tôi đứng trước bình luận của tôi - bạn nên từ chối một cách rõ ràng từ chối trách nhiệm và nói rằng nó không được bảo đảm và cũng nên tuyên bố rằng bạn không sử dụng các đoạn - nếu không thì tại sao phải đăng câu trả lời đó! :) Chỉ cần nói ...
t0mm13b

35

Một ngoại lệ như vậy sẽ xảy ra nếu bạn cố gắng thực hiện chuyển tiếp đoạn sau hoạt động của đoạn onSaveInstanceState() được gọi.

Một lý do điều này có thể xảy ra, là nếu bạn để lại một AsyncTask(hoặcThread ) chạy khi một hoạt động bị dừng lại.

Bất kỳ chuyển đổi nào sau khi onSaveInstanceState()được gọi đều có khả năng bị mất nếu hệ thống lấy lại hoạt động cho tài nguyên và tạo lại sau đó.


2
Xin chào Funk, tôi có một câu hỏi ở đây, tại sao onBackPression có thể được gọi cho hoạt động hoặc đoạn đó, nếu hoạt động hoặc đoạn đó đã bị dừng. Ngoại lệ trên dường như được tạo ra từ một số sự kiện UI (tức là nhấn phím BACK), tôi không thể tìm ra mối quan hệ giữa tác vụ Async và phím Back.
dcool

Vì bạn có thể lưu chuyển tiếp đoạn sang trạng thái quay lại, nhấn nút quay lại có thể gây ra sự đảo ngược của quá trình chuyển đổi mà bạn lưu (để các đoạn cũ quay trở lại). onSaveInstanceState được gọi trước khi hoạt động của bạn bị hủy để khôi phục tài nguyên cho hệ thống, không phải luôn luôn sau khi onStop được gọi. Xin lỗi, điều đó không rõ ràng trong câu trả lời của tôi.
FunkTheMonk

Funk, nhưng tôi không sử dụng bất kỳ Fragment nào trong ứng dụng của mình. Nó có thể là các đoạn được sử dụng trong mã gốc. trước đó tôi nghĩ bạn đang nói về điều tương tự
dcool

1
Tôi đã có một AsyncTask với một tham chiếu đến một đoạn. Sự cố đã được giải quyết sau khi xóa cuộc gọi super () khỏi onSaveInstanceState và thay thế tham chiếu từ AsyncTask của tôi bằng WeakReference <Fragment>.
Trâu

2
@Buffalo Đó chắc chắn không phải là một giải pháp cho vấn đề. Bạn nên gọi luôn super.onSaveInstanceState().
Alex Lockwood

27

Chỉ cần gọi super.onPostResume () trước khi hiển thị đoạn của bạn hoặc di chuyển mã của bạn trong phương thức onPostResume () sau khi gọi super.onPostResume (). Điều này giải quyết vấn đề!


6
Gọi onPostResume () đảm bảo rằng onResumeFragments () được gọi và với tôi đây là giải pháp lý tưởng.
j2emanue

20

Điều này cũng có thể xảy ra khi gọi dismiss()một đoạn hộp thoại sau khi màn hình bị khóa \ blanks và trạng thái thể hiện của hộp thoại Activity + đã được lưu. Để giải quyết cuộc gọi này:

dismissAllowingStateLoss()

Theo nghĩa đen, mỗi lần tôi bỏ qua một hộp thoại, tôi không quan tâm đến trạng thái của nó nữa, vì vậy điều này là ổn để làm - bạn thực sự không mất bất kỳ trạng thái nào.


2
Đây là vấn đề chính xác của tôi! Thưa ngài thật tài giỏi!
Tash Pemhiwa

17

Giải pháp ngắn gọn và hiệu quả:

Thực hiện theo các bước đơn giản:

Bước 1 : Ghi đè trạng thái onSaveInstanceState trong đoạn tương ứng. Và loại bỏ siêu phương pháp từ nó.

@Override
public void onSaveInstanceState(Bundle outState) {
};

Bước 2 : Sử dụng Commit ALLowingStateLoss (); thay vì cam kết (); trong khi hoạt động phân mảnh.

fragmentTransaction.commitAllowingStateLoss();

1
Loại bỏ các phương pháp siêu đã làm thủ thuật, bạn có thể giải thích tại sao mặc dù? Có an toàn để loại bỏ điều đó?
Bruce

7
Sẽ không an toàn khi xóa super (), bạn sẽ mất dữ liệu khác sau đó!
cá chết


7

điều này làm việc cho tôi ... tìm thấy điều này một mình ... hy vọng nó sẽ giúp bạn!

1) KHÔNG có FragmentManager / FragmentTransaction "tĩnh" toàn cầu.

2) onCreate, LUÔN LUÔN khởi tạo lại FragmentManager!

mẫu dưới đây: -

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}

6

Tôi đã luôn nhận được điều này khi tôi cố gắng hiển thị đoạn trong phương thức onActivityForResult (), vì vậy vấn đề tiếp theo là:

  1. Hoạt động của tôi bị tạm dừng và dừng lại, điều đó có nghĩa là onSaveInstanceState () đã được gọi (đối với cả thiết bị trước Honeycomb và sau Honeycomb).
  2. Trong trường hợp có bất kỳ kết quả nào, tôi đã thực hiện giao dịch để hiển thị / ẩn đoạn, điều này gây ra IllegalStateException này.

Những gì tôi làm là tiếp theo:

  1. Giá trị gia tăng để xác định xem hành động tôi muốn đã được thực hiện chưa (ví dụ: chụp ảnh từ camere - isPhotoTaken) - đó có thể là giá trị boolean hoặc số nguyên tùy thuộc vào số lượng giao dịch bạn cần.
  2. Trong phương thức overriden onResumeFragments () tôi đã kiểm tra giá trị của mình và sau khi thực hiện các giao dịch phân đoạn tôi cần. Trong trường hợp này, commit () không được thực hiện sau onSaveInstanceState, vì trạng thái được trả về trong phương thức onResumeFragments ().

5

Tôi đã giải quyết vấn đề với onconfigurationchanged. Bí quyết là theo vòng đời hoạt động của Android, khi bạn gọi một cách rõ ràng một ý định (ý định máy ảnh, hoặc bất kỳ mục đích nào khác); hoạt động bị tạm dừng và onsattedInstance được gọi trong trường hợp đó. Khi xoay thiết bị đến một vị trí khác ngoài vị trí mà hoạt động đang hoạt động; thực hiện các hoạt động phân đoạn như cam kết phân mảnh gây ra ngoại lệ bất hợp pháp. Có rất nhiều phàn nàn về nó. Đó là một cái gì đó về quản lý vòng đời hoạt động Android và các cuộc gọi phương thức thích hợp. Để giải quyết vấn đề này, tôi đã làm điều này: 1-Ghi đè phương thức onsattedInstance của hoạt động của bạn và xác định hướng màn hình hiện tại (dọc hoặc ngang) sau đó đặt hướng màn hình của bạn thành trước khi hoạt động của bạn bị tạm dừng. bằng cách đó, hoạt động bạn khóa xoay màn hình cho hoạt động của bạn trong trường hợp nó bị xoay bởi hoạt động khác. 2-sau đó, ghi đè phương thức hoạt động onresume và đặt chế độ định hướng của bạn ngay bây giờ thành cảm biến để sau khi gọi phương thức tiện, nó sẽ gọi thêm một lần cấu hình để xử lý xoay vòng đúng cách.

Bạn có thể sao chép / dán mã này vào hoạt động của mình để đối phó với nó:

@Override
protected void onSaveInstanceState(Bundle outState) {       
    super.onSaveInstanceState(outState);

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 

4

Tôi gặp vấn đề tương tự, nhận IllegalStateException, nhưng thay thế tất cả các cuộc gọi của tôi để cam kết () bằng commit ALLowingStateLoss () không giúp ích được gì.

Thủ phạm là một cuộc gọi đến DialogFragment.show ().

Tôi bao quanh nó với

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

và điều đó đã làm nó. OK, tôi không được hiển thị hộp thoại, nhưng trong trường hợp này thì ổn.

Đó là nơi duy nhất trong ứng dụng của tôi, nơi lần đầu tiên tôi gọi là FragmentManager.beginTransaction () nhưng không bao giờ được gọi là commit () vì vậy tôi đã không tìm thấy nó khi tôi tìm "commit ()".

Điều buồn cười là, người dùng không bao giờ rời khỏi ứng dụng. Thay vào đó, kẻ giết người là một quảng cáo xen kẽ AdMob xuất hiện.


3
Tương tự ở đây. Tôi đã giải quyết Ghi đè phương thức 'hiển thị (Trình quản lý FragmentManager, thẻ Chuỗi)', thay thế 'cam kết' bằng 'commit ALLowingStateLoss'; mất thứ gì đó vì tôi không thể đặt hai thuộc tính riêng của Hộp thoại: mDismissed và mShownByMe. Nhưng nó dường như hoạt động mọi lúc :)
Francesco Ditrani

Tôi đã thực hiện một giải pháp thay thế cho DialogFragment, có thể tránh ngoại lệ này: github.com/AndroidDeveloperLB/DialogShard
nhà phát triển Android

4

Giải pháp của tôi cho vấn đề đó là

Trong các phương thức thêm đoạn:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

Có thể là xấu, nhưng không thể tìm thấy bất cứ điều gì tốt hơn.


Trues .. bắt ngoại lệ có thể tránh sự cố ứng dụng, nhưng các vấn đề về hành vi như vậy bị bỏ lại trên màn hình hoặc không được thêm.
Marcos Vasconcelos

4
tiếp tục di chuyển sự thật là ngoài kia
Anil

4

Tôi đã gặp vấn đề này. Nhưng tôi nghĩ vấn đề này không liên quan đến cam kết và commit ALLowStateLoss.

Theo dõi ngăn xếp và thông báo ngoại lệ sau đây là về commit ().

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Nhưng ngoại lệ này là do onBackPression ()

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

Tất cả đều do checkStateLoss () gây ra

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mStateSatted sẽ đúng sau khi onSaveInstanceState.

Vấn đề này hiếm khi xảy ra. Tôi chưa bao giờ gặp phải vấn đề này. Tôi không thể nhắc lại vấn đề.

Tôi tìm thấy vấn đề 25517

Nó có thể đã xảy ra trong các trường hợp sau đây

  1. Phím quay lại được gọi sau onSaveInstanceState, nhưng trước khi hoạt động mới được bắt đầu.

  2. sử dụng onStop () trong mã

Tôi không chắc gốc rễ của vấn đề là gì. Vì vậy, tôi đã sử dụng một cách xấu xí.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}

Tôi đã không thực sự giải quyết vấn đề, nhưng vấn đề này không liên quan đến cam kết và commit ALLowStateLoss.
oO_ox

4

Tôi đã có cùng một vấn đề trong ứng dụng của tôi. Tôi đã được giải quyết vấn đề này chỉ bằng cách gọi super.onBackPressed();lớp trước và gọi commitAllowingStateLoss()lớp hiện tại với đoạn đó.


2
Cảm ơn bạn. Giải pháp này đã giải quyết vấn đề uisng commitAllowingStateLoss()thay vìcommit()
Chintak Patel

Tránh sử dụng commit ALLowingStateLoss () Medium.com/@elye.project/ từ
swooby

3

onSaveInstance sẽ được gọi nếu người dùng xoay màn hình để nó có thể tải tài nguyên được liên kết với hướng mới.

Có thể người dùng này đã xoay màn hình theo sau bằng cách nhấn nút quay lại (vì cũng có thể người dùng này đã dò dẫm điện thoại của họ trong khi sử dụng ứng dụng của bạn)


2
Mặc dù thay đổi cấu hình (chẳng hạn như thay đổi hướng) có thể dẫn đến ngoại lệ này, nhưng chúng không phải là nguyên nhân gốc.
Alex Lockwood


2

Vấn đề tương tự từ tôi và sau một ngày dài phân tích tất cả các bài viết, blog và stackoverflow tôi đã tìm thấy một giải pháp đơn giản. Không sử dụng saveInstanceState, đây là điều kiện với một dòng mã. Trên đoạn mã:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....

2

Điều này xảy ra bất cứ khi nào bạn đang cố tải một đoạn nhưng hoạt động đã thay đổi trạng thái thành onPause (). Điều này xảy ra ví dụ khi bạn cố gắng tìm nạp dữ liệu và tải nó vào hoạt động nhưng khi người dùng đã nhấp vào nút nào đó và đã chuyển sang hoạt động tiếp theo.

Bạn có thể giải quyết điều này theo hai cách

Bạn có thể sử dụng giao dịch.

hoặc là

Đảm bảo rằng hoạt động ở trạng thái tiếp tục và sẽ không tạm dừng trạng thái khi tải một đoạn. Tạo một boolean và kiểm tra xem hoạt động sẽ không ở trạng thái onPause ().

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

sau đó trong khi tải đoạn kiểm tra nếu hoạt động có mặt và chỉ tải khi hoạt động được nền trước.

if(mIsResumed){
 //load the fragment
}

1

Cảm ơn @gunar, nhưng tôi nghĩ có một cách tốt hơn.

Theo tài liệu:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

Vì vậy, sử dụng commitNowđể thay thế:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()

0

Vâng, sau khi thử tất cả các giải pháp trên mà không thành công (vì về cơ bản tôi không có giao dịch).

Trong trường hợp của tôi, tôi đã sử dụng AlertDialogs và ProgressDialog như những mảnh vỡ, đôi khi, khi xoay vòng, khi yêu cầu FragmentManager, lỗi tăng lên.

Tôi tìm thấy một cách giải quyết pha trộn một số bài viết tương tự:

Đây là giải pháp 3 bước, tất cả được thực hiện trên FragmentActivity của bạn (trong trường hợp này, được gọi là GenericActivity):

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}

0

Khi tôi sử dụng tính khởi động trong một đoạn, tôi sẽ nhận được ngoại lệ này;

Khi tôi thay đổi để sử dụng startactivityforresult, ngoại lệ sẽ biến mất :)

Vì vậy, cách dễ dàng để khắc phục nó là sử dụng api startActivityForResult :)


0

Tôi đã nhận được ngoại lệ này khi tôi nhấn nút quay lại để hủy trình chọn ý định trên hoạt động phân đoạn bản đồ của mình. Tôi đã giải quyết vấn đề này bằng cách thay thế mã của onResume () (nơi tôi đang khởi tạo phân đoạn và cam kết giao dịch) thành onStart () và ứng dụng hiện đang hoạt động tốt. Hy vọng nó giúp.


0

Điều này được sửa trong Android 4.2 và cả trong nguồn của thư viện hỗ trợ. [*]

Để biết chi tiết về nguyên nhân (và cách giải quyết), hãy tham khảo báo cáo lỗi của Google: http://code.google.com.vn/p/android/issues/detail?id=19917

Nếu bạn đang sử dụng thư viện hỗ trợ thì bạn không cần phải lo lắng về lỗi này (lâu dài) [*]. Tuy nhiên, nếu bạn đang sử dụng API trực tiếp (tức là Không sử dụng FragmentManager của thư viện hỗ trợ) và nhắm mục tiêu API bên dưới Android 4.2 thì bạn sẽ cần thử một trong những cách giải quyết.

[*] Tại thời điểm viết Trình quản lý SDK Android vẫn đang phân phối một phiên bản cũ thể hiện lỗi này.

Biên tập Tôi sẽ thêm một số làm rõ ở đây vì rõ ràng tôi đã nhầm lẫn bất cứ ai đã bỏ phiếu cho câu trả lời này.

một số trường hợp khác nhau (nhưng có liên quan) có thể khiến ngoại lệ này bị ném . Câu trả lời của tôi ở trên là đề cập đến trường hợp cụ thể được thảo luận trong câu hỏi, tức là một lỗi trong Android đã được sửa chữa sau đó. Nếu bạn nhận được ngoại lệ này vì một lý do khác thì đó là vì bạn đã thêm / xóa các đoạn khi bạn không nên (sau khi các trạng thái phân đoạn đã được lưu). Nếu bạn đang ở trong tình huống như vậy thì có lẽ " Các mảnh vỡ lồng nhau - IllegalStateException, không thể thực hiện hành động này sau khi onSaveInstanceState Lần " có thể được sử dụng cho bạn.



0

Trường hợp sử dụng của tôi: Tôi đã sử dụng trình nghe trong đoạn để thông báo cho hoạt động rằng một số điều đã xảy ra. Tôi đã thực hiện cam kết đoạn mới trên phương thức gọi lại. Điều này hoạt động hoàn toàn tốt vào lần đầu tiên. Nhưng về định hướng thay đổi, hoạt động được tạo lại với trạng thái thể hiện đã lưu. Trong trường hợp đó, đoạn không được tạo ra một lần nữa ngụ ý rằng đoạn đó có người nghe là hoạt động bị phá hủy cũ. Bất kỳ cách nào phương thức gọi lại sẽ được kích hoạt trên hành động. Nó đi đến hoạt động bị phá hủy gây ra vấn đề. Giải pháp là thiết lập lại trình nghe theo từng đoạn với hoạt động trực tiếp hiện tại. Điều này giải quyết vấn đề.


0

Những gì tôi tìm thấy là nếu một ứng dụng khác là loại hộp thoại và cho phép các lần chạm được gửi đến ứng dụng nền thì hầu như bất kỳ ứng dụng nền nào cũng sẽ gặp sự cố với lỗi này. Tôi nghĩ rằng chúng ta cần kiểm tra mỗi khi giao dịch được thực hiện nếu thể hiện được lưu hoặc khôi phục.


0

Trong trường hợp của tôi, với cùng một ngoại lệ lỗi, tôi đặt "onBackPression ()" trong một runnable (bạn có thể sử dụng bất kỳ chế độ xem nào của bạn):

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

Tôi không hiểu tại sao, nhưng nó hoạt động!


Đăng lên chế độ xem sẽ chỉ chạy Runnable sau khi chế độ xem được đặt đúng và được vẽ lên màn hình; điều này thường có nghĩa là Bản thân Hoạt động đã được khôi phục hoàn toàn, do đó không có vấn đề gì
Mercato

0

Bạn có thể đang gọi FragmentManager.popBackStackImmediate (); khi hoạt động bị tạm dừng. Hoạt động chưa kết thúc nhưng bị tạm dừng và không ở phía trước. Bạn cần kiểm tra xem hoạt động có bị tạm dừng hay không trước khi popBackStackImmediate ().


0

Tôi nhận thấy một điều rất thú vị. Tôi có trong ứng dụng của mình tùy chọn để mở thư viện điện thoại và thiết bị sẽ hỏi ứng dụng nào sẽ sử dụng, ở đó tôi nhấp vào vùng màu xám cách xa hộp thoại và thấy vấn đề này. Tôi nhận thấy cách hoạt động của tôi đi từ onPause, onSaveInstanceState trở lại onResume, nó không xảy ra để truy cập onCreateView. Tôi đang thực hiện giao dịch tại onResume. Vì vậy, những gì tôi đã làm là thiết lập một cờ bị phủ định onPause, nhưng là onCreateView đúng. nếu cờ đúng là onResume thì hãy thực hiện onCommit, nếu không thì commit ALLowingStateLoss. Tôi có thể tiếp tục và lãng phí rất nhiều thời gian nhưng tôi muốn kiểm tra vòng đời. Tôi có một thiết bị sdkversion 23 và tôi không gặp phải vấn đề này, nhưng tôi có một thiết bị khác là 21, và tôi thấy nó.


-1

bạn có thể sử dụng FragmentActivity.onStart trước popBackStackImmediate

như thế này:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jiltyliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.