[英]Android “Permission Denial: can't use the camera”
我正在学习有关在 Android 应用程序中使用相机的教程。 在模拟器和物理设备上运行调试时,我收到错误“权限拒绝:无法使用相机” 。 我在清单文件中尝试了各种权限。 似乎大多数遇到此错误的人都有拼写错误、缺少权限或权限不在其清单中的正确位置。
这是我的清单文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.karudo.dbzrealpowerup" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera2" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".DBZHome"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".DBZStartPowerUp"
android:label="@string/title_activity_dbzstart_power_up" >
</activity>
</application>
</manifest>
这是我的活动:
package com.example.karudo.dbzrealpowerup;
import android.app.Activity;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Bundle;
import android.util.Size;
import android.view.Menu;
import android.view.MenuItem;
import android.view.TextureView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class DBZStartPowerUp extends Activity {
private Size mPreviewSize;
private String mCameraId;
private TextureView mTextureView;
private TextureView.SurfaceTextureListener mSurfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
setupCamera(width, height);
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private CameraDevice mCameraDevice;
private CameraDevice.StateCallback mCameraDeviceStateCallback
= new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
Toast.makeText(getApplicationContext(), "Camera Opened!", Toast.LENGTH_SHORT).show();
}
@Override
public void onDisconnected(CameraDevice camera) {
camera.close();
mCameraDevice = null;
}
@Override
public void onError(CameraDevice camera, int error) {
camera.close();
mCameraDevice = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dbzstartpowerup);
mTextureView = (TextureView) findViewById(R.id.dbzCameraPreview);
}
@Override
public void onResume() {
super.onResume();
if(mTextureView.isAvailable()) {
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_dbzstartpowerup, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private void setupCamera(int width, int height) {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for(String cameraId : cameraManager.getCameraIdList()) {
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), width, height);
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private Size getPreferredPreviewSize(Size[] mapSizes, int width, int height) {
List<Size> collectorSizes = new ArrayList<>();
for(Size option : mapSizes) {
if(width > height) {
if(option.getWidth() > width &&
option.getHeight() > height) {
collectorSizes.add(option);
}
} else {
if(option.getWidth() > height &&
option.getHeight() > width) {
collectorSizes.add(option);
}
}
}
if(collectorSizes.size() > 0) {
return Collections.min(collectorSizes, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
}
});
}
return mapSizes[0];
}
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
我的 logcat 中的错误:
10-04 03:15:02.740 961-8780/? E/CameraService﹕ Permission Denial: can't use the camera pid=20601, uid=10059
10-04 03:15:02.741 20601-20601/com.example.karudo.dbzrealpowerup E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.karudo.dbzrealpowerup, PID: 20601
java.lang.SecurityException: Lacking privileges to access camera service
at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108)
at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:336)
at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:324)
at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:454)
at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.openCamera(DBZStartPowerUp.java:163)
at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.access$100(DBZStartPowerUp.java:23)
at com.example.karudo.dbzrealpowerup.DBZStartPowerUp$1.onSurfaceTextureAvailable(DBZStartPowerUp.java:34)
at android.view.TextureView.getHardwareLayer(TextureView.java:368)
at android.view.View.updateDisplayListIfDirty(View.java:15151)
at android.view.View.draw(View.java:15948)
at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
at android.view.View.updateDisplayListIfDirty(View.java:15169)
at android.view.View.draw(View.java:15948)
at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
at android.view.View.updateDisplayListIfDirty(View.java:15169)
at android.view.View.draw(View.java:15948)
at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
at android.view.View.updateDisplayListIfDirty(View.java:15169)
at android.view.View.draw(View.java:15948)
at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
at android.view.View.draw(View.java:16181)
at com.android.internal.policy.PhoneWindow$DecorView.draw(PhoneWindow.java:2690)
at android.view.View.updateDisplayListIfDirty(View.java:15174)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067)
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:5417)
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)
我是应用程序开发的新手,不可否认,我不擅长调试,但从我看到的其他人的文件(以及仅 4 个月大的教程)来看,我的清单权限似乎是正确的。
谁能告诉我我做错了什么?
干杯,李。
更新:我通过调试发现它一到达这个方法就会崩溃......
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
...特别是一旦它运行 try 语句。
更新 2:如果我注释掉 try/catch 语句,应用程序不会崩溃,但预期的结果(即打印“Camera Opened!”)不会发生。 有任何想法吗?
更新 3:我很抱歉,我刚刚意识到我上面的编辑是多么愚蠢。 很明显为什么它不再崩溃,但至少我现在知道我必须调试我的cameraManager.openCamera
参数。 如果有人可以看看,代码就在那里:)
如果您使用的是 Android 6 Marshmallow,则此问题可能是由新的权限管理引起的。 在我的情况下,我通过覆盖以下活动方法解决了这个问题:
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case CAMERA_PERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if(mClss != null) {
Intent intent = new Intent(this, ClassUsingCamera);
startActivity(intent);
}
} else {
Toast.makeText(this, "Please grant camera permission to use the QR Scanner", Toast.LENGTH_SHORT).show();
}
return;
}
}
然后,我使用以下代码启动了需要相机 (ClassUsingCamera) 的活动:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION);
} else {
Intent intent = new Intent(this, ClassUsingCamera);
startActivity(intent);
}
在第一次启动应用程序时,您会看到弹出窗口,要求您授予对相机的访问权限。
另一种选择是使用 PermissionsDispatcher,如下所述https://github.com/hotchemi/PermissionsDispatcher
仅当我在配备 (Android 6.0.0) 的设备或配备 (API 23) 的模拟器上尝试该应用程序时,才会遇到相同的错误。 但与其他人一起工作正常。
那是因为 M 以上的 android 对运行时应用程序的授予权限进行了一些更改。
要做到这一点,请按照以下几个步骤操作。
第一:添加这个静态变量。
private static final int REQUEST_CAMERA_RESULT = 1;
然后修改你的 openCamera 方法
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
Log.v("CAMERA", mCameraId + " " + mCameraDeviceStateCallback);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED){
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback,mBackgroundHandler);
}
else {
if (shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)){
Toast.makeText(this,"No Permission to use the Camera services", Toast.LENGTH_SHORT).show();
}
requestPermissions(new String[] {android.Manifest.permission.CAMERA},REQUEST_CAMERA_RESULT);
}
}
else {
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
随意将 mBackgroundHandler 更改为 null,我只是让它在后台线程中处理相机工作。
然后覆盖这个方法
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode){
case REQUEST_CAMERA_RESULT:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "Cannot run application because camera service permission have not been granted", Toast.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
我可以说几点:
- 如果连接到相机服务失败(例如,如果相机被任何其他应用程序使用或设备管理器禁用了相机或任何应用程序无法释放相机。)
确保您在代码中安全关闭/释放相机。
您是否尝试检查相机是否正在被其他人使用,或者您的策略管理器是否有关闭相机的某些设置?
尝试将此代码放在您的 manifest.xml 文件中。
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission
android:name="android.permission.INTERNET"
android:maxSdkVersion="19" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_DEBUG_APP"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
您对权限的第一个选项是转到该特定应用程序的 android 设置,并从那里授予它所需的所有权限。 您的第二个选择是检查在运行期间是否需要该权限并请求应用程序所需的权限,或者更好的方法是在运行需要该权限的功能时请求该权限。
好吧,通过一些代码,在活动打开时请求应用程序权限。 但是,当需要拍摄相机等功能时,您可以修改该代码以请求许可。
另一件事是,某些权限(例如 Internet)是由系统自动授予的,因此您无需请求。 但是,您需要明确要求一些。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
上面显示了在 AndroidManifest 中打开应用程序标签正上方声明的权限。
public boolean hasFineLocationPermission(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
public boolean hasExternalStoragePermission(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
return true;
}
else{
return false;
}
}
public boolean hasLocationForegroundService(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.FOREGROUND_SERVICE) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
public boolean hasReadPhoneStatePermission(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
public boolean hasCameraPermission(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
public boolean hasAccessNetworkState(){
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
我们上面有一些函数。 所有功能所做的基本上是检查是否授予了某个权限。 如果返回 true,则无需请求该许可。 如果不返回 false,那么请稍后请求该许可。 例如:
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){ return true; }
这会检查我们是否有权访问相机,如果我们访问它,则返回 True,否则返回 false,如函数 hasCameraPermission() 所示
现在我们可以使用以下函数检查是否授予了所有权限:
public void getApplicationPermissions(){
List<String> listPermissionsNeeded = new ArrayList<>();
if (!hasFineLocationPermission()){
listPermissionsNeeded.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
if (!hasExternalStoragePermission()){
listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if (!hasLocationForegroundService()){
listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (!hasReadPhoneStatePermission()){
listPermissionsNeeded.add(Manifest.permission.READ_PHONE_STATE);
}
if (!hasCameraPermission()){
listPermissionsNeeded.add(Manifest.permission.CAMERA);
}
if (!hasAccessNetworkState()){
listPermissionsNeeded.add(Manifest.permission.ACCESS_NETWORK_STATE);
}
Log.i("listPermissionsNeeded", String.valueOf(listPermissionsNeeded));
if (!listPermissionsNeeded.isEmpty()){
ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
}
else{
Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
}
}
上面的代码首先声明了一个列表List<String> listPermissionsNeeded = new ArrayList<>();
字符串类型
然后检查是否:
if (!hasCameraPermission()){
listPermissionsNeeded.add(Manifest.permission.CAMERA);
}
含义:如果未授予相机权限,则通过添加Manifest.permission.CAMERA
将该权限添加到列表中
如果我们的权限列表为空,我们将只向用户展示一个祝酒词。 但是,如果它确实有一些权限,我们调用函数ActivityCompat.requestPermissions()
传入上下文、我们的列表和一个标识符,在这种情况下为 0:
if (!listPermissionsNeeded.isEmpty()){
ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
}
else{
Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
}
当该 requestPermissions 被调用时,另一个 android 方法被调用,即onRequestPermissionsResult()
。 我们实现如下:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode=0){
for (int i=0;i<grantResults.length;i++){
if (grantResults[i] == -1){
getApplicationPermissions();
}
}
}
}
请求代码将为 0,grantResults 将是我们在函数中传递的列表: ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
当用户拒绝某个权限时,您将在 grantResult 中得到一个 -1 的值。 我在该函数中所做的只是遍历我的 grantResults 并搜索任何已被拒绝的权限,如果有已被拒绝的权限,我会一次又一次地请求它。 这对用户体验来说太糟糕了。 但你明白了。
最后我们需要检查一下所使用的Android版本是否是Android Marshmallow Api Level 23。根据我得到的,在android 6.0以下只要在Manifest中声明就可以得到所有权限,但是从6.0开始就需要请求权限。 通过这种方式,您可以避免低于该级别的 api 级别崩溃。 不是 100% 对此,我发现了有关此的相互矛盾的信息,但解决了这个问题,有人可以在那里纠正我。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
getApplicationPermissions();
}
希望我没有让你感到困惑。 如果你有任何问题,你可以问,或者我可以发送 github 链接住房,以便你可以看到所有代码如何连接,即如果你需要它。
所有代码都是用Java编写的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.