You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读取手机联系人时Java安卓应用崩溃的修复请求

问题修复:安卓读取联系人时StaleDataException崩溃

核心问题分析

报错android.database.StaleDataException: Attempted to access a cursor after it has been closed的直接原因是主Cursor在while循环内部被提前关闭。在getContactList()方法中,每次执行cursor.moveToNext()后就调用了cursor.close(),但此时循环尚未结束,下一次循环尝试访问已关闭的Cursor就会触发异常。此外代码还存在权限判断不严谨、Cursor资源泄漏、RecyclerView未初始化等潜在问题。

修复后的完整代码

public class MainActivity extends AppCompatActivity {

    // Initialize variable
    RecyclerView recyclerView;
    ArrayList<ContactModel> arrayList = new ArrayList<ContactModel>();
    MainAdapter adapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 初始化RecyclerView,替换为你布局中对应的ID
        recyclerView = findViewById(R.id.recyclerView);
        
        // 检查读取联系人权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 100);
        } else {
            getContactList();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 仅在权限成功授予时执行读取逻辑
        if (requestCode == 100 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getContactList();
        }
    }


    private void getContactList() {
        // 清空旧数据,避免重复加载
        arrayList.clear();
        
        Uri uri = Contacts.CONTENT_URI;
        String sort = CommonDataKinds.Phone.DISPLAY_NAME + " ASC";

        Cursor cursor = getContentResolver().query(uri, null, null, null, sort);

        if (cursor != null && cursor.getCount() > 0) {
            while(cursor.moveToNext()){
                String id = cursor.getString(cursor.getColumnIndexOrThrow(Contacts._ID));
                String name = cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME));

                Uri uriPhone = CommonDataKinds.Phone.CONTENT_URI;
                String selection = CommonDataKinds.Phone.CONTACT_ID + " =?";

                Cursor phoneCursor = getContentResolver().query(uriPhone, null, selection, new String[]{id},null);
                
                if (phoneCursor != null) {
                    if (phoneCursor.moveToNext()){
                        String number = phoneCursor.getString(phoneCursor.getColumnIndexOrThrow(CommonDataKinds.Phone.NUMBER));
                        ContactModel model = new ContactModel();
                        model.setName(name);
                        model.setNumber(number);
                        arrayList.add(model);
                    }
                    // 无论是否获取到号码,都必须关闭phoneCursor
                    phoneCursor.close();
                }
            }
            // 主Cursor需在循环结束后再关闭
            cursor.close();

            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            adapter = new MainAdapter(this, arrayList);
            recyclerView.setAdapter(adapter);
        }
        // 处理cursor为空的情况,避免资源泄漏
        if (cursor != null) {
            cursor.close();
        }
    }
}

关键修复点说明

  • 调整主Cursor关闭时机:将cursor.close()从while循环内部移至循环结束后,确保整个循环过程中Cursor处于可用状态。
  • 严谨的权限结果判断:在onRequestPermissionsResult中增加判断逻辑,仅当权限被成功授予时才执行联系人读取操作。
  • 安全处理phoneCursor:增加空指针判断,且无论是否获取到号码都关闭phoneCursor,避免资源泄漏。
  • 初始化RecyclerView:在onCreate中完成RecyclerView的初始化,避免空指针异常。
  • 清空旧数据:读取联系人前清空数组列表,防止重复加载导致数据重复。

内容的提问来源于stack exchange,提问作者K. Wenzel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:41:25