Closed ggggxiaolong closed 6 years ago
看上去kotlin代码经过编译后在Span
类里面生成了两个辅助方法,那个d
混淆之前是getLength
而第二个方法正好重载了其父类SpannableStringBuilder
的length()
方法,所以SpannableStringBuilder
内部调用时就调用到了那个重载的length()
但是我这边目前看来只混淆了getLength()
方法,运行时没有问题,你这边能看一下是不是有个方法被shrink掉了,或者你给我看一哈你的proguard文件,我试试看能不能复现
# http://drakeet.me/android-advanced-proguard-and-security/
-ignorewarnings
-keep public class * extends android.os.Binder
-keepclassmembers enum * {
**[] $VALUES;
public *;
}
# v7
-keep public class android.support.v7.widget.** { *; }
-keep public class android.support.v7.internal.widget.** { *; }
-keep public class android.support.v7.internal.view.menu.** { *; }
-keep public class * extends android.support.v4.view.ActionProvider {
public <init>(android.content.Context);
}
# log
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int d(...);
public static int w(...);
public static int v(...);
public static int i(...);
}
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keep class org.ocpsoft.prettytime.** { *; }
# Fabric
#-keep public class * extends java.lang.Exception
#-keepattributes *Annotation*
#-keep class com.crashlytics.** { *; }
#-dontwarn com.crashlytics.**
#-keep class io.fabric.** { *; }
-renamesourcefileattribute Proguard
-keepattributes SourceFile,LineNumberTable
# 混淆字典
#-obfuscationdictionary dictionary-drakeet.txt
#-classobfuscationdictionary dictionary-drakeet.txt
#-packageobfuscationdictionary dictionary-drakeet.txt
# 把代码以及所使用到的各种第三方库代码统统移动到同一个包下
-repackageclasses 'com.mrtan.data'
-allowaccessmodification
#############################################################################################
######################## 以上通用 ##################################
#############################################################################################
# umeng
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
# ARoute
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
#Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#okhttp
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
#bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
#alipush
-keepclasseswithmembernames class ** {
native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-keep class com.ut.** {*;}
-dontwarn com.ut.**
-keep class com.ta.** {*;}
-dontwarn com.ta.**
-keep class anet.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
#update
-keep class android.content.pm.** { *; }
# BottomNavigationViewEx
-keep public class android.support.design.widget.BottomNavigationView { *; }
-keep public class android.support.design.internal.BottomNavigationMenuView { *; }
-keep public class android.support.design.internal.BottomNavigationPresenter { *; }
-keep public class android.support.design.internal.BottomNavigationItemView { *; }
这个是混淆文件,后来我把整个span包 keep 之后还是会报错.有点奇怪
我用了你这个混淆文件,还是不能复现啊- -反编译出来看上去也一切正常
你试试看能不能反编译找到Span
类,看一下调用环节里面哪个方法缺失了
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val news = arguments!!.getParcelable<News>(KEY_NEWS)!!
mDataBinding.news = news
//获取所有附件
val attach = news.attachments()
if (attach.isNotEmpty()) {
val span = span{
span("附件")
}
attach.forEach {
span.append("\n")
val url = it.url
span.span(it.name){
spans.add(NewsSpan(url))
}
}
mDataBinding.attachment.movementMethod = LinkMovementMethod.getInstance()
mDataBinding.attachment.text = span
}
}
是不是我代码写的有问题 哈?
对了 我使用了 R8 混淆 gradle.properties
org.gradle.jvmargs=-Xmx4096m
# gradle4.6 添加kotlin cache
org.gradle.caching=true
# AGP 3.2.0-alpha06 R8 混淆配置
android.enableR8 = true
你方不方便把混淆后的apk发给我,我邮箱2dxgujun@gmail.com
你上面的代码我看没什么问题,不过你可以换种写法
val text = span {
+"附件"
attach.forEach {
span("\n${it.name}") {
addSpan(NewsSpan(it.url))
}
}
}
发过去了,我反编译之后发现 length() 方法并没有被重写
你的mapping里面缺少这些方法,而且反编译出来也缺少这些方法
这是正常的反编译后的结果:
我用了你的proguard配置也启用了R8,都是一切正常的,这是我的mapping:
me.gujun.android.span.Span -> com.mrtan.data.hp:
java.lang.CharSequence text -> b
java.lang.Integer textColor -> c
java.lang.Integer backgroundColor -> d
java.lang.Integer textSize -> e
java.lang.String fontFamily -> f
android.graphics.Typeface typeface -> g
java.lang.String textStyle -> h
java.lang.String alignment -> i
java.lang.String textDecorationLine -> j
java.lang.Integer lineSpacing -> k
java.lang.Integer paddingTop -> l
java.lang.Integer paddingBottom -> m
java.lang.Integer verticalPadding -> n
kotlin.jvm.functions.Function0 onClick -> o
java.util.ArrayList spans -> p
me.gujun.android.span.Span style -> q
me.gujun.android.span.Span parent -> r
me.gujun.android.span.Span EMPTY_STYLE -> s
me.gujun.android.span.Span globalStyle -> t
me.gujun.android.span.Span$Companion Companion -> a
37:37:void setText(java.lang.CharSequence) -> a
39:39:void setTextColor(java.lang.Integer) -> a
41:41:void setBackgroundColor(java.lang.Integer) -> b
43:43:void setTextSize(java.lang.Integer) -> c
45:45:void setFontFamily(java.lang.String) -> a
47:47:void setTypeface(android.graphics.Typeface) -> a
49:49:void setTextStyle(java.lang.String) -> b
51:51:void setAlignment(java.lang.String) -> c
53:53:void setTextDecorationLine(java.lang.String) -> d
55:55:void setLineSpacing(java.lang.Integer) -> d
61:61:void setVerticalPadding(java.lang.Integer) -> e
63:63:kotlin.jvm.functions.Function0 getOnClick() -> a
63:63:void setOnClick(kotlin.jvm.functions.Function0) -> a
65:65:java.util.ArrayList getSpans() -> b
67:67:void setStyle(me.gujun.android.span.Span) -> a
70:111:void buildCharacterStyle(java.util.ArrayList) -> a
114:140:void buildParagraphStyle(java.util.ArrayList) -> b
143:144:void prebuild() -> g
147:318:me.gujun.android.span.Span build() -> c
172:212:void override(me.gujun.android.span.Span) -> b
215:218:java.lang.CharSequence unaryPlus(java.lang.CharSequence) -> b
29:67:void <init>(me.gujun.android.span.Span) -> <init>
29:29:void <init>(me.gujun.android.span.Span,int,kotlin.jvm.internal.DefaultConstructorMarker) -> <init>
void <init>() -> <init>
32:34:void <clinit>() -> <clinit>
29:29:char get(int) -> a
29:29:char charAt(int) -> charAt
29:29:int getLength() -> d
29:29:int length() -> length
29:29:me.gujun.android.span.Span access$getEMPTY_STYLE$cp() -> e
29:29:me.gujun.android.span.Span access$getGlobalStyle$cp() -> f
29:29:void access$setGlobalStyle$cp(me.gujun.android.span.Span) -> c
按理来说proguard不会把那几个方法给shrink掉,不排除是kotlin编译器的问题,你不跑proguard就没问题吗?我的kotlin版本时1.2.20,你可以试试看
我 clone 的你的项目 修改了sample的代码 可以复现问题(需要修改maven地址) https://github.com/ggggxiaolong/Spannable-In-Kotlin
修改的内容 https://github.com/ggggxiaolong/Spannable-In-Kotlin/commit/098396d3f3d63e907a6bd0e1f4ab3b48b542b65e
应该是R8的问题,开了R8之后这些方法就没了,我明天试试看写个简单点的demo去复现一下
又一个疑问想问你一下,你的 Sapn 类并没有重写父类的 length 方法。为什么 kotlin 还要生成一个代理方法? Sapn 类的父类存在 length 方法 而且 Span 也没有重写 length 方法为什么会报 length 方法找不到的错误?有点困惑
kotlin为了让java.lang.CharSequence
支持下标访问和length
属性访问,对java.lang.CharSequence
做了一些特殊处理,用kotlin.CharSequence
替换掉了接口,因为下标访问和属性访问都是需要Java这边的方法满足一定的条件(get(int)和getXXX()),所以kotlin编译后会生成这些synthetic方法来提供这种支持
另外,这个问题已经有结论了,是R8的BUG,已经给官方提Issue了,https://issuetracker.google.com/issues/76383728
kotlin编译器对CharSequence
做的优化正好掉进了这个坑,R8会把满足特定条件的方法认为是visibility bridges给优化掉,例如下面的代码,通过asm修改bridge
方法的access flags,R8就会把bridge
方法给删掉,建议你先不要混淆这个lib或者把proguard-rules.pro里面的-allowaccessmodification
给注释掉
public class ClassA {
int method() {
return 0;
}
}
public class ClassB extends ClassA {
int bridge() {
return super.method();
}
int method() {
return bridge();
}
}
https://r8-review.googlesource.com/c/r8/+/19264 Google已经修复这个问题了,你可以再混淆试试看
java.lang.NoSuchMethodError
我自定义了一个类,是它的问题吗