大疆MSDK5.15.0已经发布了快2个月了,为啥现在才开始适配,因为M400飞机才刚刚回来。 在升级的过程中遇到了一个小问题记录一下。
java.lang.AbstractMethodError: abstract method "void dji.v5.manager.interfaces.ICameraStreamManager$AvailableCameraUpdatedListener.onCameraStreamEnableUpdate(java.util.Map)
升级完毕之后程序闪退,出现了上述这个错误提示。
WTF这是什么鬼东西?这一看就是大疆SDK里面的东西。下面贴出这个错误的完整信息。
可以看到,这个错误是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、可选重写: 实现类可以选择重写该方法,但是不强制要求。
这样设计的目的,主要用于接口的演化,当需要向现有接口添加新方法时,避免破坏所有已存在的实现类。
举个栗子
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、测试结果
二、在kotlin中有类似的关键字嘛?
在kotlin中接口本身就支持提供方法实现(无需特殊关键字),这等价于Java的default方法。 例:
interface AnimalKotlin {
fun eat()
fun drink(){
System.out.println("kotlin 喝水")
}
}
运行结果如下:
最后我们再来说说大疆的那个问题:为什么使用的default关键字,程序依然提示java.lang.AbstractMethodError: abstract method
大疆的开发人员一定是出于好心,想尽量不影响现在的代码,所以才用了default关键字,这个在设计上来说没毛病。 虽然大疆在接口中为 onCameraStreamEnableUpdate 方法提供了默认实现(使用 default修饰),但运行时却找不到该方法的实现。所以才会报错。
为什么会产生这样的原因。
1、编译与运行环境不一致,可能在编译时使用了包含默认方法的新版接口,但是在运行时却使用了旧版本的接口,没有默认方法。(这点可以排除,所有的依赖都是统一管理)
2、我觉得这点的可能性更大,因为大疆启用了代码混淆,可能默认方法,被意外移除。