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

Android 7(Nougat)下自定义联系人页面持续崩溃求助

Hey there! Let's fix that crash you're seeing on Android 7. There are a few outdated practices and missing steps in your code causing the issue—here's how to resolve it:

Problem Breakdown & Fix Steps

1. Outdated Contact APIs & URI

The content://contacts/people URI you're using is a legacy contact address that was replaced by ContactsContract way back in Android 2.0. Additionally, managedQuery() was deprecated in API 11—modern Android uses CursorLoader or ContentResolver.query() to handle cursors, with explicit lifecycle management (opening/closing) to avoid leaks.

2. Missing Runtime Permission Request

Android 6.0 (API 23) and above require runtime permission requests for "dangerous" permissions like reading contacts. Android 7 falls into this category, so skipping this step will cause an immediate crash when your app tries to access contact data.

3. Incorrect Layout for SimpleCursorAdapter

You passed your main activity layout (R.layout.activity_main) to SimpleCursorAdapter, but this parameter expects the single list item layout (the layout for each contact entry), not the layout containing your ListView. This mismatch causes layout parsing errors and crashes.


Fixed Full Code

First, add the required permission to your AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_CONTACTS" />

Then update your MainActivity code:

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    private SimpleCursorAdapter adapter;
    private static final int READ_CONTACTS_PERMISSION_REQUEST = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize adapter with empty cursor first
        String[] columns = new String[]{ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID};
        int[] views = new int[]{R.id.contact_name, R.id.contact_id}; // Match IDs in your list item layout
        adapter = new SimpleCursorAdapter(this, R.layout.contact_list_item, null, columns, views, 0);

        ListView listView = findViewById(R.id.ListView);
        listView.setAdapter(adapter);

        // Check for permissions before loading data
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_CONTACTS},
                    READ_CONTACTS_PERMISSION_REQUEST);
        } else {
            getSupportLoaderManager().initLoader(0, null, this);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == READ_CONTACTS_PERMISSION_REQUEST) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                getSupportLoaderManager().initLoader(0, null, this);
            } else {
                Toast.makeText(this, "Contact permission is required to view the list", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @NonNull
    @Override
    public Loader<Cursor> onCreateLoader(int id, @Nullable Bundle args) {
        // Use CursorLoader to safely load contact data asynchronously
        String[] projection = {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME};
        return new CursorLoader(this, ContactsContract.Contacts.CONTENT_URI,
                projection, null, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
    }

    @Override
    public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
        // Swap in the loaded cursor
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(@NonNull Loader<Cursor> loader) {
        // Clear cursor to avoid memory leaks
        adapter.swapCursor(null);
    }
}

Add a List Item Layout

Create a new layout file res/layout/contact_list_item.xml for individual contact entries:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/contact_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/contact_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="#666666" />
</LinearLayout>

Key Changes Explained
  • CursorLoader instead of managedQuery: CursorLoader handles asynchronous data loading and cursor lifecycle automatically, preventing ANRs and memory leaks.
  • Runtime Permission Handling: Ensures your app requests contact access properly on Android 6+ systems, avoiding crashes from missing permissions.
  • Separate Layouts: Uses a dedicated list item layout instead of the main activity layout, fixing layout parsing errors.
  • Standard Contacts URI: ContactsContract.Contacts.CONTENT_URI is the official, backward-compatible URI for accessing contacts on modern Android versions.

内容的提问来源于stack exchange,提问作者Sanjay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:29:59