Android android.database.StaleDataException

时间:2012-05-11 13:55:10

标签: android database cursor

我已将设备更新为Android ICS(4.0.3) 我有一个活动,其中包含一个列表视图,其中填入了从数据库中读取的数据。 使用ResourceCursorAdapter的扩展名将数据排列在列表视图中。

列表加载并显示在屏幕上后,我按下主屏幕以显示主屏幕。然后我从最近恢复我的应用程序(长按主屏幕)突然我得到以下异常:

05-10 15:49:17.925: E/AndroidRuntime(10721): Caused by: android.database.StaleDataException: Attempted to access a cursor after it has been closed.
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:75)
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.database.BulkCursorToCursorAdaptor.requery(BulkCursorToCursorAdaptor.java:144)
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.database.CursorWrapper.requery(CursorWrapper.java:186)
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.app.Activity.performRestart(Activity.java:4505)
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.app.Activity.performResume(Activity.java:4531)
05-10 15:49:17.925: E/AndroidRuntime(10721):    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2446)

在我的案例中,我已经阅读过操作系统的行为,并且光标似乎无效。 问题是尽管我已经注册了一个DataSetObserver,但是从未调用onInvalidated方法,并且在崩溃之前也没有调用Activity的方法(比如onResume,onRestart)。代码甚至没有到达适配器中的bindView。

你可以帮帮我吗? 如果需要,我可以提供更多信息和相关代码。

提前致谢

这里的代码,对不起,如果它是一团糟但我只是在微调之前使其工作:

