原始boolean属性赋值触发NullPointerException原因排查
问题描述
正在修复一款已发布至Google Play商店应用的崩溃故障,从Google Play控制台获取的崩溃堆栈如下:
java.lang.NullPointerException: at com.myApp.Advertising.InterstitialAdsRunCallback.setViewed (InterstitialAdsRunCallback.java:11) at com.myApp.AndroidLauncher$2.onInterstitialAdClosed (AndroidLauncher.java:65) at com.ironsource.mediationsdk.A$4.run (A.java:7) at android.os.Handler.handleCallback (Handler.java:938) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loop (Looper.java:223) at android.app.ActivityThread.main (ActivityThread.java:7888) at java.lang.reflect.Method.invoke (Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:981)
堆栈最初定位到InterstitialAdsRunCallback类第11行的setViewed方法,该类源码如下:
package com.myApp.Advertising; public abstract class InterstitialAdsRunCallback implements Runnable { boolean viewed; public boolean viewedAd() { return viewed; } public void setViewed(boolean viewed) { this.viewed = viewed; // line 11 } }
setViewed的调用逻辑位于IronSource插屏广告关闭回调中,对应代码如下:
public void onInterstitialAdClosed() { InterstitialAdsRunCallback adRunnable = showInterstitialAdRunnable.getAdRunnable(); adRunnable.setViewed(true); // line 65 adRunnable.run(); IronSource.loadInterstitial(); }
从发布的AAB安装包反编译得到InterstitialAdsRunCallback的smali代码,确认viewed字段为原始boolean类型(smali中标记为Z类型),反编译片段如下:
.class public abstract Lcom/myApp/Advertising/InterstitialAdsRunCallback; .super Ljava/lang/Object; .source "InterstitialAdsRunCallback.java" # interfaces .implements Ljava/lang/Runnable; # instance fields .field public viewed:Z # direct methods .method public constructor <init>()V .registers 1 .line 3 invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method
按照Java基础语法,原始类型boolean的属性赋值操作不可能触发NullPointerException,目前未定位问题根因,需要明确异常触发原因和排查方向。
根因分析
你被release构建的行号映射错位误导了,这个NPE根本不是setViewed方法内部的赋值操作抛的:
setViewed是仅包含一行赋值逻辑的简单setter,R8(release构建默认开启的代码优化器)会直接把这个方法内联到调用位置,不会保留单独的方法调用栈帧。- 如果构建时生成的mapping映射文件和Google Play控制台的去混淆逻辑匹配有偏差,就会把调用处的NPE错误标记到原setter方法的行号上。
- 真正的异常原因是:
showInterstitialAdRunnable.getAdRunnable()返回了null,你在null对象上调用setViewed方法触发了NPE。
这个问题本质是多线程竞态导致的:IronSource的广告关闭回调是在SDK自己的Handler线程触发(从堆栈可以看到回调执行在IronSource的A$4 Runnable中),和你主线程维护showInterstitialAdRunnable的逻辑存在时序差——比如广告展示结束后主线程提前清空了adRunnable引用,SDK的关闭回调晚几毫秒触发,就会拿到null值。
排查与修复方向
- 找到对应版本构建产物中的
mapping.txt文件,手动对崩溃堆栈做重新去混淆,可验证实际抛异常的位置就是调用setViewed的那一行,而非setViewed方法内部。 - 检查
showInterstitialAdRunnable的全生命周期逻辑,确认adRunnable的赋值、清空时机,补上多线程访问的同步处理,避免竞态。 - 临时兼容修复直接加空判断即可,避免崩溃:
public void onInterstitialAdClosed() { InterstitialAdsRunCallback adRunnable = showInterstitialAdRunnable.getAdRunnable(); if (adRunnable != null) { adRunnable.setViewed(true); adRunnable.run(); } IronSource.loadInterstitial(); }
- 低概率校验项:补全反编译smali中
setViewed方法的片段,确认release包中该方法没有被注入恶意代码、字段引用没有错乱,这个场景出现概率不足1%。
内容的提问来源于stack exchange,提问作者edunanami
相关产品推荐
相关产品推荐

