android开发问题:Attempted to access a cursor after it has been closed.

本文探讨了Android开发中遇到的一个奇怪问题,即项目从一个页面跳转到另一个程序后,在返回时出现崩溃现象。问题源于在查询数据库时使用了`managedQuery`函数,并在之后手动关闭了游标,而Android 4.0系统自动关闭了游标,导致错误。文章提供了解决方法,以及对`managedQuery`函数的讨论,包括其可能带来的主线程阻塞问题。此外,还分享了另一种避免关闭游标的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天,开发android项目中遇到了一个很奇怪的问题。


问题:项目由一个页面跳转到另一个程序(比如按home键),再回到这个程序,就会奔溃。

有错误提示:Attempted to access a cursor after it has been closed.

问题奇怪的地方有,这个问题在android2.2版本下没有,到了4.0版本以上时就出现了。


自己想了一段时间,实在是想不到哪里有错误,最后在网上找到了答案。

是因为查询数据库时用到了以下函数:

Cursor cusor = activity.managedQuery(uri, projection, "address like ?",new String[]{ strsql },"date desc");

并在之后调用了cusor.close(); 关闭游标,但是android 4.0系统会关闭游标,不用自己手动的去关闭,所以就出现了这个奇怪的问题。


解决的办法是:

原来的代码:

Cursor cusor = activity.managedQuery(uri, projection, "address like ?",new String[]{ strsql },"date desc");

换成了以下代码:

ContentResolver cr = activity.getContentResolver();

Cursor cusor = cr.query(uri, projection, "address like ?",new String[]{ strsql },"date desc");


猿友们也有提供另一种方法,游标不要关闭。

但程序中其他的地方都是用了游标就关闭,这个地方就不关闭,总觉得怪怪的(不知道是不是强迫症)。


还有猿友说,managedQuery会阻塞主线程,已经不推荐使用。


1、觉得这个问题很奇怪。

2、managedQuery会阻塞主线程,已经不推荐使用,想找时间研究研究。

3、项找时间研究研究这个解决办法的原理。

基于以上3个原因,写下此文,希望能够给遇到同样问题的猿友帮助。

