Android 10设备TelephonyManager空指针崩溃问题求助(附Crashlytics栈信息及相关代码)
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 aClassCastException;as?is safer since it returns null instead of crashing. - You're calling
substring(0,3)andsubstring(3)without checking ifnetworkOperatoris long enough, which could triggerIndexOutOfBoundsException. - The
if (tm != null)check is redundant after a successful cast (if the cast works,tmcan'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

