![](https://i-blog.csdnimg.cn/direct/cd76d532df1d44efa77511a083171d84.png)
1. 闪退
同意隐私政策后,启动进入游戏 Activity 闪退
getLifecycle NullPointerException 空指针异常
java
FATAL EXCEPTION: main
Process: com.primer.aa.gg, PID: 15722
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.primer.aa.gg/com.android.boot.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.lifecycle.Lifecycle com.primer.game.LifecycleManager.getLifecycle()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4529)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4822)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:118)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:153)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:104)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:3078)
at android.os.Handler.dispatchMessage(Handler.java:117)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:302)
at android.app.ActivityThread.main(ActivityThread.java:9668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:601)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1062)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.lifecycle.Lifecycle com.primer.game.LifecycleManager.getLifecycle()' on a null object reference
at com.primer.unitybridge.UniWbActivity.getLifecycle(UniWbActivity.java:387)
at androidx.activity.ComponentActivity.<init>(ComponentActivity.java:221)
at androidx.fragment.app.FragmentActivity.<init>(FragmentActivity.java:103)
at androidx.appcompat.app.AppCompatActivity.<init>(AppCompatActivity.java:94)
at com.google.androidgamesdk.GameActivity.<init>(GameActivity.java:58)
at com.unity3d.player.UnityPlayerGameActivity.<init>(UnityPlayerGameActivity.java:20)
at com.unity3d.player.UnityPlayerActivity.<init>(UnityPlayerActivity.java:3)
at com.primer.unitybridge.UniWbActivity.<init>(UniWbActivity.java:53)
at com.android.boot.MainActivity.<init>(MainActivity.java:56)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1392)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4508)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4822)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:118)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:153)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:104)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:3078)
at android.os.Handler.dispatchMessage(Handler.java:117)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:302)
at android.app.ActivityThread.main(ActivityThread.java:9668)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:601)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1062)
2. 分析
2.1 源码追溯
继承关系
java
MainActivity
UniWbActivity
UnityPlayerGameActivity
GameActivity
AppCompatActivity
FragmentActivity
ComponentActivity
Activity
Activity 来源
- MainActivity:游戏页面
- UniWbActivity:游戏页面
- UnityPlayerGameActivity:游戏页面
- GameActivity:依赖
androidx.games:games-activity:3.0.5
- AppCompatActivity:依赖
androidx.appcompat:appcompat:1.3.1
- FragmentActivity:依赖
androidx.fragment:fragment:1.3.6
- ComponentActivity:依赖
androidx.activity:activity:1.2.4
- Activity:依赖 Android API 31
接口实现
-
UniWbActivity:实现接口
LifecycleOwner
- 重写
getLifecycle
方法(getLifecycle 里面初始化) - getLifecycle 里面调用成员变量直接 new 的 LifecycleManager 【注:这里是闪退关键,下面使用例子说明】
- 重写
-
ComponentActivity:实现接口
LifecycleOwner
-
Activity:实现接口 implements
LifecycleOwner
详细看堆栈日志:
他是在类实例初始化init
方法里面调用getLifecycle
抛出异常
![](https://i-blog.csdnimg.cn/direct/e330751348a24e34b972c6e6a434a6af.png)
跟进源码到androidx.activity.ComponentActivity
结合堆栈 <init>
方案发现其构造函数调用了getLifecycle()
![](https://i-blog.csdnimg.cn/direct/e9b815c1aba045e3af27505f73a97c6c.png)
1、我们知道类的继承:实例化一个对象,先会执行父类的构造,再执行子类的构造方法
所以结合上面的类继承关系
可知:
- 先执行父类 ComponentActivity 构造方法
(里面调用了 getLifecycle)
- 再执行子类 UniWbActivity 构造方法
2、还知道类的多态:如果父类和子类都实现了同一个接口,并且在父类中调用了接口方法,实际执行的是子类重写的接口方法实现
所以综上可知:
- 会执行 UniWbActivity 里面重写的 getLifecycle 方法
这样看堆栈就对得上了,执行 ComponentActivity.<init>
之后跳到UniWbActivity.getLifecycle
执行重写方法
2.2 问题复现
为什么会空指针?无非就是对象未实例化就调用!
为什么没有执行实例化对象?我根据源码写 Demo 复现
源码是怎么样的
- 父类和子类实现同一个接口
- 父类在构造函数里,调用实现的方法
- 子类 new 一个成员变量
- 子类实现的方法里调用成员变量实例
根据上面步骤编码,实例化子类执行,问题复现,出现空指针异常!
![](https://i-blog.csdnimg.cn/direct/c5452eac6749403eb1cd6c363b43f22c.png)
java
//共同接口
public interface AnimalInterface {
public void eat();
}
java
//父类
public class Parent implements AnimalInterface {
static {
System.out.println("父类:cinit 静态初始化方法");
}
Parent() {
System.out.println("父类:init 初始化方法(构造方法)");
eat();
}
@Override
public void eat() {
System.out.println("父类:eat");
}
}
java
//子类
public class Child extends Parent implements AnimalInterface {
private Cat cat = new Cat();
static {
System.out.println("子类:cinit 静态初始化方法");
}
Child() {
System.out.println("子类:init 初始化方法(构造方法)");
}
@Override
public void eat() {
System.out.println("子类:eat");
cat.eatFish();
}
}
java
//成员对象
public class Cat {
static {
System.out.println("Cat:cinit 静态初始化方法");
}
Cat() {
System.out.println("Cat:init 初始化方法(构造方法)");
}
public void eatFish() {
System.out.println("成员变量:猫吃鱼");
}
}
java
//运行代码
public interface Main {
public static void main(String[] args) {
new Child();
}
}
2.3 继续思考
为什么子类里面的成员对象 Cat
没有执行 new 实例化?显然是代码执行顺序问题!
怎么验证是执行顺序问题呢?把子类里面//cat.eatFish();
注释掉再运行看看输出什么
![](https://i-blog.csdnimg.cn/direct/a7c99b6ab03c4522a0127b5db31b5a34.png)
很容易看出:
- 先是执行子类重写的 eat 方法
- 再执行成员变量 cat 的初始化
所以导致空指针异常!
不难发现,其实就是代码执行顺序的问题
2.4 产生错觉
调整代码执行顺序,使成员变量 Cat 的初始化先于子类 eat 执行即可。
可以把成员变量成员变量 cat 实例化写到静态代码块里,
这样 Child 的静态代码块先执行实例化 cat 再调用 eat 方法不会产生空指针异常了
java
public class Child extends Parent implements AnimalInterface {
private static Cat cat;
static {
System.out.println("子类:cinit 静态初始化方法");
cat = = new Cat();
}
Child() {
System.out.println("子类:init 初始化方法(构造方法)");
}
@Override
public void eat() {
System.out.println("子类:eat");
cat.eatFish();
}
}
以为万事大吉就此结束
2.5 新的错误
谁知道操作应用进入后台时出现了新的崩溃
![](https://i-blog.csdnimg.cn/direct/fba834c60cb540bf80220b446170e3fb.png)
重新复盘了,我的代码时这样的:
- AppCompatActivity 的父类 ComponentActivity 实现了 LifecycleOwner,并重写 getLifecycle 方法
- 这里我使用静态的 LifecycleRegistry,并重写 getLifecycle
java
package com.example.javademo;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
private static LifecycleRegistry LIFECYCLE_REGISTRY;
private static final LifecycleOwner LIFECYCLE_OWNER = new LifecycleOwner() {
@NonNull
@Override
public Lifecycle getLifecycle() {
return LIFECYCLE_REGISTRY;
}
};
private TestActivityLifeObserver testActivityLifeObserver = new TestActivityLifeObserver();
static {
//有问题的代码:onpause 执行闪退,LifecycleOwner 已被回收导致空指针
LIFECYCLE_REGISTRY = new LifecycleRegistry(new LifecycleOwner() {
@NonNull
@Override
public Lifecycle getLifecycle() {
return LIFECYCLE_REGISTRY;
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
getLifecycle().addObserver(testActivityLifeObserver);
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
@Override
protected void onStart() {
super.onStart();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
@Override
protected void onResume() {
super.onResume();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
@Override
protected void onPause() {
super.onPause();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
@Override
protected void onStop() {
super.onStop();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
@Override
protected void onDestroy() {
super.onDestroy();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
LIFECYCLE_REGISTRY.removeObserver(testActivityLifeObserver);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return LIFECYCLE_REGISTRY;
}
}
java
package com.example.javademo;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
public class TestActivityLife implements DefaultLifecycleObserver {
private final String TAG = "ceshi";
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onCreate: TestActivityLife");
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onStart: TestActivityLife");
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onResume: TestActivityLife");
}
@Override
public void onPause(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onPause: TestActivityLife");
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onStop: TestActivityLife");
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
Log.d(TAG, "onDestroy: TestActivityLife");
}
}
定位源码是 lifecycleOwner.get()
为空
![](https://i-blog.csdnimg.cn/direct/09ca1fc2428f44b49d9df35c7fbc7a88.png)
原来弱引用 lifecycleOwner 就是静态代码块 new 的那个内部类 LifecycleOwner,应该是被回收了导致的空指针,
所以如何确保这个对象不轻易被回收?
kotlin
open class LifecycleRegistry private constructor(
provider: LifecycleOwner,
private val enforceMainThread: Boolean
) : Lifecycle() {
private val lifecycleOwner: WeakReference<LifecycleOwner>
init {
// LifecycleOwner 就是
lifecycleOwner = WeakReference(provider)
}
}
强引用、弱引用
- 如果在静态代码块中使用弱引用保存内部类对象,一旦静态代码块执行完毕,就没有任何强引用指向该对象了。 在下一次垃圾回收时,该对象很可能被回收。 除非有其他强引用指向该对象
- 对象被声明为 static 时,它属于类本身,而不是类的实例。 会一直存在于内存中,直到类被卸载,静态变量的生命周期与类的生命周期相同,可以理解为他是强引用,不易被回收
把 LifecycleRegistry 代码抽象出来时这样的:
java
//弱引用,所以后面你应该回收
private final WeakReference<LifecycleOwner> mLifecycleOwner;
public LifecycleRegistry(@NonNull LifecycleOwner provider) {
mLifecycleOwner = new WeakReference<>(provider);
}
调整之后的代码,确保 obj 不易被回收
java
static Object obj = new Object();
WeakReference<Object> staticWeakReference = new WeakReference<>(obj);
- obj: 这是一个静态变量,直接指向 new Object() 创建的对象。这是一个强引用。
- staticWeakReference: 这是一个静态变量,保存了一个指向 obj 指向的同一个对象的弱引用。
关键在于,obj 这个强引用仍然存在。即使你创建了一个弱引用 staticWeakReference,obj 仍然持有对对象的强引用,这会阻止垃圾回收器回收该对象
3. 解决
那么把 LifecycleOwner 也设置成一个静态对象不久可以了吗
java
package com.example.javademo;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
private static final LifecycleRegistry LIFECYCLE_REGISTRY;
private static final LifecycleOwner LIFECYCLE_OWNER = new LifecycleOwner() {
@NonNull
@Override
public Lifecycle getLifecycle() {
return LIFECYCLE_REGISTRY;
}
};
private TestActivityLifeObserver testActivityLifeObserver = new TestActivityLifeObserver();
static {
//LIFECYCLE_OWNER 使用静态对象,避免被回收
LIFECYCLE_REGISTRY = new LifecycleRegistry(LIFECYCLE_OWNER);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
getLifecycle().addObserver(testActivityLifeObserver);
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
@Override
protected void onStart() {
super.onStart();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
@Override
protected void onResume() {
super.onResume();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
@Override
protected void onPause() {
super.onPause();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
@Override
protected void onStop() {
super.onStop();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
@Override
protected void onDestroy() {
super.onDestroy();
LIFECYCLE_REGISTRY.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
//最后别忘了移除观察者,避免内存泄露
LIFECYCLE_REGISTRY.removeObserver(testActivityLifeObserver);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return LIFECYCLE_REGISTRY;
}
}
相比随着 Android 的不断更新 Activity 变成了 ComponentActivity,内部已实现生命周期
LifecycleOwner,随着逐渐升级到新版本 androidx.activity:activity,相比不久的将来你也要适配吧!
除了上述处理当然也有其他方法处理,只是恰巧我接手的这个老项目就是这样的场景,也不想改动太多就这样吧 😄