Android Kotlin迁移Explicit @Metadata is disallowed报错解决
问题说明
- 现有一个用于降低ViewPager2拖拽灵敏度的Java工具类,实现逻辑为通过反射修改ViewPager2内部RecyclerView的
mTouchSlop字段,完成拖拽灵敏度调整,原始Java代码如下:
@Metadata( mv = {1, 6, 0}, k = 1, d1 = {"\u0000\u001c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\b\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0014\u0010\u0003\u001a\u00020\u0004*\u00020\u00052\b\b\u0002\u0010\u0006\u001a\u00020\u0007¨\u0006\b"}, d2 = {"Lcom/testmepracticetool/toeflsatactexamprep/ui/activities/main/KTest;", "", "()V", "reduceDragSensitivity", "", "Landroidx/viewpager2/widget/ViewPager2;", "f", "", "android_testme.app"} ) public final class ViewPagerSensitivity { public static void reduceDragSensitivity(@NotNull ViewPager2 $this$reduceDragSensitivity, int f) throws NoSuchFieldException, IllegalAccessException { Intrinsics.checkNotNullParameter($this$reduceDragSensitivity, "$this$reduceDragSensitivity"); Field recyclerViewField = ViewPager2.class.getDeclaredField("mRecyclerView"); Intrinsics.checkNotNullExpressionValue(recyclerViewField, "recyclerViewField"); recyclerViewField.setAccessible(true); Object var10000 = recyclerViewField.get($this$reduceDragSensitivity); if (var10000 == null) { throw new NullPointerException("null cannot be cast to non-null type androidx.recyclerview.widget.RecyclerView"); } else { RecyclerView recyclerView = (RecyclerView)var10000; Field touchSlopField = RecyclerView.class.getDeclaredField("mTouchSlop"); Intrinsics.checkNotNullExpressionValue(touchSlopField, "touchSlopField"); touchSlopField.setAccessible(true); var10000 = touchSlopField.get(recyclerView); if (var10000 == null) { throw new NullPointerException("null cannot be cast to non-null type kotlin.Int"); } else { int touchSlop = (Integer)var10000; touchSlopField.set(recyclerView, touchSlop * f); } } } }
- 通过Android Studio自带的Java转Kotlin自动迁移功能转换后,得到如下Kotlin代码,编译时抛出
Explicit @Metadata is disallowed错误:
@Metadata( mv = [1, 6, 0], k = 1, d1 = ["\u0000\u001c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\b\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0014\u0010\u0003\u001a\u00020\u0004*\u00020\u00052\b\b\u0002\u0010\u0006\u001a\u00020\u0007¨\u0006\b"], d2 = ["Lcom/xxx/xxx/ui/activities/main/KTest;", "", "()V", "reduceDragSensitivity", "", "Landroidx/viewpager2/widget/ViewPager2;", "f", "", "android_xxx.app"] ) object ViewPagerSensitivity { @Throws(NoSuchFieldException::class, IllegalAccessException::class) fun reduceDragSensitivity(`$this$reduceDragSensitivity`: ViewPager2, f: Int) { Intrinsics.checkNotNullParameter( `$this$reduceDragSensitivity`, "\$this\$reduceDragSensitivity" ) val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView") Intrinsics.checkNotNullExpressionValue(recyclerViewField, "recyclerViewField") recyclerViewField.isAccessible = true var var10000 = recyclerViewField[`$this$reduceDragSensitivity`] if (var10000 == null) { throw NullPointerException("null cannot be cast to non-null type androidx.recyclerview.widget.RecyclerView") } else { val recyclerView = var10000 as RecyclerView val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop") Intrinsics.checkNotNullExpressionValue(touchSlopField, "touchSlopField") touchSlopField.isAccessible = true var10000 = touchSlopField[recyclerView] if (var10000 == null) { throw NullPointerException("null cannot be cast to non-null type kotlin.Int") } else { val touchSlop = var10000 as Int touchSlopField[recyclerView] = touchSlop * f } } } }
- 当前已知信息:不清楚
@Metadata注解的具体作用,工具类从网络获取,不确定是否可以直接删除该注解;应用正处于整体迁移Kotlin阶段,尚未开展功能测试,需要正确完成该类的Kotlin转换,解决编译错误。
错误原因
@Metadata是Kotlin编译器自动为编译产物添加的内置注解,作用是存储类的Kotlin语法相关元数据,包括函数签名、空值约束、泛型信息等,Kotlin编译器明确禁止开发者在手写源码中手动声明该注解,这就是编译报错的核心原因。
你拿到的原始Java代码本身是反编译Kotlin字节码生成的,反编译工具自动把字节码中携带的@Metadata注解还原到了Java源码中,这个注解在手写Java/Kotlin源码中没有任何作用,直接删除即可,不会影响功能逻辑。
除此之外,自动转换生成的Kotlin代码还携带了大量反编译/自动转换产生的冗余代码:包括编译器自动生成的空值检查逻辑、无意义的临时变量名、不符合Kotlin命名规范的参数名,都可以一并清理。
修正后的可用Kotlin实现
清理冗余代码、删除非法注解后的可正常编译运行的代码如下:
import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 object ViewPagerSensitivity { /** * 降低ViewPager2拖拽灵敏度 * @param viewPager2 目标ViewPager2实例 * @param scaleFactor 触摸阈值缩放系数,值越大触发滑动需要的拖拽距离越长,推荐取值3~5 */ @Throws(NoSuchFieldException::class, IllegalAccessException::class) fun reduceDragSensitivity(viewPager2: ViewPager2, scaleFactor: Int) { // 反射获取ViewPager2内部持有的RecyclerView实例 val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView") recyclerViewField.isAccessible = true val innerRecyclerView = recyclerViewField.get(viewPager2) as RecyclerView // 反射修改RecyclerView的触摸滑动判定阈值 val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop") touchSlopField.isAccessible = true val originalSlop = touchSlopField.get(innerRecyclerView) as Int touchSlopField.set(innerRecyclerView, originalSlop * scaleFactor) } }
使用注意事项
- 调用方式:在ViewPager2初始化完成后直接调用即可,示例:
ViewPagerSensitivity.reduceDragSensitivity(viewPager, 4) - 缩放系数可根据实际交互需求调整,取值范围一般为2~6,数值越大拖拽灵敏度越低
- 该实现依赖反射修改AndroidX组件的私有字段,后续升级ViewPager2、RecyclerView依赖版本后需要做回归测试,防止官方修改内部字段名导致反射调用失败
内容的提问来源于stack exchange,提问作者Diego Perez
相关产品推荐
相关产品推荐

