Android的android.database.StaleDataException [英] Android android.database.StaleDataException

查看:308
本文介绍了Android的android.database.StaleDataException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经更新我的设备到Android ICS(4.0.3)
我有一个包含从数据库中读取数据填充列表视图的活动。
的数据被布置在利用ResourceCursorAdapter的扩展的列表视图。

在列表中已被加载,我已经$ P $以打开主屏幕pssed主屏幕显示在屏幕上。然后,我已经恢复我的应用程序从最近用过(长pressing主屏幕),突然我得到了以下异常:

  15 05-10:49:17.925:E / AndroidRuntime(10721):android.database.StaleDataException:尝试它已被关闭后访问光标所致。
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:75)
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.database.BulkCursorToCursorAdaptor.requery(BulkCursorToCursorAdaptor.java:144)
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.database.CursorWrapper.requery(CursorWrapper.java:186)
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.app.Activity.performRestart(Activity.java:4505)
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.app.Activity.performResume(Activity.java:4531)
05-10 15:49:17.925:E / AndroidRuntime(10721):在android.app.ActivityThread.performResumeActivity(ActivityThread.java:2446)

我读过有关我的情况,并且光标操作系统的行为似乎是无效的。
现在的问题是,尽管我已经注册了一个DataSetObserver中的方法OnInvalidated不会被调用,坠机没有活动的方法之前(如onResume,onRestart)被称为好。在code,甚至没有在适配器达到bindView。

您能帮我吗?
如果你需要我可以提供更多的信息和相关的code。

在此先感谢

