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

Android 10设备TelephonyManager空指针崩溃问题求助(附Crashlytics栈信息及相关代码)

Android 10设备TelephonyManager相关NullPointerException崩溃分析与修复方案

Hey there, let's break down this crash you're seeing on Android 10 devices and figure out how to fix it.

首先,崩溃根源分析

Your crash stack points to a system-level NullPointerException inside TelephonyManager's error callback:

致命异常:java.lang.NullPointerException
尝试在空对象引用上调用虚拟方法'java.lang.Throwable android.os.ParcelableException.getCause()'
android.telephony.TelephonyManager$1.lambda$onError$2 (TelephonyManager.java:5346)

This is a bug in the Android 10 framework itself: when the telephony service encounters an error, the system expects a non-null ParcelableException to handle the error, but on some devices, this object ends up being null. When the system tries to call getCause() on it, boom—NPE.

Your code's call to getNetworkOperator() triggers the underlying telephony flow that leads to this broken callback. Importantly, this crash doesn't come directly from your code, which is why your existing try-catch blocks don't catch it—it's happening on a system-managed thread outside your execution stack.

Also, your current code has a few minor issues that aren't causing this crash but should be fixed for robustness:

  • Using as TelephonyManager (a hard cast) can throw a ClassCastException; as? is safer since it returns null instead of crashing.
  • You're calling substring(0,3) and substring(3) without checking if networkOperator is long enough, which could trigger IndexOutOfBoundsException.
  • The if (tm != null) check is redundant after a successful cast (if the cast works, tm can't be null).

解决方案

1. 切换到SubscriptionManager(推荐)

On Android 10+, SubscriptionManager is a more reliable way to get MCC/MNC info, and it avoids triggering the broken TelephonyManager callback. Here's updated code:

import android.content.Context
import android.os.Build
import android.telephony.SubscriptionManager

fun getMCCFromDevice(context: Context): String? {
    return try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            val subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
            val activeSubs = subscriptionManager?.activeSubscriptionInfoList
            activeSubs?.firstOrNull()?.let { subInfo ->
                val mcc = subInfo.mcc
                if (mcc != 0) mcc.toString().padStart(3, '0') else null
            }
        } else {
            // 兼容旧版本,保留TelephonyManager逻辑
            val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
            tm?.networkOperator?.takeIf { it.length >= 3 }?.substring(0, 3)
        }
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

fun getMNCFromDevice(context: Context): String? {
    return try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            val subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
            val activeSubs = subscriptionManager?.activeSubscriptionInfoList
            activeSubs?.firstOrNull()?.let { subInfo ->
                val mnc = subInfo.mnc
                if (mnc != 0) mnc.toString().padStart(2, '0') else null
            }
        } else {
            // 兼容旧版本
            val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
            tm?.networkOperator?.takeIf { it.length >= 5 }?.substring(3)
        }
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

2. 优化现有TelephonyManager代码

如果必须保留TelephonyManager调用逻辑,可以先修复代码中的健壮性问题:

fun getMCCFromDevice(context: Context): String? {
    return try {
        val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
        tm?.networkOperator?.takeIf { it.isNotEmpty() && it.length >= 3 }?.substring(0, 3)
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

fun getMNCFromDevice(context: Context): String? {
    return try {
        val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
        tm?.networkOperator?.takeIf { it.isNotEmpty() && it.length >= 5 }?.substring(3)
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

另外,针对Android 10设备,可以通过全局异常捕获来避免这个系统级崩溃导致应用终止:

// 在Application的onCreate方法中添加
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
    val isTelephonySystemCrash = throwable is NullPointerException &&
            throwable.stackTrace.any {
                it.className == "android.telephony.TelephonyManager\$1" &&
                it.methodName.contains("lambda\$onError\$2")
            }
    
    if (isTelephonySystemCrash) {
        // 记录日志但不终止应用
        android.util.Log.w("TelephonyCrash", "忽略系统级TelephonyManager空指针崩溃", throwable)
    } else {
        // 其他崩溃交给默认处理逻辑
        defaultHandler?.uncaughtException(thread, throwable)
    }
}

3. 确认权限配置

确保你的应用已经申请了必要的权限:

  • Android 10及以上:需要READ_PHONE_STATE危险权限,需动态申请
  • 旧版本Android:READ_PHONE_STATE 或 ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION(如果通过网络获取运营商信息)

总结

这个崩溃的核心是Android 10框架层面的bug,切换到SubscriptionManager是最彻底的解决方式,能完全避开触发该bug的路径。如果无法切换API,全局异常捕获可以防止应用因这个系统bug而崩溃。同时,修复原代码中的小问题也能提升整体健壮性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:47:47