APKTool反编译修改Smali从资源取字符串触发空指针崩溃
硬编码字符串替换为资源读取的崩溃修正
崩溃根因
你新增的Lc/c/a/a;->l:Landroid/content/Context;成员变量从未被任何代码赋值,构造函数读取该字段时拿到的是类初始化后的默认null值,直接调用getString()方法就会触发空指针,和Logcat报的“在空对象上调用Context.getString(int)”完全吻合。
另外修改后的代码存在无效冗余:原本要替换的硬编码语句const-string v0, "Choose Storage"没有删除,执行后会被后续资源读取逻辑直接覆盖,没有实际作用。
核心修改思路
在普通Java实体类中,靠自定义成员字段存储Context的思路根本走不通——没有框架会自动给这个类的字段做依赖注入,读出来必然是null。最稳妥的方案是给类构造方法增加Context入参,从根本上拿到有效的Context对象做资源读取,不需要额外写反射逻辑。
具体调整步骤
- 删掉新增的无用成员字段
.field public l:Landroid/content/Context; - 把原本的无参构造方法修改为接收1个Context类型参数的形式,入参直接作为资源读取的上下文来源
- 删除无效的硬编码常量加载语句,直接用传入的Context加载对应ID的字符串
- 根据实际寄存器使用量调整
.locals声明,不需要盲目调高寄存器数量
修改完成的可运行smali代码
.class public Lc/c/a/a; .super Ljava/lang/Object; .source "SourceFile" # instance fields .field public a:Ljava/lang/String; # direct methods .method public constructor <init>(Landroid/content/Context;)V # 仅需要1个临时寄存器存储资源ID,.locals 1即可满足需求 .locals 1 .line 1 invoke-direct {p0}, Ljava/lang/Object;-><init>()V # 加载目标字符串的资源ID const v0, 0x7f1200a3 # p1为构造方法传入的Context实例,直接调用getString方法 invoke-virtual {p1, v0}, Landroid/content/Context;->getString(I)Ljava/lang/String; move-result-object v0 .line 2 iput-object v0, p0, Lc/c/a/a;->a:Ljava/lang/String; return-void .end method
配套调用逻辑修正
因为构造方法已经从无参改成了带Context参数的形式,所有项目中原本实例化这个类的位置,都需要把当前持有的Context实例作为参数传入,否则会触发方法找不到的类加载错误:
- 原本调用无参构造的代码格式:
new-instance v0, Lc/c/a/a; invoke-direct {v0}, Lc/c/a/a;-><init>()V - 调整为传入当前上下文的形式:
new-instance v0, Lc/c/a/a; # p0为当前调用位置持有的Context实例(Activity、Service等系统组件本身就实现了Context接口) invoke-direct {v0, p0}, Lc/c/a/a;-><init>(Landroid/content/Context;)V
注意:如果不想修改原有调用方逻辑,也可以通过全局Application Context完成资源读取,但该方案需要写反射逻辑获取应用实例,稳定性不如直接从构造方法传入Context的方式。
内容的提问来源于stack exchange,提问作者Vadim Turcanu
相关产品推荐
相关产品推荐

