Android CameraX QR 扫描仪仅在模拟器中工作,不适用于物理设备
我正在为一个学校项目开发一个应用程序,它涉及扫描二维码。我已经使用 Android 模拟器上的 CameraX 库完美地完成了 QR 码扫描,但是当我导出 APK 并将其安装到我的手机 (LG Velvet) 上时,它不会扫描任何代码。相机仍然拉起,我可以看到预览,但它似乎无法识别代码。
这是我的 build.gradle 文件:
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.qrhunter"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
multiDexEnabled true;
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'com.google.firebase:firebase-firestore:21.4.0'
implementation 'com.google.firebase:firebase-auth:20.0.4'
implementation 'com.google.firebase:firebase-storage:20.0.1'
implementation "io.grpc:grpc-okhttp:1.32.2"
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//Camera Dependencies
def camerax_version = "1.1.0-beta01"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
//Zebra Crossing Dependencies
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
}
这也是我的 AndroidManifest.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.qrhunter">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QRHunter">
<activity
android:name=".Login"
android:exported="false" />
<activity
android:name=".Register"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".qrAddObjectPictureActivity"
android:exported="false" />
<activity
android:name=".qrScannedGetInfoActivity"
android:exported="false" />
<activity
android:name=".qrScanCameraActivity"
android:exported="false" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
这是我初始化相机和 QR 分析器的代码
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutionException;
/**
* Activity for running and managing the camera used to scan QR Codes
* Code for camera usage and qr scanning referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class qrScanCameraActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CAMERA = 0;
private PreviewView cameraPreviewView;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private Button qrCodeFoundButton;
private String qrCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
cameraPreviewView = findViewById(R.id.cameraPreviewView);
qrCodeFoundButton = findViewById(R.id.cameraActivity_qrCodeFoundButton);
qrCodeFoundButton.setVisibility(View.INVISIBLE);
qrCodeFoundButton.setOnClickListener(new View.OnClickListener() {
// onClick function for the QR Code Found Button
@Override
public void onClick(View view){
Log.i(qrScanCameraActivity.class.getSimpleName(), "QR Code Found: " + qrCode);
Intent qrScannedIntent = new Intent(qrScanCameraActivity.this, qrScannedGetInfoActivity.class);
qrScannedIntent.putExtra("QR_Content", qrCode);
qrScannedIntent.putExtra("entryFromScanBool", true);
startActivity(qrScannedIntent);
}
});
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
requestCamera();
}
/**
* Requests permission to use the camera in the application
*/
private void requestCamera() {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
startCamera();
}else{
if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)){
ActivityCompat.requestPermissions(qrScanCameraActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}else{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}
}
}
@SuppressLint("MissingSuperCall") //Do not need the super method call unless dealing with fragments
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
if(requestCode == PERMISSION_REQUEST_CAMERA){
if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //if permissions were granted start camera
startCamera();
}else{
Toast.makeText(this, "Camera Permission Denied, please allow camera use in settings", Toast.LENGTH_SHORT).show();
}
}
}
/**
* start the camera and bind the preview correctly
* catch any exceptions
*/
private void startCamera(){
cameraProviderFuture.addListener(() -> {
try{
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindCameraPreview(cameraProvider);
}catch(ExecutionException | InterruptedException e) { //catch any errors when starting the camera
Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}, ContextCompat.getMainExecutor(this));
}
/**
* binds the created camera to the camera preview view in order to allow user to see camera view
* set the analyzer for the camera to detect QR Codes
* @param cameraProvider object representing the Camera
*/
private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider){
cameraPreviewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
preview.setSurfaceProvider(cameraPreviewView.getSurfaceProvider());
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(new Size(1280,720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeListener() {
@Override
public void qrCodeFound(String _qrCode) {
qrCode = _qrCode;
qrCodeFoundButton.setVisibility(View.VISIBLE);
}
@Override
public void qrCodeNotFound() {
qrCodeFoundButton.setVisibility(View.INVISIBLE);
}
}));
Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
}
@Override
public void onBackPressed() {
finish();
}
}
我不确定问题还存在于哪里,因为所有代码都可以完美运行。我的手机在 Android 11、API 30 上运行,我的模拟器也在同一设备上运行(Pixel XL、API 30、Android 11) 对此的任何帮助将不胜感激
编辑:添加分析器代码
/**
* Class for analyzing frames of camera to check for QR Code, and decode QR Code
* Code is referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
private QRCodeListener listener;
/**
* Constructor for QRCodeImageAnalyzer
* @param listener
*/
public QRCodeImageAnalyzer(QRCodeListener listener){
this.listener = listener;
}
@Override
public void analyze(@NonNull ImageProxy image){
if(image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888){
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] imageData = new byte[byteBuffer.capacity()];
byteBuffer.get(imageData);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, image.getWidth(), image.getHeight(),
0, 0, image.getWidth(), image.getHeight(), false);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try{
Result result = new QRCodeMultiReader().decode(binaryBitmap);
listener.qrCodeFound(result.getText());
}catch(FormatException | ChecksumException | NotFoundException e) {
listener.qrCodeNotFound();
}
}
image.close();
}
}
I am developing an app for a school project and it involves scanning QR Codes. I have gotten the QR Code scanning working perfectly using the CameraX library on the Android emulator, but when I export my APK and install it on my phone (LG Velvet) it will not scan any code. The camera still pulls up and I can see the preview, but it seemingly cannot recognize a code.
Here is my build.gradle file:
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.qrhunter"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
multiDexEnabled true;
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'com.google.firebase:firebase-firestore:21.4.0'
implementation 'com.google.firebase:firebase-auth:20.0.4'
implementation 'com.google.firebase:firebase-storage:20.0.1'
implementation "io.grpc:grpc-okhttp:1.32.2"
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//Camera Dependencies
def camerax_version = "1.1.0-beta01"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
//Zebra Crossing Dependencies
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
}
Here is my AndroidManifest.xml file as well
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.qrhunter">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QRHunter">
<activity
android:name=".Login"
android:exported="false" />
<activity
android:name=".Register"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".qrAddObjectPictureActivity"
android:exported="false" />
<activity
android:name=".qrScannedGetInfoActivity"
android:exported="false" />
<activity
android:name=".qrScanCameraActivity"
android:exported="false" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
Here is the code where I am initializing the camera and the QR analyzer
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutionException;
/**
* Activity for running and managing the camera used to scan QR Codes
* Code for camera usage and qr scanning referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class qrScanCameraActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CAMERA = 0;
private PreviewView cameraPreviewView;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private Button qrCodeFoundButton;
private String qrCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
cameraPreviewView = findViewById(R.id.cameraPreviewView);
qrCodeFoundButton = findViewById(R.id.cameraActivity_qrCodeFoundButton);
qrCodeFoundButton.setVisibility(View.INVISIBLE);
qrCodeFoundButton.setOnClickListener(new View.OnClickListener() {
// onClick function for the QR Code Found Button
@Override
public void onClick(View view){
Log.i(qrScanCameraActivity.class.getSimpleName(), "QR Code Found: " + qrCode);
Intent qrScannedIntent = new Intent(qrScanCameraActivity.this, qrScannedGetInfoActivity.class);
qrScannedIntent.putExtra("QR_Content", qrCode);
qrScannedIntent.putExtra("entryFromScanBool", true);
startActivity(qrScannedIntent);
}
});
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
requestCamera();
}
/**
* Requests permission to use the camera in the application
*/
private void requestCamera() {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
startCamera();
}else{
if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)){
ActivityCompat.requestPermissions(qrScanCameraActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}else{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}
}
}
@SuppressLint("MissingSuperCall") //Do not need the super method call unless dealing with fragments
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
if(requestCode == PERMISSION_REQUEST_CAMERA){
if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //if permissions were granted start camera
startCamera();
}else{
Toast.makeText(this, "Camera Permission Denied, please allow camera use in settings", Toast.LENGTH_SHORT).show();
}
}
}
/**
* start the camera and bind the preview correctly
* catch any exceptions
*/
private void startCamera(){
cameraProviderFuture.addListener(() -> {
try{
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindCameraPreview(cameraProvider);
}catch(ExecutionException | InterruptedException e) { //catch any errors when starting the camera
Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}, ContextCompat.getMainExecutor(this));
}
/**
* binds the created camera to the camera preview view in order to allow user to see camera view
* set the analyzer for the camera to detect QR Codes
* @param cameraProvider object representing the Camera
*/
private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider){
cameraPreviewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
preview.setSurfaceProvider(cameraPreviewView.getSurfaceProvider());
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(new Size(1280,720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeListener() {
@Override
public void qrCodeFound(String _qrCode) {
qrCode = _qrCode;
qrCodeFoundButton.setVisibility(View.VISIBLE);
}
@Override
public void qrCodeNotFound() {
qrCodeFoundButton.setVisibility(View.INVISIBLE);
}
}));
Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
}
@Override
public void onBackPressed() {
finish();
}
}
I'm not sure where else the issue could exist, because all of the code work perfectly. My phone is operating on Android 11, API 30, and my emulator is running on the same thing (Pixel XL, API 30, Android 11)
Any help with this would be greatly appreciated
Edit: Added analyzer code
/**
* Class for analyzing frames of camera to check for QR Code, and decode QR Code
* Code is referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
private QRCodeListener listener;
/**
* Constructor for QRCodeImageAnalyzer
* @param listener
*/
public QRCodeImageAnalyzer(QRCodeListener listener){
this.listener = listener;
}
@Override
public void analyze(@NonNull ImageProxy image){
if(image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888){
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] imageData = new byte[byteBuffer.capacity()];
byteBuffer.get(imageData);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, image.getWidth(), image.getHeight(),
0, 0, image.getWidth(), image.getHeight(), false);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try{
Result result = new QRCodeMultiReader().decode(binaryBitmap);
listener.qrCodeFound(result.getText());
}catch(FormatException | ChecksumException | NotFoundException e) {
listener.qrCodeNotFound();
}
}
image.close();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您没有提供 QRCodeImageAnalyzer 实现,也许问题就在那里。
以下代码对我有用。注意:我在调用活动而不是扫描仪活动中进行相机权限检查,因此这里缺少它。
分析器:
所需的依赖项(camerax 和 MLKit):
You didn't provide your QRCodeImageAnalyzer implementation, maybe the issue lies there.
Following code works for me. Note: I do the camera permission check in the calling activity not the scanner activity so it's missing here.
The analyzer:
Required dependencies (camerax an MLKit):