Android无弹窗添加联系人报Can't create handler inside thread错误求助
问题根源分析
- 错误1:
ContactAdd类错误继承了Activity:Android系统组件(Activity/Service等)不能自行通过构造方法实例化,必须由系统启动才会拥有合法的上下文环境,你这里只是要做联系人插入的逻辑封装,完全不需要继承Activity。 - 错误2:代码执行在无Looper的子线程:从日志可以看出你是在SDL子线程(通常是Python侧调用Java的工作线程)执行联系人插入逻辑,而
ContentResolver.applyBatch内部会创建Handler依赖Looper,子线程默认没有初始化Looper就会抛出该异常。 - 隐藏问题:未配置联系人写入权限:无弹窗插入联系人需要持有
WRITE_CONTACTS权限,Android 6.0+需要主动动态申请该权限,否则插入会失败。
修正后的代码实现
首先是工具类代码,去掉不必要的Activity继承:
import android.content.Context; import android.content.ContentProviderOperation; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.Phone; import java.util.ArrayList; class ContactAdd { private String firstName, lastName, phoneNumber; private Context mContext; public ContactAdd(String first, String last, String phone, Context context) { this.firstName = first; this.lastName = last; this.phoneNumber = phone; this.mContext = context; } public ArrayList<ContentProviderOperation> getInsertOps() { ArrayList<ContentProviderOperation> ops = new ArrayList<>(); SharedPreferences preference = PreferenceManager.getDefaultSharedPreferences(mContext); // 插入RawContacts基础记录 ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) .withValue(RawContacts.ACCOUNT_TYPE, preference.getString("accountKey", null)) .withValue(RawContacts.ACCOUNT_NAME, preference.getString("emailKey", null)) .build()); // 插入姓名 ops.add(ContentProviderOperation .newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) .withValue(StructuredName.DISPLAY_NAME, firstName + " " + lastName) .build()); // 插入手机号 ops.add(ContentProviderOperation .newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) .withValue(Phone.NUMBER, phoneNumber) .withValue(Phone.TYPE, Phone.TYPE_MOBILE) .build()); return ops; } }
然后是Contact类的addContact方法,需要切换到Android主线程执行插入逻辑:
import android.app.Activity; import android.content.ContentProviderResult; import android.provider.ContactsContract; import java.util.ArrayList; public class Contact { public static void addContact(Activity pyActivity, String name, String phone, String email) { // 切换到主线程执行,主线程默认有Looper pyActivity.runOnUiThread(() -> { ContactAdd contactHelper = new ContactAdd(name, name, phone, pyActivity.getApplicationContext()); ArrayList<ContentProviderOperation> ops = contactHelper.getInsertOps(); try { ContentProviderResult[] res = pyActivity.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { e.printStackTrace(); } }); } }
必要配置说明
- 权限配置:在
AndroidManifest.xml中添加写入联系人权限:
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
- 动态权限申请:Android 6.0(API 23)以上需要在调用
addContact之前,主动向用户申请WRITE_CONTACTS权限,用户授权后才能正常插入联系人。 - 如果你不需要绑定特定账号,可以把RawContacts的ACCOUNT_TYPE和ACCOUNT_NAME设为null,系统会自动绑定默认的本地联系人账号。
无弹窗插入联系人的其他思路
目前通过ContentProviderOperation批量插入的方式就是官方推荐的无用户交互插入联系人的方案,不需要跳转任何系统页面,只要有权限就可以静默插入。你之前用Intent的方案是调用系统自带的联系人创建页面,所以必然会弹出让用户确认的窗口,不适合静默插入的场景。
内容的提问来源于stack exchange,提问作者Gheorghe
相关产品推荐
相关产品推荐

