背景
Flutter-Android混合开发,远程引用了jcenter上的第三方aar包。这个第三方aar包内部又依赖了jar包,jar包里有个接口类EglBase,里面有个静态方法create:
public interface EglBase {
static EglBase create(@Nullable EglBase.Context sharedContext, int[] configAttributes) {
return (EglBase)(!EglBase14.isEGL14Supported() || sharedContext != null && !(sharedContext instanceof video.pano.EglBase14.Context) ? new EglBase10((video.pano.EglBase10.Context)sharedContext, configAttributes) : new EglBase14((video.pano.EglBase14.Context)sharedContext, configAttributes));
}
}
运行的时候,提示:
W/System.err( 6150): Caused by: java.lang.NoSuchMethodError: No static method create()Lvideo/pano/EglBase; in class Lvideo/pano/EglBase; or its super classes (declaration of 'video.pano.EglBase' appears in /data/app/xxx-c7mnoReovPKpIyPmc2_R4g==/base.apk)
另外:
1.在纯android工程里,使用远程依赖该第三方aar/本地依赖都运行正常;
2.将该aar包从jcenter上下载后,放在flutter-android工程的libs目录下,gradle本地依赖使用,运行正常。
分析
对比运行正常(本地依赖aar)和运行崩溃(jcenter远程依赖)的apk解压后的EglBase.class文件:
运行正常.png运行crash.png
可以发现,运行异常的EglBase.class文件里多了报错的静态方法(为什么反而是多了这两个静态方法?没搞明白)。
说明Flutter-Android工程在远程依赖第三方库和本地直接依赖第三方库两种情况下打包apk的方式是有区别的。
在一篇文章里 https://github.com/xamarin/xamarin-android/issues/4574 ,有提到类似错误的原因:
The problem is caused by "desugaring", which "moves things around." In particular, if you run dexdump classes.dex
on the classes.dex
contained within the .apk
, the only create
method with a "type" of ()Lorg/webrtc/EglBase;
is declared in Lorg/webrtc/EglBase$-CC;
:
$ $HOME/android-toolchain/sdk/build-tools/29.0.2/dexdump classes.dex
…
Class #1481 -
Class descriptor : 'Lorg/webrtc/EglBase$-CC;'
Access flags : 0x1011 (PUBLIC FINAL SYNTHETIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
…
#1 : (in Lorg/webrtc/EglBase$-CC;)
name : 'create'
type : '()Lorg/webrtc/EglBase;'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 2
ins : 0
outs : 2
insns size : 8 16-bit code units
catches : (none)
positions :
0x0000 line=170
locals :
EglBase$-CC
is not a class present within the original classes.jar
. Equally important, our code is looking for the create
method on org/webrtc/EglBase
, not org/webrtc/EglBase$-CC;
, and while org/webrtc/EglBase
exists in classes.dex
, it does not contain the static create
method.
This is why the Java.Lang.NoSuchMethodError
is thrown.
Disabling desugar allow the app to run, but requires -- as you note in step (2) -- that the Minimum Android version to API Level be 24 or higher. 26 in this case, actually, to avoid a different build error:
D8 : error : Invoke-customs are only supported starting with Android O (--min-api 26)
I'm not entirely sure how to address this on our end. We could skip binding static interface methods/etc. unless building against Mono.Android.dll
v7.0, which would avoid the NoSuchMethodException
… by removing the method entirely. (This isn't a "fix.") I'm also not sure it would actually work, because all Mono.Android.dll
s have an assembly version of 0.0.0.0, so we'd have to "somehow" ensure that the v7.0 bit makes it into the NuGet package? (I don't understand how that process works.)
The only other fix I can think of is to IL-rewrite the binding assembly to replace appropriate instances of Lorg/webrtc/EglBase;
with Lorg/webrtc/EglBase$-CC;
, which could be done but could be "brittle".
这里就不再翻译了,大意是desuagaring 脱糖处理会把原本在EglBase里的静态方法挪到EglBase$-CC这个class文件,导致在EglBase里找不到原本属于他的方法。不过这貌似也解释不通?这块知识比较匮乏,目前我也只能探索到这一步了。
解决方案
在 gradle.properties 中添加
android.enableDexingArtifactTransform.desugaring=false
这个解决方案,我前前后后断断续续找了两星期,百度google无果,最后是在Bing上搜索到的。
造成上述bug的原因,应该是跟Android Studio 3.0+ 新Dex编译器D8 Desugar R8有关,后续有机会继续研究。
https://blog.csdn.net/jamin0107/article/details/81123154
网友评论