public class CallLogsList extends Activity implements Runnable,
        OnItemClickListener {

    // ... various declaration here

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case CALLLOGS_LOAD_DONE:
                loadCurrCallLogsList(true);
                break;
        case SHOW_ALL_LOG:
            case SHOW_MISSED_LOG:
            case SHOW_OUTGOING_LOG:
            case SHOW_INCOMING_LOG:
                // - set the adapter
                if (null == mCad) {
                    // the first time the adapter need to be called
                    mCad = new CallLogsCursorAdapter(mContext, mDataCursor);
                    mCallLogsListView.setAdapter(mCad);
                } else {
                    mCad.changeCursor(mDataCursor);
                    mCad.notifyDataSetChanged();
                }
                break;
            } // end of switch ctrl structure
            return;
        } // end of method handleMessage

    }; // end of Handler object


    /**
     * The following inner class implements the custom adapter to contain the
     * call log entries as read from the database
 * 
     */
    class CallLogsCursorAdapter extends ResourceCursorAdapter {
        Cursor mCallLogCursor = null;
        CallLogDataSetObserver mLogDataObserver = null;

        /**
         * Class constructor
         * 
         * @param context
         * @param c
         */
        public CallLogsCursorAdapter(Context context, Cursor c) {
            super(context, R.layout.recent_calls_list_item, c);
            mLogDataObserver = new CallLogDataSetObserver();
            mCallLogCursor = c;
            mCallLogCursor.registerDataSetObserver(mLogDataObserver);
            return;
        } // end of class constructor

        /**
         * This method binds an existing view to the data pointed to by the
         * cursor
         */
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
                  // ... bind data to the view here
        } // end of method bindView

        /**
         * This method inflates the new view from the specified resource Such
         * resource has been passed to the super class in the call at the parent
         * class constructor we did in this derived class
         */
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            View view = super.newView(context, cursor, parent);
                    // ... create the view
            return view;
        } // end of method newView

        /**

        /**
         * The data set observer for the data used by this adapter
         */
        private class CallLogDataSetObserver extends DataSetObserver {

            @Override
            public void onChanged() {
                return;
            } // end of method onChanged

            @Override
            public void onInvalidated() {
                if( null != mCallLogCursor ) {
                                // TODO: Remove this call coz the UI can get stuck
                                // if the call log is too long. Just ask for a new         
                                // cursor asynchronously
                                mCallLogCursor.requery();
                             }
                return;
            } // end of method onInvalidated

        } // end of class inner class CallLogDataSetObserver

    } // end of class CallLogsCursorAdapter



    /**
     * This method is called the first time the activity is created
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(THIS_FILE, "Enter onCreate");
        super.onCreate(savedInstanceState);
             // ... initialization code here
        loadCurrCallLogsList(false);
        return;
    } // end of method onCreate




    /**
     * This method loads the current communication list
     */
    private synchronized void loadCurrCallLogsList(final boolean fromPullDown) {
        if (false == fromPullDown) {
            showLoadingView(true);
        }
        // start the loader thread
        Thread loader = new Thread(this);
        loader.start();
        return;
    } // end of method loadCurrCommunicationsList

    /**
     * This method is called when the activity is going to be destroyed
     */
    @Override
    protected void onDestroy() {
        if (null != database) database.close();
        database = null;
        if (mDataCursor != null)  mDataCursor.close();
        mDataCursor = null;
        // call the super class onDestroy method
        super.onDestroy();
    }


    /**
     * This method create the menu for the activity
     * 
     * @param menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.in_call_log_list_menu, menu);
        return super.onCreateOptionsMenu(menu);
    } // end of method onCreateOptionsMenu

    /**
     * This method is called when the menu is going to be shown
     */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (false == CordlessApplication.getInstance().canUseJungoApi()) {
            menu.findItem(R.id.menu_item_edit).setEnabled(false);
        } else {
            // enable the edit item in the menu if the list is not empty
            menu.findItem(R.id.menu_item_edit).setEnabled(
                    null != mCad && !mCad.isEmpty());
        }
        return super.onPrepareOptionsMenu(menu);
    } // end of method onPrepareOptionsMenu

    /**
     * This method is called when the user click on an item in the menu
     * 
     * @param item
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        item.setChecked(true);
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.sub_menu_item_all:
            mCurrentLogView = CURR_LIST_ALL_LOG;
            mListHeader.setText(R.string.all_calls_log_header_txt);
            loadCurrCallLogsList(false);
            break;
        case R.id.sub_menu_item_in_only:
            mCurrentLogView = CURR_LIST_INCOMING_LOG;
            mListHeader.setText(R.string.received_calls_log_header_txt);
            loadCurrCallLogsList(false);
            break;
        case R.id.sub_menu_item_out_only:
            mCurrentLogView = CURR_LIST_OUTGOING_LOG;
            mListHeader.setText(R.string.dialled_calls_log_header_txt);
            loadCurrCallLogsList(false);
            break;
        case R.id.sub_menu_item_miss_only:
            mCurrentLogView = CURR_LIST_MISSED_LOG;
            mListHeader.setText(R.string.missed_calls_log_header_txt);
            loadCurrCallLogsList(false);
            break;
        case R.id.menu_item_edit:
            startModifyActivity();
            break;
           default:
            return super.onOptionsItemSelected(item);
        }

        return (true);
    } // end of method onOptionsItemSelected




    /**
     * This method is called when the user comes back to this activity from a
     * sub activity
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (Activity.RESULT_OK != resultCode)
            return;
        switch (requestCode) {
        case DialingUtils.REQ_CODE_ADD_NEW_CONTACT: // pass through
        case DialingUtils.REQ_CODE_UPDATE_EXISTING_CONTACT:
            // refresh the call log list
            mCad.getCursor().requery();
            mCad.notifyDataSetChanged();
            break;
        case DialingUtils.REQ_CODE_PICK_CONTACT:
            DialingUtils.updateExistingContact(this, data.getData(),
                    mCallerInfo.mPhoneNumber, true);
            break;
        }
    } // end of method onActivityResult

    /**

    /**
     * This method load a filter version of the call logs
     * 
     * @param filter
     */
    private void loadFilteredData(final int filter) {
        if( null != mDataCursor ) mDataCursor.close();
        mDataCursor = null;
        // see whether it is needed to recover the database
        if (null == database) {
            database = new DBAdapter(mContext);
            database.open();
        }
        // read all the call logs from the database
        mDataCursor = database.getFilteredCallLogs(filter);
        return;
    } // end of method loadFilterData

    /**
     * This method is called when the user press a key on the device We use this
     * method to handle the press on the back key
     */
    @Override

    /**
     * This method is called in order to load the data from the 
     * local database in a separated thread
     */
    @Override
    public synchronized void run() {
        Looper.prepare();
        synchronized (MyConstants.mCallLogsMutex) {
            switch (mCurrentLogView) {
            case CURR_LIST_ALL_LOG:
                loadFilteredData(0);
                mHandler.sendEmptyMessage(SHOW_ALL_LOG);
                break;
            case CURR_LIST_MISSED_LOG:
                loadFilteredData(CallLog.Calls.MISSED_TYPE);
                mHandler.sendEmptyMessage(SHOW_MISSED_LOG);
                break;
            case CURR_LIST_OUTGOING_LOG:
                loadFilteredData(CallLog.Calls.OUTGOING_TYPE);
                mHandler.sendEmptyMessage(SHOW_OUTGOING_LOG);
                break;
            case CURR_LIST_INCOMING_LOG:
                loadFilteredData(CallLog.Calls.INCOMING_TYPE);
                mHandler.sendEmptyMessage(SHOW_INCOMING_LOG);
                break;
            }
        } // end of synch block
    } // end of method run


} // end of class CallLogsList

3 个答案:

答案 0 :(得分:9)

我找到了原因,解决方案如下: 当你必须处理Cursor或Database时,请避免使用Activity.managedQuery(...)和Activity.startManagingCursor(...)。 文档说他们确实被弃用了。

答案 1 :(得分:0)

而不是使用Activity.ManagedQuery()使用Context.Query()

string[] projection = new string[] { CalendarContract.Calendars.InterfaceConsts.Id };
        string selection = CalendarContract.Calendars.InterfaceConsts.Id + "=?";
        string[] selectionArgs =new string[]{Constants.CALENDAR_ID.ToString()};
        var cursor = m_ContentResolver.Query(calendarUri, projection, selection, selectionArgs, null);

来自Xamarin App的示例,日历操作

答案 2 :(得分:0)

使用

Activity.getContentResolver().query() 

而不是

Activity.managedQuery()