使用Activity作为Context时触发NullPointerException的原因探究
非空Activity实例调用Context方法触发NullPointerException的原因分析
问题重现
相关代码
class Player( private val scope: CoroutineScope, private val ctx: Context ) { init { Log.d("FOO", ctx.toString()) Log.d("FOO", ctx.cacheDir.toString()) } ... } class MainActivity : AppCompatActivity() { val player = Player(lifecycleScope, this) ... }
日志输出
FOO/de.andrekr.android.box.MainActivity@90d5f8e AndroidRuntime/Shutting down VM AndroidRuntime/FATAL EXCEPTION: main Process: de.andrekr.android.box, PID: 30690 java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{de.andrekr.android.box/de.andrekr.android.box.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File android.content.Context.getCacheDir()' on a null object reference
核心矛盾
日志打印出了MainActivity的实例地址,证明ctx引用的Activity对象本身不为空,但调用getCacheDir()时却触发了NPE,同时看到有说法称:
但严格来说Activity并非Context,它只是可以访问Context
从Java/Android源码角度解析
Activity与Context的真实关系
没错,Activity确实是Context的子类(继承链是Activity extends ContextThemeWrapper extends ContextWrapper extends Context),但ContextWrapper是一个代理类,它内部持有一个真正的Context实现对象mBase。所有标准的Context方法(比如getCacheDir())最终都是通过这个mBase来执行的。非空Activity触发NPE的本质原因
问题出在Activity的初始化时机:- 代码中
player是MainActivity的直接成员变量,会在MainActivity对象实例化阶段(构造函数执行时)就被初始化,此时传入的this是已经创建的Activity实例,但这个实例的核心Context能力还未被系统初始化。 - Android系统中,Activity的
mBase是在Activity对象创建之后,通过系统的attach()方法注入的,构造函数执行时mBase还处于null状态。 - 调用
ctx.toString()时,执行的是Activity自身的toString()方法,不需要依赖mBase,所以能正常输出实例地址;但调用getCacheDir()时,ContextWrapper会尝试调用mBase.getCacheDir(),此时mBase还是null,直接触发NPE。
- 代码中
关于Kotlin
val不可变的误区val player和val ctx的不可变仅指引用本身不会指向其他对象,但对象内部的状态(比如Activity的mBase)是可以变化的。这里ctx始终指向同一个Activity实例,只是这个实例内部的Context核心依赖在初始化阶段还未被系统赋值。
解决方法
不要在Activity的成员变量初始化阶段直接传入this并调用Context相关方法,应该在Activity生命周期方法(比如onCreate())中初始化Player,此时mBase已经被系统赋值完成:
class MainActivity : AppCompatActivity() { lateinit var player: Player override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) player = Player(lifecycleScope, this) } ... }
内容的提问来源于stack exchange,提问作者AndreKR
相关产品推荐
相关产品推荐

