在Android应用开发中,条码扫描是很多场景下的必备功能,结合CameraX的相机管理能力和ML Kit的条码识别能力,可以在Java项目中快速实现稳定高效的扫描方案。CameraX简化了相机API的调用复杂度,ML Kit则提供了预训练的条码识别模型,无需开发者自行训练即可支持多种类型的条码识别。

环境配置与依赖添加
首先需要在项目的build.gradle文件中添加相关依赖,确保CameraX和ML Kit的库被正确引入。同时需要申请相机权限,保证应用可以正常调用设备相机。
添加Gradle依赖
在app模块的build.gradle的dependencies节点中添加以下依赖:
dependencies {
// CameraX 核心依赖
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:${camerax_version}"
// ML Kit 条码扫描依赖
implementation "com.google.mlkit:barcode-scanning:17.2.0"
}
申请权限
在AndroidManifest.xml中添加相机权限声明:
<uses-permission android:name="android.permission.CAMERA" />
如果应用需要适配Android 6.0及以上系统,还需要在运行时动态申请相机权限,避免权限缺失导致功能无法使用。
布局文件配置
布局中需要包含相机预览的容器和用于显示扫描结果的区域,核心是使用CameraX提供的PreviewView组件作为相机预览的载体。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 相机预览视图 -->
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 扫描结果展示 -->
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#80000000"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:padding="16dp"
android:text="扫描结果将显示在这里" />
</FrameLayout>
核心功能实现
初始化相机与ML Kit扫描器
在Activity中完成相机初始化和ML Kit条码扫描器的配置,绑定相机生命周期并启动预览。
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.*;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.mlkit.vision.barcode.Barcode;
import com.google.mlkit.vision.barcode.BarcodeScanner;
import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
import com.google.mlkit.vision.barcode.BarcodeScanning;
import com.google.mlkit.vision.common.InputImage;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScanActivity extends AppCompatActivity {
private PreviewView previewView;
private TextView tvResult;
private ExecutorService cameraExecutor;
private BarcodeScanner barcodeScanner;
private static final int REQUEST_CAMERA_PERMISSION = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
previewView = findViewById(R.id.previewView);
tvResult = findViewById(R.id.tvResult);
cameraExecutor = Executors.newSingleThreadExecutor();
// 配置ML Kit扫描器,支持所有条码类型
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
.build();
barcodeScanner = BarcodeScanning.getClient(options);
// 检查相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
} else {
startCamera();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startCamera();
} else {
Toast.makeText(this, "需要相机权限才能使用扫描功能", Toast.LENGTH_SHORT).show();
finish();
}
}
}
private void startCamera() {
ProcessCameraProvider.getInstance(this).addListener(() -> {
try {
ProcessCameraProvider cameraProvider = ProcessCameraProvider.getInstance(this).get();
// 配置预览用例
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 配置图像分析用例,用于获取相机帧进行条码识别
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
// 将ImageProxy转换为ML Kit支持的InputImage
InputImage inputImage = InputImage.fromMediaImage(imageProxy.getImage(), imageProxy.getImageInfo().getRotationDegrees());
// 执行条码扫描
barcodeScanner.process(inputImage)
.addOnSuccessListener(barcodes -> {
if (!barcodes.isEmpty()) {
// 取第一个识别到的条码结果
Barcode barcode = barcodes.get(0);
runOnUiThread(() -> tvResult.setText("扫描结果:" + barcode.getRawValue()));
}
})
.addOnFailureListener(e -> {
runOnUiThread(() -> tvResult.setText("扫描失败:" + e.getMessage()));
})
.addOnCompleteListener(task -> imageProxy.close());
});
// 选择后置摄像头
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
// 绑定用例到相机生命周期
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, ContextCompat.getMainExecutor(this));
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraExecutor.shutdown();
barcodeScanner.close();
}
}
功能优化与适配
上述基础实现已经可以完成条码扫描功能,实际使用中可以根据需求做进一步优化:
- 添加扫描框UI,提示用户将条码对准扫描区域,提升用户体验
- 对扫描结果做去重处理,避免同一条码短时间内重复回调
- 根据实际场景调整图像分析的分辨率,平衡识别准确率和性能
- 针对弱光环境添加补光提示,或者调整相机曝光参数提升识别成功率
常见问题说明
在实际开发中可能会遇到以下问题:
- 相机预览黑屏:检查相机权限是否申请成功,或者PreviewView的布局参数是否正确
- 条码识别率低:可以调整图像分析的分辨率,或者限制扫描的条码类型减少干扰
- 内存泄漏:确保在Activity销毁时关闭相机执行器并释放ML Kit扫描器资源
通过CameraX和ML Kit的结合,开发者无需处理复杂的相机底层逻辑和条码识别算法,只需要少量代码即可实现稳定可用的条码扫描功能,适配大部分Android设备。