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:
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.
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>
- CursorLoader instead of managedQuery:
CursorLoaderhandles 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_URIis the official, backward-compatible URI for accessing contacts on modern Android versions.
内容的提问来源于stack exchange,提问作者Sanjay

