java.lang.AbstractMethodError: abstract method "void dji.v5.manager.interfaces.I

133 阅读3分钟

大疆MSDK5.15.0已经发布了快2个月了,为啥现在才开始适配,因为M400飞机才刚刚回来。 在升级的过程中遇到了一个小问题记录一下。

java.lang.AbstractMethodError: abstract method "void dji.v5.manager.interfaces.ICameraStreamManager$AvailableCameraUpdatedListener.onCameraStreamEnableUpdate(java.util.Map)

升级完毕之后程序闪退,出现了上述这个错误提示。

WTF这是什么鬼东西?这一看就是大疆SDK里面的东西。下面贴出这个错误的完整信息。

1753944969346.png

可以看到,这个错误是CameraStreamManager中一个AvailableCameraUpdatedListene接口,未能正常实现接口中的方法所抛出的。

简单理解一下,这里定义了一个接口,但是这个接口中的方法没有实现,在编译期间没有问题,但是一运行就抛出了java.lang.AbstractMethodError: abstract method。

是时候看看AvailableCameraUpdatedListener的庐山正面目了

public interface AvailableCameraUpdatedListener {
    @MainThread
    void onAvailableCameraUpdated(@NonNull List<ComponentIndexType> availableCameraList);

    default void onCameraStreamEnableUpdate(@NonNull Map<ComponentIndexType, Boolean> cameraStreamEnableMap) {
    }
}

依据上面的错误信息,和这个接口的信息,应该就是onCameraStreamEnableUpdate方法没有实现才抛出的错误,这个方法前面不是有default么?这个default有什么用?Kotlin中有类似的关键字么?这些问题我们稍后再说,先说说如何让程序不崩溃。

1、定位到我们的代码,找到哪个类。实现了 AvailableCameraUpdatedListener 接口。 2、经过排查终于锁定了我的出问题的代码。

val availableCameraUpdatedListener = AvailableCameraUpdatedListener { list ->
    if (list.contains(cameraIndex)) {
        updateTextureCameraStream(textureSurface.value)
    } else {
        textureSurface.value?.let { surface ->
            MediaDataCenter.getInstance().cameraStreamManager.removeCameraStreamSurface(
                surface
            )
        }
    }
}

3、查看我自己的代码,确实这个接口中的 onCameraStreamEnableUpdate 方法没有实现,应该怎么改?非常简单,直接重写这个方法,给它添加一个实现就好了。

val availableCameraUpdatedListener= object : AvailableCameraUpdatedListener {

    override fun onAvailableCameraUpdated(list: MutableList<ComponentIndexType>) {
        if (list.contains(cameraIndex)) {
            updateTextureCameraStream(textureSurface.value)
        } else {
            textureSurface.value?.let { surface ->
                MediaDataCenter.getInstance().cameraStreamManager.removeCameraStreamSurface(
                    surface
                )
            }
        }
    }
    
    //重写这个方法
    override fun onCameraStreamEnableUpdate(cameraStreamEnableMap: MutableMap<ComponentIndexType, Boolean>) {

    }
}

好了到这里为止,程序不崩溃了,能正常运行了。

接下来说说 default 关键字的用法,以及为什么程序在编译期没有报错,但是一运行就报错。

一、default 关键字

在 Java 中,接口方法使用 default 修饰符,表示该方法是一个默认方法

具体含义如下:

1、提供默认实现 接口可以包含带有具体实现的方法,而不只是抽象方法。

2、可选重写: 实现类可以选择重写该方法,但是不强制要求。

这样设计的目的,主要用于接口的演化,当需要向现有接口添加新方法时,避免破坏所有已存在的实现类。

举个栗子

1753947036034.png

1、定义接口

public interface Animal {
    public void eat();

    default public void drink(){
        System.out.println("动物喝水");
    }
}

2、添加实现类

class Person:Animal {
    override fun eat() {
        Log.e("AnimalTag","吃香的")
    }
}

class Child:Animal{
    override fun eat() {
        Log.e("AnimalTag","小孩需要健康的食物")
    }


    override fun drink() {
        Log.e("AnimalTag","小孩喝牛奶")
    }

}

3、编写测试程序

val person=Person()
val child=Child()

person.eat()
person.drink()
Log.e("AnimalTag","----------------------华丽的分割线--------------------")
child.eat()
child.drink()

4、测试结果

1753947649554.png

二、在kotlin中有类似的关键字嘛?

在kotlin中接口本身就支持提供方法实现(无需特殊关键字),这等价于Java的default方法。 例:

interface AnimalKotlin {
    fun eat()

    fun drink(){
        System.out.println("kotlin 喝水")
    }
}

运行结果如下: 1753948482070.png

最后我们再来说说大疆的那个问题:为什么使用的default关键字,程序依然提示java.lang.AbstractMethodError: abstract method

大疆的开发人员一定是出于好心,想尽量不影响现在的代码,所以才用了default关键字,这个在设计上来说没毛病。 虽然大疆在接口中为 onCameraStreamEnableUpdate 方法提供了默认实现(使用 default修饰),但运行时却找不到该方法的实现。所以才会报错。

为什么会产生这样的原因。

1、编译与运行环境不一致,可能在编译时使用了包含默认方法的新版接口,但是在运行时却使用了旧版本的接口,没有默认方法。(这点可以排除,所有的依赖都是统一管理)

2、我觉得这点的可能性更大,因为大疆启用了代码混淆,可能默认方法,被意外移除。