2011-07-20 31 views
14

Tôi có một vài ListFragments sử dụng CursorLoader để truy xuất nội dung của chúng. Khi người dùng đi sâu vào nội dung, một Fragment thay thế một phần khác (Activity vẫn giữ nguyên). Nhưng nếu những thay đổi nội dung trên một Fragment phi trên cùng, các tai nạn ứng dụng:Android: Sự cố CursorLoader trên Fragment không phải trên cùng

E/AndroidRuntime(18830): FATAL EXCEPTION: main 
E/AndroidRuntime(18830): java.lang.RuntimeException: Unable to resume activity {com.example.ExampleApp/com.example.ExampleApp.ExampleActivity}: java.lang.IllegalStateException: Content view not yet created 
E/AndroidRuntime(18830):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2120) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2135) 
E/AndroidRuntime(18830):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:957) 
E/AndroidRuntime(18830):  at android.os.Handler.dispatchMessage(Handler.java:99) 
E/AndroidRuntime(18830):  at android.os.Looper.loop(Looper.java:130) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.main(ActivityThread.java:3683) 
E/AndroidRuntime(18830):  at java.lang.reflect.Method.invokeNative(Native Method) 
E/AndroidRuntime(18830):  at java.lang.reflect.Method.invoke(Method.java:507) 
E/AndroidRuntime(18830):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
E/AndroidRuntime(18830):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
E/AndroidRuntime(18830):  at dalvik.system.NativeStart.main(Native Method) 
E/AndroidRuntime(18830): Caused by: java.lang.IllegalStateException: Content view not yet created 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328) 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.setListShown(ListFragment.java:280) 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.setListShownNoAnimation(ListFragment.java:266) 
E/AndroidRuntime(18830):  at com.example.ExampleApp.FirstListFragment.onLoadFinished(FirstListFragment.java:102) 
E/AndroidRuntime(18830):  at com.example.ExampleApp.FristListFragment.onLoadFinished(FirstListFragment.java:20) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:414) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl$LoaderInfo.reportStart(LoaderManager.java:298) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl.doReportStart(LoaderManager.java:751) 
E/AndroidRuntime(18830):  at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:512) 
E/AndroidRuntime(18830):  at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129) 
E/AndroidRuntime(18830):  at android.app.Activity.performStart(Activity.java:3791) 
E/AndroidRuntime(18830):  at android.app.Activity.performRestart(Activity.java:3821) 
E/AndroidRuntime(18830):  at android.app.Activity.performResume(Activity.java:3826) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2110) 
E/AndroidRuntime(18830):  ... 10 more 

Điều này có thể xảy ra khi một bản cập nhật nền thay đổi nội dung, hoặc khi dùng quay trở lại từ màn hình chủ và khác Fragment là ở đầu của ngăn xếp lại. Trình xử lý onLoadFinished cố gắng cập nhật giao diện người dùng không còn tồn tại nữa. Nhưng tại sao mảnh vỡ vẫn nhận được cập nhật con trỏ?

Tôi đã làm việc xung quanh nó bằng cách hủy bỏ bản cập nhật nếu đoạn không hiển thị, nhưng điều này có vẻ như là điều sai trái.

Đây là những gì Fragment của tôi trông như thế nào, thay đổi nội dung một chút cho ngắn gọn:

public class FirstListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 
    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     // Initialize empty cursor adapter. 
     mAdapter = new ExampleCursorAdapter(getActivity(), null, 0); 
     setListAdapter(mAdapter); 

     // Start with a progress indicator 
     setListShown(false); 

     // Prepare the loader. Either re-connect with an existing one, or start a new one. 
     getLoaderManager().initLoader(EXAMPLE_LOADER_ID, null, this); 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     return new CursorLoader(getActivity(), ExampleProvider.CONTENT_URI, PROJECTION, null, null, null); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     // Do nothing if we're not visible. 
     if(!isVisible()) { 
      return; 
     } 

     // Swap the new cursor in. (The framework will take care of closing the 
     // old cursor once we return.) 
     mAdapter.swapCursor(data); 

     // The list should now be shown. 
     if(isResumed()) { 
      setListShown(true); 
     } else { 
      setListShownNoAnimation(true); 
     } 
    } 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     // This is called when the last Cursor provided to onLoadFinished() above is about to be 
     // closed. We need to make sure we are no longer using it. 
     mAdapter.swapCursor(null); 
    } 
} 

Vui lòng cung isVisible() ngăn chặn sự sụp đổ, nhưng nó không có trong bất kỳ mã ví dụ tôi đã nhìn thấy. Có gì sai ở đây?


Edit: đủ Chắc chắn, tôi lên vết thương với một StaleDataException với một cái nhìn cũ cố gắng sử dụng một con trỏ khép kín. Vì vậy, bây giờ tôi đang phá hủy LoaderManager khi khung nhìn bị hủy. Vẫn không chắc chắn đây có phải là điều đúng hay không, và tôi không thể tái tạo StaleDataException.

tôi loại bỏ các isVisible() Hack từ trên cao và bổ sung này:

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

    // The CursorLoader example doesn't do this, but if we get an update while the UI is 
    // destroyed, it will crash. Why is this necessary? 
    getLoaderManager().destroyLoader(EXAMPLE_LOADER_ID); 
} 

Trả lời

2

Điều này dường như là một vấn đề với danh sách các mảnh nhận được xem danh sách. Đối với một số lý do getListView() sẽ không trả về một khung nhìn hợp lệ trừ khi đoạn được hiển thị. Tôi đã nhìn thấy nó ném một ngoại lệ nhà nước bất hợp pháp vào bất cứ điều gì trong vòng đời trước khi onStart().

Cách tôi giải quyết vấn đề là tôi gọi trình nạp trong phương thức onCreateView(). Khi trình tải trở về với dữ liệu tôi kiểm tra xem hoạt động có được bắt đầu hay không. Nếu đó là sau đó tôi tải nó vào danh sách xem, nếu không tôi lưu nó trong một biến địa phương, và sau đó tải dữ liệu trong phương thức onStart().

Điều này có vẻ như một siêu hack, nhưng nó hoạt động.

+0

Dường như có một số lỗi với trình tải trở lại quá sớm và getActivity() vẫn là null (đoạn không được đính kèm đầy đủ). Bộ nạp dường như được khởi động lại bởi khung công tác đôi khi quá. khiến nó trở lại quá sớm. – pjco

Các vấn đề liên quan