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

Android分批加载联系人并实时展示时Adapter通知异常求助

Hey there! Let's break down what's causing that error and fix it so you can show contacts in batches smoothly.

Why You're Seeing That Error

The core issue here is that you're modifying your ListAdapter's data or calling its notification methods (like notifyDataSetChanged()) from a background thread. Android strictly enforces that all UI-related operations (including updating adapters) must happen on the main thread—that's exactly what the error message is yelling at you about.

Fix 1: Use AsyncTask Correctly (Legacy but Works)

AsyncTask has built-in support for updating the UI mid-task via publishProgress() and onProgressUpdate(). The latter runs on the main thread, so it's safe to update your adapter here.

Here's a concrete example:

private class FetchContactsTask extends AsyncTask<Void, List<Contact>, Void> {

    @Override
    protected Void doInBackground(Void... voids) {
        List<Contact> batch = new ArrayList<>();
        int contactCount = 0;

        // Your contact-fetching logic (e.g., using ContentResolver)
        Cursor cursor = getContentResolver().query(
            ContactsContract.Contacts.CONTENT_URI,
            null, null, null, null
        );

        if (cursor != null && cursor.moveToFirst()) {
            do {
                // Parse a single contact from the cursor
                Contact contact = parseContactFromCursor(cursor);
                batch.add(contact);
                contactCount++;

                // Every 50 contacts, send the batch to the UI thread
                if (contactCount % 50 == 0) {
                    publishProgress(batch);
                    batch.clear(); // Reset for next batch
                }
            } while (cursor.moveToNext());

            // Don't forget any remaining contacts after the last full batch
            if (!batch.isEmpty()) {
                publishProgress(batch);
            }
            cursor.close();
        }
        return null;
    }

    // This runs on the MAIN thread—safe to update the adapter here!
    @Override
    protected void onProgressUpdate(List<Contact>... values) {
        List<Contact> newContacts = values[0];
        yourContactAdapter.addAll(newContacts); // Assume your adapter has an addAll() method
        yourContactAdapter.notifyDataSetChanged();
    }
}

Fix 2: Use Thread + runOnUiThread() (For Custom Threads)

If you're sticking with a plain Thread, wrap all adapter updates in runOnUiThread() to switch back to the main thread:

new Thread(() -> {
    List<Contact> batch = new ArrayList<>();
    int contactCount = 0;

    // Your contact-fetching logic here...
    Cursor cursor = getContentResolver().query(
        ContactsContract.Contacts.CONTENT_URI,
        null, null, null, null
    );

    if (cursor != null && cursor.moveToFirst()) {
        do {
            Contact contact = parseContactFromCursor(cursor);
            batch.add(contact);
            contactCount++;

            if (contactCount % 50 == 0) {
                // Switch to main thread to update adapter
                runOnUiThread(() -> {
                    yourContactAdapter.addAll(batch);
                    yourContactAdapter.notifyDataSetChanged();
                    batch.clear();
                });
            }
        } while (cursor.moveToNext());

        // Handle remaining contacts
        if (!batch.isEmpty()) {
            runOnUiThread(() -> {
                yourContactAdapter.addAll(batch);
                yourContactAdapter.notifyDataSetChanged();
            });
        }
        cursor.close();
    }
}).start();

Fix 3: Use Kotlin Coroutines (Modern Approach)

If you're using Kotlin, coroutines make this way cleaner with Dispatchers.IO for background work and Dispatchers.Main for UI updates:

lifecycleScope.launch(Dispatchers.IO) {
    val batch = mutableListOf<Contact>()
    var contactCount = 0

    contentResolver.query(
        ContactsContract.Contacts.CONTENT_URI,
        null, null, null, null
    )?.use { cursor ->
        while (cursor.moveToNext()) {
            val contact = parseContactFromCursor(cursor)
            batch.add(contact)
            contactCount++

            if (contactCount % 50 == 0) {
                // Switch to main thread safely
                withContext(Dispatchers.Main) {
                    yourContactAdapter.addAll(batch)
                    yourContactAdapter.notifyDataSetChanged()
                    batch.clear()
                }
            }
        }

        // Process leftover contacts
        if (batch.isNotEmpty()) {
            withContext(Dispatchers.Main) {
                yourContactAdapter.addAll(batch)
                yourContactAdapter.notifyDataSetChanged()
            }
        }
    }
}

Key Takeaways to Avoid This Error Again

  • Never modify your adapter's data list (add/remove/clear) from a background thread.
  • Never call notifyDataSetChanged() or similar adapter notification methods off the main thread.
  • Always route UI-related updates through the main thread—whether via AsyncTask's onProgressUpdate(), runOnUiThread(), or coroutines' Dispatchers.Main.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:35:49