
本文详解如何在 android java 项目中基于 camerax 和 google ml kit 实现高性能条码(含 qr 码)扫描,同时彻底解决 kotlin 标准库版本冲突(如 duplicate class kotlin.collections.jdk8.collectionsjdk8kt)这一常见构建错误。
本文详解如何在 android java 项目中基于 camerax 和 google ml kit 实现高性能条码(含 qr 码)扫描,同时彻底解决 kotlin 标准库版本冲突(如 duplicate class kotlin.collections.jdk8.collectionsjdk8kt)这一常见构建错误。
在 Android 开发中,使用 CameraX 替代传统 Camera2 API 可显著降低相机集成复杂度;而结合 Google ML Kit 的 Barcode Scanning SDK,则能提供跨平台、离线、高精度且持续更新的条码识别能力。值得注意的是,官方 CameraX 文档虽以 Kotlin 为主,但其 Java API 完全可用——所有用例均可通过标准 Java 8+ 语法无缝调用,无需 Kotlin 运行时依赖。
✅ 正确配置依赖(关键:避免 Kotlin 冲突)
上述 Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt 错误,本质是模块间 Kotlin 标准库版本不一致导致的重复类冲突(例如 kotlin-stdlib:1.8.0 与 kotlin-stdlib-jdk8:1.6.0 同时被拉入)。解决方案如下:
在 app/build.gradle 中显式统一 Kotlin 版本并排除冗余传递依赖:
android {
compileSdk 34
// 确保启用 Java 8+ 特性支持(ML Kit 和 CameraX 所需)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// ✅ CameraX 核心组件(Java 兼容)
def camerax_version = "1.3.0"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:1.3.0" // 替代旧版 preview-view
// ✅ ML Kit 条码扫描(轻量、离线、Java 友好)
implementation 'com.google.mlkit:barcode-scanning:18.4.0'
// ⚠️ 关键:强制统一 Kotlin 版本,防止间接引入多个 kotlin-stdlib-jdk8
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20"
// 并排除可能由其他库引入的旧版 jdk8 模块
configurations.all {
resolutionStrategy {
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.20"
force "org.jetbrains.kotlin:kotlin-stdlib:1.9.20"
}
}
}? 提示:kotlin-stdlib-jdk8 在 Kotlin 1.8+ 已被合并进主 kotlin-stdlib,因此推荐直接使用 kotlin-stdlib:1.9.20 并移除对 -jdk8 的单独引用,避免版本分裂。
立即学习“Java免费学习笔记(深入)”;
? Java 中实现 CameraX + ML Kit 扫描流程(核心代码)
以下为 Activity 中关键 Java 实现逻辑(无 Kotlin 依赖):
// 1. 声明视图与分析器
PreviewView previewView;
ImageAnalysis imageAnalysis;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scanner);
previewView = findViewById(R.id.previewView);
startCamera();
}
private void startCamera() {
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
Log.e("CameraX", "Use case binding failed", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void bindPreview(ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 2. 构建 BarcodeScanner(Java 调用完全等效)
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_13,
Barcode.FORMAT_CODE_128)
.build();
BarcodeScanner scanner = BarcodeScanning.getClient(options);
// 3. ImageAnalysis 用于逐帧处理
imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), image -> {
InputImage inputImage = InputImage.fromMediaImage(
image.getImage(), image.getImageInfo().getRotationDegrees());
scanner.process(inputImage)
.addOnSuccessListener(barcodes -> {
for (Barcode barcode : barcodes) {
String rawValue = barcode.getRawValue();
if (!TextUtils.isEmpty(rawValue)) {
Log.d("Barcode", "Detected: " + rawValue);
// ✅ 在主线程更新 UI(如 Toast 或 TextView)
runOnUiThread(() -> showResult(rawValue));
}
}
})
.addOnFailureListener(e -> Log.w("Barcode", "Scan failed", e))
.addOnCompleteListener(() -> image.close()); // 必须关闭,否则内存泄漏
});
try {
cameraProvider.unbindAll();
cameraProvider.bindToLifecycle(this, UseCaseGroup.Builder()
.addUseCase(preview)
.addUseCase(imageAnalysis)
.build());
} catch (Exception e) {
Log.e("CameraX", "Bind failed", e);
}
}⚠️ 注意事项与最佳实践
- 生命周期绑定:务必使用 bindToLifecycle(this, ...) 而非 bindToLifecycle(this, lifecycleOwner, ...),确保 CameraX 自动随 Activity 生命周期启停;
- 资源释放:Image.close() 必须在 onComplete 或 onFailure 后调用,否则会导致预览卡顿或 OOM;
- 旋转适配:InputImage.fromMediaImage(...) 需传入正确的 rotationDegrees(来自 imageInfo),否则横竖屏下识别率骤降;
- 性能优化:设置 STRATEGY_KEEP_ONLY_LATEST 可防止分析队列积压;避免在 onSuccess 中执行耗时操作(如网络请求),应交由后台线程处理;
-
权限声明:别忘了在 AndroidManifest.xml 中添加:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" />
? 总结
CameraX 与 ML Kit 的组合在 Java 项目中完全可行且稳定。核心在于:
① 通过 resolutionStrategy.force 统一 Kotlin 标准库版本,根治 Duplicate class 冲突;
② 使用 ImageAnalysis + BarcodeScanner 构建低延迟、高准确率的扫描流水线;
③ 严格遵循资源管理规范(关闭 Image、绑定生命周期、处理旋转)。
如需进一步参考,推荐 LearnToDroid 的 QR 扫描实战教程 —— 全 Java 示例,步骤清晰,可直接复用。










