Android LifecycleOwner 闪退,java 继承、多态特性!

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

    1. 重写 getLifecycle 方法(getLifecycle 里面初始化)
    2. getLifecycle 里面调用成员变量直接 new 的 LifecycleManager 【注:这里是闪退关键,下面使用例子说明】
  • ComponentActivity:实现接口 LifecycleOwner

  • Activity:实现接口 implements LifecycleOwner

详细看堆栈日志:

他是在类实例初始化init方法里面调用getLifecycle抛出异常

跟进源码到androidx.activity.ComponentActivity 结合堆栈 <init> 方案发现其构造函数调用了getLifecycle()

1、我们知道类的继承:实例化一个对象,先会执行父类的构造,再执行子类的构造方法

所以结合上面的类继承关系可知:

  • 先执行父类 ComponentActivity 构造方法(里面调用了 getLifecycle)
  • 再执行子类 UniWbActivity 构造方法

2、还知道类的多态:如果父类和子类都实现了同一个接口,并且在父类中调用了接口方法,实际执行的是子类重写的接口方法实现

所以综上可知:

  • 会执行 UniWbActivity 里面重写的 getLifecycle 方法

这样看堆栈就对得上了,执行 ComponentActivity.<init> 之后跳到UniWbActivity.getLifecycle执行重写方法

2.2 问题复现

为什么会空指针?无非就是对象未实例化就调用!

为什么没有执行实例化对象?我根据源码写 Demo 复现

源码是怎么样的

  • 父类和子类实现同一个接口
  • 父类在构造函数里,调用实现的方法
  • 子类 new 一个成员变量
  • 子类实现的方法里调用成员变量实例

根据上面步骤编码,实例化子类执行,问题复现,出现空指针异常!

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();注释掉再运行看看输出什么

很容易看出:

  • 先是执行子类重写的 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 新的错误

谁知道操作应用进入后台时出现了新的崩溃

重新复盘了,我的代码时这样的:

  • 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() 为空

原来弱引用 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,相比不久的将来你也要适配吧!

除了上述处理当然也有其他方法处理,只是恰巧我接手的这个老项目就是这样的场景,也不想改动太多就这样吧 😄

相关推荐
violin-wang35 分钟前
Intellij IDEA如何查看当前文件的类
java·ide·intellij-idea
【 】42335 分钟前
安卓7以上抓包证书安装
android·javascript·爬虫·网络爬虫
圆圆同学1 小时前
Springboot实现TLS双向认证
java·spring boot·后端
大地爱1 小时前
基于SpringBoot和PostGIS的各国及所属机场信息检索及可视化实现
java·spring boot·spring
明似水1 小时前
高效管理Dart和Flutter多包项目:Melos工具全解析
android·前端·flutter
荔枝味啊~1 小时前
杭州某小厂面试
java·网络·数据库·tcp/ip·面试
九圣残炎1 小时前
【异常记录Java-20250204】调用讯飞星火AI(Spark lite 版本)Api 授权错误问题处理
java·人工智能·spring·spark
神秘的t2 小时前
javaEE初阶————多线程初阶(3)
java·开发语言
liuhaikang2 小时前
【鸿蒙HarmonyOS Next实战开发】实现组件动态创建和卸载-优化性能
java·前端·数据库
m0_748246872 小时前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring