Search code examples
androidcursor

Ensuring my cursor is closed


So I have a few different Activities that use a SimpleCursorAdapter for a ListView. My main concern right now would be memory leaks and whatnot. I need to make sure that the cursor is closed when it is supposed to be.

I noticed that my app crashes when I go to re-run the code after I've made changes due to a nullpointerexception.

Can someone take a look at my code for one of my activities and tell me if there are issues with the cursor not being closed properly? Or any other issues you see that may cause memory leaks or ANR?

RoutinesActivity.java:

package com.gauvion.gfit;

import android.annotation.SuppressLint;
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

public class RoutinesActivity extends ListActivity {

    private RoutinesDataSource datasource;
    private SimpleCursorAdapter dataAdapter;
    private boolean isEditing = false;
    private Toast toast_deleted;
    private String[] columns = new String[] { MySQLiteHelper.COLUMN_NAME };
    private int[] to;

    @SuppressLint("ShowToast")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_routines);
        getActionBar().setDisplayHomeAsUpEnabled(true);

        toast_deleted = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        datasource = new RoutinesDataSource(this);
        datasource.open();

        Cursor cursor = datasource.fetchAllRoutines();
        to = new int[] { R.id.listitem_routine_name };
        dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine, cursor, columns, to, 0);
        setListAdapter(dataAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.activity_routines, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {     
        Cursor cursor = datasource.fetchAllRoutines();
        switch (item.getItemId()) {
        case android.R.id.home:
            this.finish();
            return true;

        case R.id.button_routines_add:
            Intent startRoutineAdd = new Intent(this, RoutineAddActivity.class);
            this.startActivity(startRoutineAdd);
            return true;

        case R.id.button_routines_edit:
            to = new int[] { R.id.listitem_routine_edit_name };
            dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine_edit, cursor, columns, to, 0);
            setListAdapter(dataAdapter);

            isEditing = true;
            invalidateOptionsMenu();
            return true;

        case R.id.button_routines_edit_done:
            to = new int[] { R.id.listitem_routine_name };
            dataAdapter = new SimpleCursorAdapter(this, R.layout.listitem_routine, cursor, columns, to, 0);
            setListAdapter(dataAdapter);

            isEditing = false;
            invalidateOptionsMenu();
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);

        menu.findItem(R.id.button_routines_edit).setVisible(!isEditing);
        menu.findItem(R.id.button_routines_edit_done).setVisible(isEditing);

        return true;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long thisID)
    {       
        Cursor cursor = ((SimpleCursorAdapter)l.getAdapter()).getCursor();
        cursor.moveToPosition(position);
        long id = cursor.getLong(cursor.getColumnIndex(MySQLiteHelper.COLUMN_ID));
        String name = cursor.getString(cursor.getColumnIndex(MySQLiteHelper.COLUMN_NAME));

        if (!isEditing) {
            Intent startDaysActivity = new Intent(this, DaysActivity.class);
            startDaysActivity.putExtra("routineDataID", id);
            startDaysActivity.putExtra("routineDataName", name);
            this.startActivity(startDaysActivity);
        }
    }

    public void onClick(View view) {        
        ListView l = getListView();
        int position = l.getPositionForView(view);

        Cursor cursor = ((SimpleCursorAdapter)l.getAdapter()).getCursor();
        cursor.moveToPosition(position);
        long id = cursor.getLong(cursor.getColumnIndex(MySQLiteHelper.COLUMN_ID));
        String name = cursor.getString(cursor.getColumnIndex(MySQLiteHelper.COLUMN_NAME));

        switch (view.getId()) { 
        case R.id.button_routine_edit:
            Intent startRoutineEdit = new Intent(this, RoutineEditActivity.class);
            startRoutineEdit.putExtra("routineDataID", id);
            startRoutineEdit.putExtra("routineDataName", name);
            this.startActivity(startRoutineEdit);
            break;

        case R.id.button_routine_delete:
            toast_deleted.setText(getString(R.string.toast_routine_deleted));
            toast_deleted.show();
            datasource.deleteRoutine(id);
            onResume();
            break;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        datasource.open();
        Cursor cursor = datasource.fetchAllRoutines();
        dataAdapter.changeCursor(cursor);
    }

    @Override
    protected void onPause() {
        super.onPause();
        datasource.close();
    }

} 

Solution

  • I ended up just making the cursor a global and calling cursor.close() in an onDestroy() @Override.