Android API21下DateTimeFormatter崩溃的替代实现方案咨询
你遇到的java.lang.NoClassDefFoundError根因是:java.time.format.DateTimeFormatter、java.time.LocalTime等java.time包下的类,是Android 8.0(API 26)才在系统层内置支持的。你当前适配的最低版本是API 21,当应用运行在低于API26的设备上时,系统类加载器无法找到这些类,就会直接触发崩溃。
你之前首次构建能正常运行,大概率是当时的测试设备版本在API26以上,没有覆盖到低版本运行场景;而代码上的@RequiresApi(api = Build.VERSION_CODES.O)注解仅用于编译期提示版本要求,不会在低版本设备上自动提供缺失的类实现,只要代码逻辑走到加载这些类的分支,必然崩溃。
方法1:开启Android官方核心库脱糖(推荐,无需改动原有时间API代码)
这是Google官方针对低版本系统兼容Java 8+时间API推出的原生方案,开启后打包流程会自动把低版本缺失的java.time类编译进APK,完全兼容API21及以上设备,原有基于DateTimeFormatter、LocalTime的代码不需要做任何修改。
配置步骤:
- 在模块级
build.gradle的android配置块中,添加Java版本和脱糖开关配置:
android { // 保留原有其他配置 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 // 开启核心库脱糖 coreLibraryDesugaringEnabled true } }
- 在同一模块的
dependencies配置块中,引入官方脱糖依赖:coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
配置完成后重新构建即可解决类缺失的问题。
方法2:替换代码为兼容API21的传统时间API实现
如果不想开启脱糖功能,可以把代码中用到高版本java.time的部分,替换为Android从API1就支持的SimpleDateFormat、Calendar类实现,适配性更好。同时改造过程会替换你原逻辑中已废弃的Date.setHours()、Date.setMinutes()方法,消除编译警告。
改造后的完整方法代码(和原有业务逻辑完全对齐):
public boolean checkTimeAgainstCurrentTime(String time) throws ParseException { boolean isTime = false; // 用兼容低版本的SimpleDateFormat解析"h:mma"格式的输入时间 SimpleDateFormat parser = new SimpleDateFormat("h:mma", Locale.ENGLISH); Date targetTime = parser.parse(time); // 提取目标时间的时、分信息 Calendar targetCal = Calendar.getInstance(); targetCal.setTime(targetTime); // 构造同日期下的目标对比时间 Calendar compareCal = Calendar.getInstance(); compareCal.set(Calendar.HOUR_OF_DAY, targetCal.get(Calendar.HOUR_OF_DAY)); compareCal.set(Calendar.MINUTE, targetCal.get(Calendar.MINUTE)); compareCal.set(Calendar.SECOND, 0); compareCal.set(Calendar.MILLISECOND, 0); // 保留你原有的加8小时时区偏移逻辑 Calendar currentCal = Calendar.getInstance(); currentCal.add(Calendar.HOUR_OF_DAY, 8); if (currentCal.after(compareCal)) { isTime = true; } // 逻辑保持不变:返回true代表时间已过期 return isTime; }
这个方案完全不依赖高版本系统类,在API21及以上的所有设备上都能正常运行。
内容的提问来源于stack exchange,提问作者Danial Hamidon

