CursorLoader and Finalizing cursor that has not be

2019-04-17 00:40发布

问题:

In my activity I have 2 CursorLoader and 2 TextView with a OnClickListener that calls the setScreen() method. Clicking the Textviews sometimes I have the error

11-29 15:27:26.045: INFO/dalvikvm(1223): Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor@43c02e18 on MAIN_TABLE that has not been deactivated or closed
11-29 15:27:26.045: INFO/dalvikvm(1223):     at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)
11-29 15:27:26.045: INFO/dalvikvm(1223):     at dalvik.system.NativeStart.run(Native Method)
11-29 15:27:26.065: INFO/dalvikvm(1223): Uncaught exception thrown by finalizer (will be discarded):

The complete code is

public class ActivityMatchesList extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> {
    private MyAdapter1 mAdapter1;
    private MyAdapter2 mAdapter2;
    private int mTab;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /** Cursor Loader */
        getSupportLoaderManager().initLoader(1, null, this);
        getSupportLoaderManager().initLoader(2, null, this);
        /** set content view */
        setContentView(R.layout.main_list);
        View vv = new View(this);
        LinearLayout ll = (LinearLayout) findViewById(R.id.boxRanking);
        vv = View.inflate(this, R.layout.tab_ranking, null);
        ll.addView(vv, new LinearLayout.LayoutParams(ll.getLayoutParams().width, ll.getLayoutParams().height));
        /** set colors */
        ListView lvLive = (ListView)findViewById(R.id.matchListList);
        ListView lvLeagueMatches = (ListView) findViewById(R.id.listLeagueMatches);
        /** create and set Adapters */
        mAdapter1 =     new MyAdapter1(
            this, 
            R.layout.main_list_row, 
            null,
            0);
        mAdapter2 = new MyAdapter2(
            this, 
            R.layout.list_ran_row, 
            null,
            0);
        lvLive.setAdapter(mAdapter1);
        lvLeagueMatches.setAdapter(mAdapter2);          

        /** listener */
        TextView tabLive = (TextView) findViewById(R.id.tab_main);
        TextView tabRank = (TextView) findViewById(R.id.tab_ran);
        tabLive.setOnClickListener(onClickListenerTab);
        tabRank.setOnClickListener(onClickListenerTab);
    }

    @Override
    protected void onResume() {
        super.onResume();
        setScreen();
    }

    private void setScreen() {
        if (mTab!=Constants.Tab.TAB_2) mTab=Constants.Tab.TAB_1;
        LinearLayout llRanking = (LinearLayout) findViewById(R.id.boxRanking);
        ListView lvLive = (ListView)findViewById(R.id.matchListList);
        switch (mTab){
            case Constants.Tab.TAB_1:
                llRanking.setVisibility(View.GONE);
                lvLive.setVisibility(View.VISIBLE);
                break;
            case Constants.Tab.TAB_2:
                llRanking.setVisibility(View.VISIBLE);
                lvLive.setVisibility(View.GONE);
                break;  
        }
        getContentResolver().notifyChange(MyProvider.CONTENT_URI, null);
    }

    private OnClickListener onClickListenerTab = new OnClickListener() {
        public void onClick(final View v) {
            int mNewTab;
            if(v.getId()==R.id.tab_ran){
                mNewTab = Constants.Tab.TAB_2;
            } else {
                mNewTab = Constants.Tab.TAB_1;
            } 
            if (mTab != mNewTab) {
                mTab = mNewTab;
                setScreen();
            }
        }
    };

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id){
        case 1:
            CursorLoader cursorLoaderLive = new CursorLoader(
                this, 
                MyProvider.CONTENT_URI_MAIN, 
                null, 
                null, 
                null, 
                "MatchDateYear, MatchDateMonth, MatchDateDay, MatchHour, MatchMinute");
            return cursorLoaderLive;
        case 2:
            CursorLoader cursorLoaderRankingLeague = new CursorLoader(
                this, 
                MyProvider.CONTENT_URI_RAN, 
                null, 
                "Round=3 AND IsMatch=1", 
                null, 
                null);
            return cursorLoaderRankingLeague;
        default:
            return null;
        }
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()){
        case 1:
            mAdapter1.swapCursor(cursor);
            break;
        case 2:
            mAdapter2.swapCursor(cursor);
            break;
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()){
        case 1:
            mAdapter1.swapCursor(null);
            break;
        case 2:
            mAdapter2.swapCursor(null);
            break;
        }
    }
}

Why CursorLoader gives this error? I read that CursorLoader should manage the cursor and close it when necessary.

回答1:

Not sure if any of this is related to your problem, but here goes:

  1. I wouldn't call getSupportLoaderManager().initLoader() before you create your adapters. Maybe it works, but if onLoadFinished() gets called before the adapters are created, it seems to me like it could cause issues.

  2. Why are you calling getContentResolver().notifyChange() in your setScreen() method? As far as I understand, notifyChange() is for notification that data in the content provider has changed, but it doesn't look to me like you're changing anything. From my limited use of content providers, I only call ContentResolver.notifyChange() within the content provider class itself. See its use here for an example: http://developer.android.com/resources/samples/NotePad/src/com/example/android/notepad/NotePadProvider.html

  3. This is really nitpicky, but in onCreateLoader() you could probably do something like this:

    return new CursorLoader(
    instead of creating unused references.

  4. Lastly, to get to the actual question, the only time I've seen that error is when I have an open cursor that I haven't closed. Since CursorLoader handles the cursor life cycle, I'd look to make sure there isn't anywhere else in your app where you have an open cursor, like from calling getContentResolver().query() or querying a database directly.

  5. If none of the above works, maybe you could split the data you're handling with separate CursorLoaders into separate CursorProviders.