Android “attempted to access a cursor after it has

2019-07-16 05:13发布

问题:

I'm trying to adapt the HelloGridView example so that I can show image thumbnails of images stored on the SD instead of images in the res/drawable. The idea is to create a list with the images first in the ImageView.initialize() function and the use it pretty much exactly as in the example.

I'm having trouble with my cursor, first I tried to have it as private in the Imageview.initialize() function (it's only commented away so you can see where I had it) since to me it seems like it is only being used there but then I got the error "Attempted to access a cursor after it has been closed." in the onCreate() function at the line

gridview.setAdapter(imageAdapter);

so this line obviously uses it somehow. Next try was to have the cursor as "global" and close it after that line instead but i get the same error but now when I'm leaving the onCreate(). This is how the code is now and I have no clue what to do. Also if you find anything else that looks odd please let me know since I'm very very new at android and java.

package se.mmarks.hellogridview;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private Cursor imagecursor;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GridView gridview = (GridView) findViewById(R.id.gridview);
        ImageAdapter imageAdapter = new ImageAdapter(this, imagecursor);
        imagecursor = imageAdapter.initialize();
        gridview.setAdapter(imageAdapter);
        /* setAdapter needs the cursor,
         * this is why it is closed here and not in ImageAdapter.initialize()
        */
        imagecursor.close();

        gridview.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            Toast.makeText(MainActivity.this, "" + position, Toast.LENGTH_SHORT).show();
            }
        });
    }
}



class ImageAdapter extends BaseAdapter {
    private ArrayList<ImageView> images = new ArrayList<ImageView>();
    Cursor imagecursor = null;


    private Context mContext;

    public ImageAdapter(Context c, Cursor cursor) {
        mContext = c;
        imagecursor = cursor;
    }

    public Cursor initialize() {
        final String[] columns = { MediaStore.Images.Thumbnails._ID };
        imagecursor = null;
        try {
            imagecursor = mContext.getContentResolver().query(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
        } catch(Exception e) {
            e.printStackTrace();
        }

        if(imagecursor != null){
            int image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Thumbnails._ID);
            int count = imagecursor.getCount();
            for (int i = 0; i < count; i++) {
                imagecursor.moveToPosition(i);
                int id = imagecursor.getInt(image_column_index);
                ImageView imageItem = new ImageView(mContext);
                imageItem.setId(id);
                imageItem.setImageBitmap(
                    MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id,
                    MediaStore.Images.Thumbnails.MICRO_KIND, null));
                images.add(imageItem);
            }
            //imagecursor.close();
        }
        return imagecursor;
    }

    @Override
    public int getCount() {
        return imagecursor.getCount();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        if (convertView == null) {  // if it's not recycled, initialize some attributes
            imageView = images.get(position);
            imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
        } else {
            imageView = (ImageView) convertView;
        }
        return imageView;
    }
}

回答1:

Android “attempted to access a cursor after it has been closed”

While you are working / accessing to data in Cursor, it needs to be opened!

Generally is recommended to close any datasources and cursors in Activity's life-cycle method either onStop() or onDestroy() method.

Basic example:

public void onDestroy() {
   super.onDestroy();
   if (cursor != null) {
      c.close();
   }
   if (db != null) {
      db.close();
   }
}


回答2:

Since I have not enough reputation to comment...

I must add the fact that even though the correct answer said that you could close your cursor onDestroy(), this is not something that you should do.

onDestroy method does not guarantee that Cursor.close() will be called for the fact that android itself does not guarantee that onDestroy would eventually be called.

From documentation:

onDestroy() = The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.