使用相机扫描二维码应用程序崩溃 |可能的修复?
Scanning QR code using camera crashes application | possible fix?
我正在尝试使用设备相机和移动视觉 API 扫描二维码。为此,我创建了一个 SurfaceView
并将图像流提取到 SurfaceView
并启用了一个 BarcodeDetector
,然后是读取检测到的二维码所需的方法。这是 Activity:
的代码
QrScanActivity.class
public class QrScanActivity extends AppCompatActivity
{
private SurfaceView cameraView;
private TextView barcodeInfo;
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private static final String TAG = "QR_ACTIVITY";
private static final int REQUEST_CAMERA = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qr_scan);
cameraView = (SurfaceView) findViewById(R.id.camera_view);
barcodeInfo = (TextView) findViewById(R.id.code_info);
barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeInfo.post(new Runnable() { // Use the post method of the TextView
public void run() {
barcodeInfo.setText( // Update the TextView
barcodes.valueAt(0).displayValue
);
}
});
}
}
});
}
private void requestCameraPermission() {
Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");
// BEGIN_INCLUDE(camera_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
{
Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(QrScanActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA)
{
Log.i(TAG, "Received response for Camera permission request.");
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
Snackbar.make(findViewById(android.R.id.content), R.string.permision_available_camera,
Snackbar.LENGTH_SHORT).show();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
Snackbar.LENGTH_SHORT).show();
}
}
else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
activity_qr_scan.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_qr_scan"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:layout_width="400dp"
android:layout_height="280dp"
android:layout_marginTop="30dp"
android:id="@+id/camera_view"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/code_info"
android:layout_below="@+id/camera_view"
android:textSize="20sp"
android:layout_marginTop="30dp"
android:layout_centerHorizontal="true"
android:text="Nothing to read."
/>
</RelativeLayout>
app为运行时得到的logcat:
Logcat
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.shreybank.shrey, PID: 12044
java.lang.RuntimeException: Fail to connect to camera service
at android.hardware.Camera.(Camera.java:518)
at android.hardware.Camera.open(Camera.java:360)
at com.google.android.gms.vision.CameraSource.zzBZ(Unknown Source)
at com.google.android.gms.vision.CameraSource.start(Unknown Source)
at com.shreybank.shrey.activities.QrScanActivity.surfaceCreated(QrScanActivity.java:64)
at android.view.SurfaceView.updateWindow(SurfaceView.java:583)
at android.view.SurfaceView.onPreDraw(SurfaceView.java:177)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:944)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2055)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5466)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
您必须设置 运行 时间权限才能解决此问题。在开始 QrScanActivity
之前你已经调用了这个。
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_READ_CAMERA);
}
else {
// Already grant Camera permission. Now call your QR scan Activity
}
}else {
// call your QR scan Activity
}
当用户授予权限后,您可以打开 QrScanActivity
onRequestPermissionsResult 方法。
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_READ_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission has granted . Call the QR scan Activity
} else {
// permission denied
}
return;
}
}
}
更新您的 QrScanActivity,将您的相机源代码创建代码移至 initCameraSource 方法,检查 CAMERA 的权限,如果获得授权则调用 initCameraSource 方法,否则请求权限。授予权限后,从 onRequestPermissionsResult 调用 initCameraSource。
public class QrScanActivity extends AppCompatActivity
{
private SurfaceView cameraView;
private TextView barcodeInfo;
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private static final String TAG = "QR_ACTIVITY";
private static final int REQUEST_CAMERA = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qr_scan);
int permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA);
if(permissionCheck == PackageManager.PERMISSION_GRANTED)
initCameraSource
else
requestCameraPermission();
}
private void requestCameraPermission() {
Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");
// BEGIN_INCLUDE(camera_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
{
Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(QrScanActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA)
{
Log.i(TAG, "Received response for Camera permission request.");
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
initCameraSource();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
Snackbar.LENGTH_SHORT).show();
}
}
else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void initCameraSource(){
cameraView = (SurfaceView) findViewById(R.id.camera_view);
barcodeInfo = (TextView) findViewById(R.id.code_info);
barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeInfo.post(new Runnable() { // Use the post method of the TextView
public void run() {
barcodeInfo.setText( // Update the TextView
barcodes.valueAt(0).displayValue
);
}
});
}
}
});
}
}
我正在尝试使用设备相机和移动视觉 API 扫描二维码。为此,我创建了一个 SurfaceView
并将图像流提取到 SurfaceView
并启用了一个 BarcodeDetector
,然后是读取检测到的二维码所需的方法。这是 Activity:
QrScanActivity.class
public class QrScanActivity extends AppCompatActivity
{
private SurfaceView cameraView;
private TextView barcodeInfo;
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private static final String TAG = "QR_ACTIVITY";
private static final int REQUEST_CAMERA = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qr_scan);
cameraView = (SurfaceView) findViewById(R.id.camera_view);
barcodeInfo = (TextView) findViewById(R.id.code_info);
barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeInfo.post(new Runnable() { // Use the post method of the TextView
public void run() {
barcodeInfo.setText( // Update the TextView
barcodes.valueAt(0).displayValue
);
}
});
}
}
});
}
private void requestCameraPermission() {
Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");
// BEGIN_INCLUDE(camera_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
{
Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(QrScanActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA)
{
Log.i(TAG, "Received response for Camera permission request.");
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
Snackbar.make(findViewById(android.R.id.content), R.string.permision_available_camera,
Snackbar.LENGTH_SHORT).show();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
Snackbar.LENGTH_SHORT).show();
}
}
else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
activity_qr_scan.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_qr_scan"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:layout_width="400dp"
android:layout_height="280dp"
android:layout_marginTop="30dp"
android:id="@+id/camera_view"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/code_info"
android:layout_below="@+id/camera_view"
android:textSize="20sp"
android:layout_marginTop="30dp"
android:layout_centerHorizontal="true"
android:text="Nothing to read."
/>
</RelativeLayout>
app为运行时得到的logcat:
Logcat
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.shreybank.shrey, PID: 12044 java.lang.RuntimeException: Fail to connect to camera service at android.hardware.Camera.(Camera.java:518) at android.hardware.Camera.open(Camera.java:360) at com.google.android.gms.vision.CameraSource.zzBZ(Unknown Source) at com.google.android.gms.vision.CameraSource.start(Unknown Source) at com.shreybank.shrey.activities.QrScanActivity.surfaceCreated(QrScanActivity.java:64) at android.view.SurfaceView.updateWindow(SurfaceView.java:583) at android.view.SurfaceView.onPreDraw(SurfaceView.java:177) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:944) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2055) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5466) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
您必须设置 运行 时间权限才能解决此问题。在开始 QrScanActivity
之前你已经调用了这个。
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_READ_CAMERA);
}
else {
// Already grant Camera permission. Now call your QR scan Activity
}
}else {
// call your QR scan Activity
}
当用户授予权限后,您可以打开 QrScanActivity
onRequestPermissionsResult 方法。
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_READ_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission has granted . Call the QR scan Activity
} else {
// permission denied
}
return;
}
}
}
更新您的 QrScanActivity,将您的相机源代码创建代码移至 initCameraSource 方法,检查 CAMERA 的权限,如果获得授权则调用 initCameraSource 方法,否则请求权限。授予权限后,从 onRequestPermissionsResult 调用 initCameraSource。
public class QrScanActivity extends AppCompatActivity
{
private SurfaceView cameraView;
private TextView barcodeInfo;
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
private static final String TAG = "QR_ACTIVITY";
private static final int REQUEST_CAMERA = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qr_scan);
int permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA);
if(permissionCheck == PackageManager.PERMISSION_GRANTED)
initCameraSource
else
requestCameraPermission();
}
private void requestCameraPermission() {
Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");
// BEGIN_INCLUDE(camera_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
{
Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(QrScanActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA)
{
Log.i(TAG, "Received response for Camera permission request.");
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
initCameraSource();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
Snackbar.LENGTH_SHORT).show();
}
}
else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void initCameraSource(){
cameraView = (SurfaceView) findViewById(R.id.camera_view);
barcodeInfo = (TextView) findViewById(R.id.code_info);
barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(640, 480)
.build();
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
}
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodeInfo.post(new Runnable() { // Use the post method of the TextView
public void run() {
barcodeInfo.setText( // Update the TextView
barcodes.valueAt(0).displayValue
);
}
});
}
}
});
}
}