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

求助:解决ContactHelper中java.lang.NullPointerException崩溃问题

Android应用NullPointerException崩溃修复方案

我们的Android应用偶尔发生崩溃,Firebase显示崩溃信息为:Fatal Exception: java.lang.NullPointerException,报错提示phoneCursor.getString(…) must not be null。该崩溃由ContactHelperClass中的getContactInformation方法引发,相关代码如下:

fun getContactInformation(activity: Activity, data: Intent, completion: (Buyer) -> Unit) {

    val buyer = Buyer()
    val contentUri = data.data
    val contentResolver = activity.contentResolver

    val cursor = contentResolver.query(contentUri!!, null, null, null, null)

    if (cursor != null) {
        if (cursor.moveToFirst()) {
            val contactId =
                cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
            if (cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) > 0) {
                buyer.firstName =
                    cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
            }
            if (cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER) > 0) {
                val phoneCursor = contentResolver.query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
                    arrayOf(contactId),
                    null
                )
                if (phoneCursor != null) {

                    if (phoneCursor.count > 1) {

                        val values = ArrayList<ContactBuyer>()

                        while (phoneCursor.moveToNext()) {

                            val contact = ContactBuyer()

                            contact.phoneNumber = phoneCursor.getString(
                                phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                            )

                            try {

                                when (phoneCursor.getString(
                                    phoneCursor.getColumnIndex(
                                        ContactsContract.CommonDataKinds.Phone.TYPE
                                    )
                                ).toInt()) {

                                    ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> {

                                        contact.name =
                                            R.string.buyer_select_phone_number_type_home.localized()

                                    }

                                    ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> {

                                        contact.name =
                                            R.string.buyer_select_phone_number_type_work.localized()

                                    }

                                    ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME, ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> {

                                        contact.name =
                                            R.string.buyer_select_phone_number_type_fax.localized()

                                    }

                                    ContactsContract.CommonDataKinds.Phone.TYPE_OTHER -> {

                                        contact.name =
                                            R.string.shared_other.localized()

                                    }

                                    ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE -> {

                                        contact.name =
                                            R.string.buyer_select_phone_number_type_mobile.localized()

                                    }

                                }

                            } catch (ex: Throwable) {

                                contact.name =
                                    R.string.shared_other.localized()

                            }

                            values.add((contact))

                        }

                        BuyerMultiNumberDialog().showDialog(
                            values,
                            object : BuyerMultiNumberInterface {
                                override fun onSelectedNumber(phoneNumber: String) {

                                    try {

                                        parsePhoneNumberFromContacts(
                                            activity,
                                            phoneNumber
                                        ) { extractedCountryCode, nationalNumber ->

                                            buyer.cellPhone =
                                                java.lang.Long.valueOf(nationalNumber)
                                                    .toString()

                                            buyer.countryCode = "+$extractedCountryCode"

                                        }

                                        phoneCursor.close()
                                        cursor.close()
                                        completion(buyer)

                                    } catch (e: Exception) {

                                    }
                                }

                            })

                    } else {

                        var phoneNumber = ""
                        while (phoneCursor.moveToNext()) {
                            phoneNumber = phoneCursor.getString(
                                phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                            )
                        }

                        try {

                            parsePhoneNumberFromContacts(
                                activity,
                                phoneNumber
                            ) { extractedCountryCode, nationalNumber ->

                                buyer.cellPhone =
                                    java.lang.Long.valueOf(nationalNumber).toString()

                                buyer.countryCode = "+$extractedCountryCode"

                            }

                            phoneCursor.close()
                            cursor.close()
                            completion(buyer)


                        } catch (e: Exception) {

                        }
                    }
                }
            }
        }
    }
}

完整堆栈跟踪信息如下:

