我正在遵循在Android应用程序中使用相机的教程。我得到错误"Permission Denial: cannot use the camera"在运行调试时,在模拟器和物理设备上。我在清单文件中尝试了各种权限。似乎大多数遇到此错误的人都有打字错误,缺少权限或权限不在清单中的正确位置。
这是我的manifest文件:
<?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)
我是应用程序开发的新手,我承认我不擅长调试,但从我看到的其他人的文件来看,我的manifest权限似乎是正确的(而且教程只有4个月的时间)。
谁能告诉我我做错了什么?欢呼,李。
更新:我发现通过调试,它崩溃一旦达到这个方法…
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
…特别是当它运行try语句时。
UPDATE 2:如果我注释掉try/catch语句,应用程序不会崩溃,但预期的结果(即打印"Camera Opened!")不会发生。什么好主意吗?
更新3:我的道歉,我刚刚意识到我上面的编辑是多么愚蠢。它不再崩溃的原因很明显,但至少我现在知道我必须调试我的cameraManager.openCamera
参数。代码是有,如果有人可以看看:)
如果你使用Android 6棉花糖这个问题可能是由新的权限管理引起的。在我的例子中,我通过覆盖下面的活动方法解决了这个问题:
@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)。但对其他人来说,它工作得很好。
这是因为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设置并从那里授予它所需的所有权限。第二个选择是检查在运行时是否需要该权限并请求应用程序所需的权限,或者更好的方法是在运行需要该权限的功能时请求权限。
我们将通过一些代码,当一个活动打开时请求应用程序的权限。但你可以修改代码,在需要使用拍照等功能时请求许可。
另一件事是一些权限,如互联网是由系统自动授予的,因此你不需要请求它。然而,你需要明确地要求一些。
<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_grant){返回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();
}
}
上面的代码首先声明了一个String
类型的列表List<String> listPermissionsNeeded = new ArrayList<>();
。然后检查是否:
if (!hasCameraPermission()){
listPermissionsNeeded.add(Manifest.permission.CAMERA);
}
含义:如果未授予相机权限,则通过添加Manifest.permission.CAMERA
如果我们的permissionsList为空,我们将向用户显示敬酒。然而,如果它确实有一些权限,我们调用函数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,并且granresults将是我们在函数中传递的列表:ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
当一个权限被用户拒绝时,您将在grantResult中返回一个值-1。我在这个函数中所做的就是循环遍历我的grantResults并搜索任何被拒绝的权限,如果有一个被拒绝的权限,我就一次又一次地请求它。这对于用户体验来说是很糟糕的。但是你懂的。
最后我们需要检查所使用的Android版本是否为Android Marshmallow Api Level 23。从我得到的,安卓6.0以下,你可以得到所有的权限,只要你声明它在清单,但从6.0及以上开始,你需要请求权限。这样就可以避免api级别低于该级别时的崩溃。我不是100%在这一点上,我发现这个矛盾的信息,但解决了,有人可以纠正我在那里。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
getApplicationPermissions();
}
希望我没有让你困惑。如果你有任何问题,你可以问或者我可以发送github链接住房,这样你就可以看到所有的代码如何连接,即,如果你需要它。
所有代码都是用Java编写的