Android“权限拒绝:无法使用相机”

时间:2015-10-04 03:37:48

标签: android android-camera android-permissions android-camera2

我正在关注在Android应用中使用相机的教程。我在模拟器和物理设备上运行调试时收到错误“Permission Denial:无法使用相机”。我在清单文件中尝试了各种权限。似乎大多数有此错误的人都有错字,缺少权限或权限不在其清单中的正确位置。

这是我的清单文件:

<?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参数。如果有人可以看看,代码就在那里:)

5 个答案:

答案 0 :(得分:10)

如果您使用的是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

答案 1 :(得分:3)

只有当我在带有(Android 6.0.0)或带有(API 23)的模拟器的设备上试用该应用程序时才会出现相同的错误。 但与其他人一起工作正常。

这是因为Android上面的M对运行时应用程序的授予权限进行了一些更改。

要做到这一点,请按照以下几个步骤进行操作。

第一次:  添加此静态变量。

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;
    }
}

答案 2 :(得分:1)

我可以说几点:

  
      
  1. 如果与相机服务的连接失败(例如,如果相机被任何其他应用程序使用或设备管理器已禁用   相机或任何应用程序无法释放   相机。)
  2.   
  1. 确保在代码中安全关闭/释放相机。

  2. 您是否尝试过检查相机是否被其他内容使用,或者您的保单管理员是否有关闭相机的设置?

答案 3 :(得分:0)

尝试将此代码放在您的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" />

答案 4 :(得分:0)

关于权限的第一个选项是转到该特定应用程序的 android 设置,并从那里授予它所需的所有权限。您的第二个选择是检查在运行期间是否需要该权限并请求应用所需的权限,或者更好的方法是在需要该权限的功能运行时请求该权限。

好吧,请查看一些在 Activity 打开时请求应用权限的代码。但是您可以修改该代码以在需要拍摄相机等功能时请求许可。

另一件事是某些权限(例如 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){ 返回真; }

这会检查我们是否可以访问相机,如果我们可以访问,则返回 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 编写的。