Fatal Exception: java.lang.NullPointerException: phoneCursor.getString(
…                        ) must not be null
at com.**.business.helpers.utils.ContactHelper.getContactInformation(ContactHelper.kt:71)
at com.***.ui.layout.modules.shareListing.dialogs.ShareListingSendDialog.resultLauncher$lambda-12(ShareListingSendDialog.kt:734)
at com.***.ui.layout.modules.shareListing.dialogs.ShareListingSendDialog.$r8$lambda$9BI94Y5eKf3mim33AzjWutfS-bk()
at com.***.ui.layout.modules.shareListing.dialogs.ShareListingSendDialog$$ExternalSyntheticLambda8.onActivityResult(:4)
at androidx.activity.result.ActivityResultRegistry$1.onStateChanged(ActivityResultRegistry.java:149)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:271)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:313)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:151)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.Fragment.performStart(Fragment.java:3026)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:589)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:300)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:3079)
at androidx.fragment.app.FragmentController.dispatchStart(FragmentController.java:262)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:510)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:248)
at com.***.ui.layout.modules.listing.activities.listingDetail.ActivityListingDetail.onStart(ActivityListingDetail.kt:469)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1433)
at android.app.Activity.performStart(Activity.java:7986)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3677)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2267)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8167)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

问题分析

崩溃的核心原因是:调用phoneCursor.getString()读取联系人电话号码或类型字段时返回了null,但代码直接将null赋值给了非空类型变量(比如contact.phoneNumber可能被定义为非空String),触发了NullPointerException。这种情况通常出现在联系人条目存在但电话号码字段为空,或者系统Contacts数据库中存在异常数据的场景。

修复方案

1. 给getString()添加null安全处理

直接在调用getString()时使用?:操作符,为null返回值提供默认值,避免赋值给非空变量:

// 获取电话号码时添加默认值
contact.phoneNumber = phoneCursor.getString(
    phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
) ?: ""

// 获取电话类型时先处理null,再转换类型
val typeStr = phoneCursor.getString(
    phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)
)
when (typeStr?.toInt() ?: ContactsContract.CommonDataKinds.Phone.TYPE_OTHER) {
    // 原类型判断逻辑不变
}

2. 提前获取列索引,优化性能与稳定性

避免在循环中重复调用getColumnIndex(),提前获取一次列索引,减少冗余操作:

if (phoneCursor != null) {
    val numberColumnIndex = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
    val typeColumnIndex = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)
    
    if (phoneCursor.count > 1) {
        val values = ArrayList<ContactBuyer>()
        while (phoneCursor.moveToNext()) {
            val contact = ContactBuyer()
            contact.phoneNumber = phoneCursor.getString(numberColumnIndex) ?: ""
            // 处理类型逻辑...
        }
        // 弹窗逻辑...
    } else {
        var phoneNumber = ""
        while (phoneCursor.moveToNext()) {
            phoneNumber = phoneCursor.getString(numberColumnIndex) ?: ""
        }
        // 解析号码逻辑...
    }
}

3. 过滤空电话号码记录

如果电话号码为空,直接跳过该条记录,避免无效数据进入后续流程:

while (phoneCursor.moveToNext()) {
    val phoneNumber = phoneCursor.getString(numberColumnIndex) ?: continue
    val contact = ContactBuyer()
    contact.phoneNumber = phoneNumber
    // 处理类型逻辑...
    values.add(contact)
}

4. 完善异常处理与日志记录

原代码中的空catch块无法定位问题,添加日志记录便于后续排查:

catch (e: Exception) {
    Log.e("ContactHelper", "解析联系人电话号码失败", e)
}

5. 使用use函数自动关闭Cursor

利用Kotlin的use函数自动管理Cursor资源,避免忘记关闭导致的资源泄漏:

contentResolver.query(contentUri!!, null, null, null, null)?.use { cursor ->
    if (cursor.moveToFirst()) {
        // 原cursor处理逻辑
        contentResolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null,
            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
            arrayOf(contactId),
            null
        )?.use { phoneCursor ->
            // 原phoneCursor处理逻辑
        }
    }
}

使用use后无需手动调用cursor.close()和phoneCursor.close(),代码更简洁安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:31:01