FATAL EXCEPTION: main Process: com.dosen.watchtest, PID: 24486 java.lang.RuntimeException: Unable to resume activity {com.dosen.watchtest/com.dosen.watchtest.activity.RingtonePickerActivity}: android.database.StaleDataException: Attempted to access a cursor after it has been closed. at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3698) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3741) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1699) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6747) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:449) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) Caused by: android.database.StaleDataException: Attempted to access a cursor after it has been closed. at android.database.BulkCursorToCursorAdaptor.throwIfCursorIsClosed(BulkCursorToCursorAdaptor.java:63) at android.database.BulkCursorToCursorAdaptor.requery(BulkCursorToCursorAdaptor.java:132) at android.database.CursorWrapper.requery(CursorWrapper.java:228) at android.app.Activity.performRestart(Activity.java:7099) at android.app.Activity.performResume(Activity.java:7125) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3673) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3741)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1699)  at android.os.Handler.dispatchMessage(Handler.j
07-19
package com.dosen.watchtest.activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.dosen.watchtest.R; import com.dosen.watchtest.widget.WatchListView; import java.util.ArrayList; import java.util.List; /** * Created by Daisy */ public class RingtonePickerActivity extends SecondaryActivity { private static final String TAG = "RingtonePickerActivity"; public static void start(Context context) { Intent starter = new Intent(context, RingtonePickerActivity.class); context.startActivity(starter); } private WatchListView watchListView; private RingtoneAdapter adapter; private List<RingtoneItem> ringtoneItems = new ArrayList<>(); private Ringtone currentRingtone; private int selectedPosition = -1; private Uri currentRingtoneUri; private Cursor cursor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ringtone_picker); currentRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_RINGTONE); watchListView = findViewById(R.id.ringtone_list); getSystemRingtones(); adapter = new RingtoneAdapter(this, ringtoneItems); watchListView.setAdapter(adapter); watchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { handleRingtoneSelection(position); } }); setDefaultSelection(); } private class RingtoneAdapter extends BaseAdapter { private final Context context; private final List<RingtoneItem> items; public RingtoneAdapter(Context context, List<RingtoneItem> items) { this.context = context; this.items = items; } @Override public int getCount() { return items.size(); } @Override public Object getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } class ViewHolder { TextView tvName; ImageView ivSelected; } @Override public View getView(int position, View view, ViewGroup viewGroup) { ViewHolder holder; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.ringtone_list_item, viewGroup, false); holder = new ViewHolder(); holder.tvName = view.findViewById(R.id.tv_ringtone_name); holder.ivSelected = view.findViewById(R.id.iv_ringtone_selected); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } RingtoneItem item = items.get(position); holder.tvName.setText(item.title); if (item.selected) { holder.tvName.setTextColor(getResources().getColor(R.color.white)); holder.ivSelected.setImageResource(R.drawable.ic_radio_button_checked); } else { holder.tvName.setTextColor(getResources().getColor(R.color.text_hui)); holder.ivSelected.setImageResource(R.drawable.ic_radio_button_unchecked); } return view; } } /** * 获取系统铃声 */ private void getSystemRingtones() { ringtoneItems.clear(); RingtoneManager manager = new RingtoneManager(this); manager.setType(RingtoneManager.TYPE_RINGTONE); cursor = manager.getCursor(); if (cursor != null && cursor.moveToFirst()) { do { String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); Uri uri = manager.getRingtoneUri(cursor.getPosition()); ringtoneItems.add(new RingtoneItem(title, uri)); } while (cursor.moveToNext()); cursor.close(); } } /** * 查找当前铃声在列表中的位置 */ private void setDefaultSelection() { for (int i = 0; i < ringtoneItems.size(); i++) { if (ringtoneItems.get(i).uri.equals(currentRingtoneUri)) { selectedPosition = i; ringtoneItems.get(i).selected = true; break; } } adapter.notifyDataSetChanged(); } /** * 设置选中的铃声 */ private void handleRingtoneSelection(int position) { RingtoneItem selectedItem = ringtoneItems.get(position); // 停止当前播放的铃声 if (currentRingtone != null && currentRingtone.isPlaying()) { currentRingtone.stop(); } // (重新)播放选中的铃声 currentRingtone = RingtoneManager.getRingtone(this, selectedItem.uri); currentRingtone.play(); // 更新选中状态 if (selectedPosition != -1) { ringtoneItems.get(selectedPosition).selected = false; } selectedItem.selected = true; selectedPosition = position; setAsDefaultRingtone(selectedItem.uri); adapter.notifyDataSetChanged(); } /** * 设置为系统默认铃声 * * @param uri 铃声Uri */ private void setAsDefaultRingtone(Uri uri) { try { RingtoneManager.setActualDefaultRingtoneUri( this, RingtoneManager.TYPE_RINGTONE, uri ); Log.d(TAG, "Ringtone set successfully: " + uri.toString()); } catch (SecurityException e) { Log.e(TAG, "Failed to set ringtone: " + e.getMessage()); // 处理权限问题 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { if (!Settings.System.canWrite(this)) { // 请求写入设置权限 Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } } } catch (Exception e) { Log.e(TAG, "Exception setting ringtone: " + e.getMessage()); } } /** * 铃声项数据类 * * @title 铃声名称 * @uri 铃声Uri * @selected 是否选中 */ private static class RingtoneItem { String title; Uri uri; boolean selected; public RingtoneItem(String title, Uri uri) { this.title = title; this.uri = uri; this.selected = false; } } @Override protected void onDestroy() { super.onDestroy(); if (currentRingtone != null && currentRingtone.isPlaying()) { currentRingtone.stop(); } if (cursor != null) { cursor.close(); } } } 上述代码出现了Android StaleDataException: Attempted to access a cursor after it has been closed 的报错,请帮忙优化修改
最新发布
07-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值