在这里,code,对不起,如果它是一个烂摊子,但我只是使它微调前工作:

 公共类CallLogsList扩展活动实现Runnable,
        OnItemClickListener {    // ...各种此处的声明    私人处理程序mHandler =新的处理程序(){        @覆盖
        公共无效的handleMessage(消息MSG){
            开关(msg.what){
            案例CALLLOGS_LOAD_DONE:
                loadCurrCallLogsList(真);
                打破;
        案例SHOW_ALL_LOG:
            案例SHOW_MISSED_LOG:
            案例SHOW_OUTGOING_LOG:
            案例SHOW_INCOMING_LOG:
                // - 设置适配器
                如果(空== MCAD){
                    //首次适配器需要被称为
                    MCAD =新CallLogsCursorAdapter(mContext,mDataCursor);
                    mCallLogsListView.setAdapter(MCAD);
                }其他{
                    mCad.changeCursor(mDataCursor);
                    mCad.notifyDataSetChanged();
                }
                打破;
            } //开关CTRL结构的结束
            返回;
        } //方法的handleMessage结束    }; // Handler对象的结束
    / **
     *以下内部类实现自定义适配器包含
     *通话记录从数据库中读取
 *
     * /
    类CallLogsCursorAdapter扩展ResourceCursorAdapter {
        光标mCallLogCursor = NULL;
        CallLogDataSetObserver mLogDataObserver = NULL;        / **
         *类的构造函数
         *
         * @参数方面
         * @参数Ç
         * /
        公共CallLogsCursorAdapter(上下文的背景下,光标C){
            超(背景下,R.layout.recent_calls_list_item,C);
            mLogDataObserver =新CallLogDataSetObserver();
            mCallLogCursor = C;
            mCallLogCursor.registerDataSetObserver(mLogDataObserver);
            返回;
        } //类的构造函数的结束        / **
         *此方法结合现有视图的数据被指向的
         *光标
         * /
        @覆盖
        公共无效bindView(查看视图,上下文的背景下,光标光标){
                  // ...数据绑定到这里查看
        } //方法bindView结束        / **
         *这个方法膨胀从指定的资源这样的新观点
         *资源已经被传递给超类中调用在父
         *类的构造函数,我们在做这个派生类
         * /
        @覆盖
        公共查看NewView的(上下文的背景下,光标光标的ViewGroup父){
            查看查看= super.newView(背景下,游标,父母);
                    // ...创建视图
            返回视图。
        } //方法NewView的结束        / **        / **
         *数据集观察员通过该适配器使用的数据
         * /
        私有类CallLogDataSetObserver扩展DataSetObserver {            @覆盖
            公共无效调用onChanged(){
                返回;
            } //方法的调用onChanged结束            @覆盖
            公共无效onInvalidated(){
                如果(NULL!= mCallLogCursor){
                                // TODO:删除此电话怎么把UI可以卡住
                                //如果通话记录太长。只要问一个新的
                                //光标异步
                                mCallLogCursor.requery();
                             }
                返回;
            } //方法结束onInvalidated        } //类的内部类CallLogDataSetObserver结束    } //类CallLogsCursorAdapter结束    / **
     *这种方法被称为第一次创建活动
     * /
    @覆盖
    保护无效的onCreate(捆绑savedInstanceState){
        Log.d(THIS_FILE,输入的onCreate);
        super.onCreate(savedInstanceState);
             // ...初始化code在这里
        loadCurrCallLogsList(假);
        返回;
    } //方法结束的onCreate
    / **
     *这个方法加载当前通信列表
     * /
    私人同步无效loadCurrCallLogsList(最终布尔fromPullDown){
        如果(假== fromPullDown){
            showLoadingView(真);
        }
        //启动加载程序线程
        螺纹装载机=新主题(本);
        loader.start();
        返回;
    } //方法loadCurrCommunicationsList结束    / **
     *这个方法被调用时,该活动将被销毁
     * /
    @覆盖
    保护无效的onDestroy(){
        如果(NULL =数据库!)database.close();
        数据库= NULL;
        如果(!mDataCursor = NULL)mDataCursor.close();
        mDataCursor = NULL;
        //调用父类的onDestroy方法
        super.onDestroy();
    }
    / **
     *这个方法创建活动菜单
     *
     * @参数菜单
     * /
    @覆盖
    公共布尔onCreateOptionsMenu(菜单菜单){
        MenuInflater吹气= getMenuInflater();
        inflater.inflate(R.menu.in_call_log_list_menu,菜单);
        返回super.onCreateOptionsMenu(菜单);
    } //方法结束onCreateOptionsMenu    / **
     *这种方法被称为菜单时将要被显示
     * /
    @覆盖
    prepareOptionsMenu(菜单菜单)上公共布尔{
        如果(假== CordlessApplication.getInstance()。canUseJungoApi()){
            menu.findItem(R.id.menu_item_edit).setEnabled(假);
        }其他{
            //启用菜单编辑项目,如果列表不为空
            menu.findItem(R.id.menu_item_edit).setEnabled(
                    空= MCAD和放大器;!&安培; !mCad.isEmpty());
        }
        返回super.on prepareOptionsMenu(菜单);
    } //在prepareOptionsMenu方法结束    / **
     *这个方法是当用户在菜单中单击一个项目叫做
     *
     * @参数项
     * /
    @覆盖
    公共布尔onOptionsItemSelected(菜单项项){
        item.setChecked(真);
        //处理项目选择
        开关(item.getItemId()){
        案例R.id.sub_menu_item_all:
            mCurrentLogView = CURR_LIST_ALL_LOG;
            mListHeader.setText(R.string.all_calls_log_header_txt);
            loadCurrCallLogsList(假);
            打破;
        案例R.id.sub_menu_item_in_only:
            mCurrentLogView = CURR_LIST_INCOMING_LOG;
            mListHeader.setText(R.string.received_calls_log_header_txt);
            loadCurrCallLogsList(假);
            打破;
        案例R.id.sub_menu_item_out_only:
            mCurrentLogView = CURR_LIST_OUTGOING_LOG;
            mListHeader.setText(R.string.dialled_calls_log_header_txt);
            loadCurrCallLogsList(假);
            打破;
        案例R.id.sub_menu_item_miss_only:
            mCurrentLogView = CURR_LIST_MISSED_LOG;
            mListHeader.setText(R.string.missed_calls_log_header_txt);
            loadCurrCallLogsList(假);
            打破;
        案例R.id.menu_item_edit:
            startModifyActivity();
            打破;
           默认:
            返回super.onOptionsItemSelected(项目);
        }        返回(真);
    } //方法结束onOptionsItemSelected
    / **
     *当用户从回来到这个活动时调用此方法
     *子活动
     * /
    @覆盖
    保护无效的onActivityResult(INT申请code,INT结果code,意图数据){
        如果(Activity.RESULT_OK!=结果code)
            返回;
        开关(要求code){
        案例DialingUtils.REQ_ code_ADD_NEW_CONTACT://通过
        案例DialingUtils.REQ_ code_UPDATE_EXISTING_CONTACT:
            //刷新通话记录列表
            mCad.getCursor()重新查询()。
            mCad.notifyDataSetChanged();
            打破;
        案例DialingUtils.REQ_ code_PICK_CONTACT:
            DialingUtils.updateExistingContact(这一点,data.getData()
                    mCallerInfo.mPhoneNumber,真正的);
            打破;
        }
    } //方法结束的onActivityResult    / **    / **
     *这个方法加载的通话记录过滤器版本
     *
     * @参数过滤器
     * /
    私人无效loadFilteredData(最终诠释过滤器){
        如果(零= mDataCursor!)mDataCursor.close();
        mDataCursor = NULL;
        //看它是否需要恢复数据库
        如果(空==数据库){
            数据库=新DBAdapter(mContext);
            database.open();
        }
        //从数据库中读取所有的通话记录
        mDataCursor = database.getFilteredCallLogs(过滤器);
        返回;
    } //方法loadFilterData结束    / **
     *当用户preSS装置我们用这个上的某个键时调用此方法
     *方法来处理的背键preSS
     * /
    @覆盖    / **
     *这个方法被调用,以加载从数据
     *在独立的线程本地数据库
     * /
    @覆盖
    公共同步无效的run(){
        尺蠖prepare()。
        同步(MyConstants.mCallLogsMutex){
            开关(mCurrentLogView){
            案例CURR_LIST_ALL_LOG:
                loadFilteredData(0);
                mHandler.sendEmptyMessage(SHOW_ALL_LOG);
                打破;
            案例CURR_LIST_MISSED_LOG:
                loadFilteredData(CallLog.Calls.MISSED_TYPE);
                mHandler.sendEmptyMessage(SHOW_MISSED_LOG);
                打破;
            案例CURR_LIST_OUTGOING_LOG:
                loadFilteredData(CallLog.Calls.OUTGOING_TYPE);
                mHandler.sendEmptyMessage(SHOW_OUTGOING_LOG);
                打破;
            案例CURR_LIST_INCOMING_LOG:
                loadFilteredData(CallLog.Calls.INCOMING_TYPE);
                mHandler.sendEmptyMessage(SHOW_INCOMING_LOG);
                打破;
            }
        同步块} //结束
    } //方法运行结束
} //类CallLogsList结束


解决方案

我已经找到了原因和解决方案如下:
只要避免Activity.managedQuery(...)和Activity.startManagingCursor(...)当你必须处理光标或数据库。
文档说他们是去$ P $确实pcated。

I've updated my device to Android ICS (4.0.3) I have an activity which contains a list view filled in with data read from a database. The data are arranged in the listview using an extension of the ResourceCursorAdapter.

Once the list has been loaded and shown on the screen I've pressed the home screen in order to bring up the home screen. Then I've recovered my application from the recents (long pressing the home screen) and suddenly I got the following exception:

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)

I've read about the behavior of the OS in my case and the cursor seem to be invalidated. The problem is that despite I've registered a DataSetObserver the method onInvalidated is never called, and before the crash no Activity's method ( like onResume, onRestart ) is called as well. The code even didn't reach the bindView in the Adapter.

Could you please help me with that? I can provide more info and relevant code if you need.

Thanks in advance

Here the code, sorry if it is a mess but I'm just making it work before fine tuning:

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

解决方案

I've found the cause and the solution is following: Just avoid Activity.managedQuery(...) and Activity.startManagingCursor(...) when you have to deal with Cursor or Database. The documentation says they are deprecated indeed.

这篇关于Android的android.database.StaleDataException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