Android静态对象成员丢失:appctx为何偶现空指针异常?
问题分析:单例类中appctx为空导致GoogleSignIn初始化异常
简化代码
public class MyTest { static MyTest instance = null; Context appctx = null; public static MyTest Instance() { if (null == instance) { instance = new MyTest(); } return instance; } // call from Application onCreate MyTest.Instance().onAppCreate(this); public void onAppCreate(Context context) { appctx = context; } // call from Activity onCreate MyTest.Instance().onActivityCreate(this); public void onActivityCreate(Activity act) { if (null == appctx) { Log.d("error", "null app"); } //... //client = GoogleSignIn.getClient(appctx, gso); } }
异常信息
Exception java.lang.RuntimeException: at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3851) at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:4027) at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2336) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loop (Looper.java:247) at android.app.ActivityThread.main (ActivityThread.java:8676) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602) at com.google.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130) Caused by java.lang.NullPointerException: Null context is not permitted. at com.google.android.gms.common.internal.Preconditions.checkNotNull (com.google.android.gms:play-services-basement@@18.3.0:2) at com.google.android.gms.common.api.GoogleApi.<init> (com.google.android.gms:play-services-base@@18.4.0:4) at com.google.android.gms.common.api.GoogleApi.<init> (com.google.android.gms:play-services-base@@18.4.0:21) at com.google.android.gms.auth.api.signin.GoogleSignInClient.<init> (com.google.android.gms:play-services-auth@@21.0.0:2) at com.google.android.gms.auth.api.signin.GoogleSignIn.getClient (com.google.android.gms:play-services-auth@@21.0.0:2)
原因分析
1. 静态instance被GC回收的可能性为0
instance是静态成员变量,属于类本身,只要类没有被ClassLoader卸载(Android常规开发场景下几乎不会发生类卸载),静态变量就会一直存在于方法区,不会被GC回收。因此这不是导致appctx为空的原因。
2. 真正的可能原因
线程不安全的单例实现:当前的
Instance()方法没有同步机制,多线程并发调用时可能创建多个MyTest实例。比如线程A进入if (null == instance)还未创建实例,线程B也进入该分支并创建实例、调用onAppCreate赋值appctx;随后线程A继续执行,又创建了一个新的MyTest实例,这个新实例的appctx未被赋值,后续调用onActivityCreate时就会出现空指针。初始化顺序异常:虽然
onAppCreate在Application的onCreate中调用,但存在极端场景:比如冷启动时系统调度异常,或者第三方库提前触发Activity启动,导致Activity的onCreate先于Application的onCreate执行完成,此时调用onActivityCreate时appctx还未被赋值。进程重启后的状态异常:当App因内存不足被系统杀死,用户重新打开时,系统会重启进程并恢复Activity栈。如果此时Application的
onCreate执行过程中出现异常,导致onAppCreate未完成赋值,或者系统恢复逻辑异常,都会导致appctx为空。
修复建议
- 实现线程安全的单例:采用双重检查锁(DCL)确保单例唯一,同时私有化构造方法防止外部创建实例:
public class MyTest { private static volatile MyTest instance = null; private Context appctx = null; private MyTest() {} // 私有构造,禁止外部实例化 public static MyTest Instance() { if (null == instance) { synchronized (MyTest.class) { if (null == instance) { instance = new MyTest(); } } } return instance; } }
- 确保appctx正确初始化:在
onAppCreate中使用Application的Context避免内存泄漏,同时增加兜底逻辑:
public void onAppCreate(Context context) { if (appctx == null) { appctx = context.getApplicationContext(); // 持有Application Context,避免内存泄漏 } } public void onActivityCreate(Activity act) { if (null == appctx) { Log.d("error", "null app, use fallback context"); appctx = act.getApplicationContext(); // 兜底获取Application Context } // 后续GoogleSignIn初始化逻辑 }
- 保证初始化顺序:将
MyTest.Instance().onAppCreate(this)放在Application onCreate方法的最开始执行,避免被其他逻辑阻塞或跳过。
内容的提问来源于stack exchange,提问作者Fosky etm
相关产品推荐
相关产品推荐

