Android M Camera Intent + 权限错误?
Android M Camera Intent + permission bug?
我正在尝试让我的应用程序为新的 Android M 权限更改做好准备,但发现了一些奇怪的行为。我的应用程序使用相机意图机制来允许用户从相机获取照片。但在另一个 activity 中,需要使用具有相机权限的相机本身(因为库依赖性 card.io 需要这个)。
但是 activity 中的 M 只需要一个相机意图,当我尝试启动相机意图时,我看到了以下崩溃(如果我从清单中删除相机权限,则不会发生这种情况),
> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0
> {act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras)} from uid 10098 on display 0 09-25
> 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting
> Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: Unable to launch as uid 10098 package
> com.example.me.mycamerselectapp, while running in android:ui 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:
> java.lang.SecurityException: Permission Denial: starting Intent {
> act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity$ChooserRowAdapter.onClick(ChooserActivity.java:990)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.view.View.performClick(View.java:5198) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityThread.main(ActivityThread.java:5417) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25
> 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25
> 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown
> buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript:
> 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/?
> E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240
这是 Android M 的已知问题吗?更重要的是我该如何解决这个问题?
在清单中我有以下内容,
<uses-permission android:name="android.permission.CAMERA" />
这是我用来让用户用相机点击图片的代码and/or select 图片
public static Intent openImageIntent(Context context, Uri cameraOutputFile) {
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = context.getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile);
cameraIntents.add(intent);
}
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
我在 activity 中点击按钮调用 openImageIntent()
。当我在我的应用程序中没有 CAMERA 权限时,它工作正常,但添加后我得到上面发布的异常。
@Override
public void onClick(View v) {
Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this));
try {
startActivityForResult(picCaptureIntenet, 100);
} catch(Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
如果您使用的是AndroidM权限模型,您首先需要在运行时检查该应用程序是否具有该权限,并且必须在运行时提示用户该权限。
您在清单中定义的权限不会在安装时自动授予。
if (checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA},
MY_REQUEST_CODE);
}
MY_REQUEST_CODE 是您可以定义的静态常量,将再次用于 requestPermission 对话框回调。
您将需要对话结果的回调:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Now user should be able to use camera
}
else {
// Your app will not have this permission. Turn off all functions
// that require this permission or it will force close like your
// original question
}
}
}
编辑
从堆栈跟踪中读取,Google 相机似乎没有启用 CAMERA 权限。毕竟,这实际上看起来像是向后兼容的东西。
让我们假设 Google 相机(或处理您的 ACTION 意图的任何其他应用程序)需要特定权限。
当您的应用没有 CAMERA 权限时,它只是让 Google 相机使用旧权限模型执行操作。
但是,在清单中声明了 CAMERA 权限后,它还在 Google 相机(没有 Android M 权限模型)中强制执行 CAMERA 权限以使用 Android M 权限模型(我认为。)
所以这意味着使用上述方法,您需要在运行时提供您的应用程序权限,这意味着它的子任务(在本例中 Google 相机)现在也将具有该权限。
我遇到了同样的问题并从 google 找到了这个文档:
https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE
"Note: if you app targets M and above and declares as using the CAMERA permission which is not granted, then atempting to use this action will result in a SecurityException."
这真的很奇怪。
完全没有意义。
该应用程序使用意图和操作 IMAGE_CAPTURE 仅 运行 将相机权限声明为 SecurityException。但是,如果您的应用未使用 intent with action IMAGE_CAPTURE 声明相机权限,则可以毫无问题地启动相机应用。
解决方法是检查应用程序是否在清单中包含摄像头权限,如果是,请在启动 intent 之前请求摄像头权限。
这里是检查权限是否包含在清单中的方法,不管是否授予权限。
public boolean hasPermissionInManifest(Context context, String permissionName) {
final String packageName = context.getPackageName();
try {
final PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
final String[] declaredPermisisons = packageInfo.requestedPermissions;
if (declaredPermisisons != null && declaredPermisisons.length > 0) {
for (String p : declaredPermisisons) {
if (p.equals(permissionName)) {
return true;
}
}
}
} catch (NameNotFoundException e) {
}
return false;
}
至于你的问题 'Is this a known problem in M?' 一位 google 开发人员回应了将此问题报告为错误的人。
看这里:
https://issuetracker.google.com/issues/37063818#comment8
下面是来自 Google 的人的话:“这是一种旨在避免用户沮丧的行为,因为他们撤销了应用的相机许可,而该应用仍然可以通过 intent 拍照。用户不知道权限撤销后拍摄的照片是通过不同的机制发生的,并且会质疑权限模型的正确性。这适用于 MediaStore.ACTION_IMAGE_CAPTURE、MediaStore.ACTION_VIDEO_CAPTURE 和 Intent.ACTION_CALL 文档,其中记录了针对 M." 的应用程序的行为更改。"
由于 Google 不介意从您的用户那里抽象出使用相机的机制,您不妨策略性地触发相机许可的第一个请求并引用 Activity 的功能使用相机作为请求的推理。如果您允许您的应用在用户只是尝试拍照时首先发出此权限请求,用户可能会认为您的应用行为异常,因为拍照通常不需要授予权限。
如果您使用的是 Google M,请转至 设置 -> 应用 -> 您的应用 -> 并赋予适当的权限。
我删除了:
uses-permission android:name="android.permission.CAMERA"
并且只依赖于:
uses-feature android:name="android.hardware.camera" android:required="true"
在清单文件中。
我在这个问题上卡住了,我已经在使用 JTY 的答案了。问题是在某些时候请求权限对话框在 "Never ask again" 上被选中。我正在开发 SDK 24。
我处理权限(在我的例子中是相机)的完整代码如下:
public void checksCameraPermission(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.d("MyApp", "SDK >= 23");
if (this.checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
Log.d("MyApp", "Request permission");
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
MY_REQUEST_CODE);
if (! shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
showMessageOKCancel("You need to allow camera usage",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(FotoPerfil.this, new String[] {Manifest.permission.CAMERA},
MY_REQUEST_CODE);
}
});
}
}
else {
Log.d("MyApp", "Permission granted: taking pic");
takePicture();
}
}
else {
Log.d("MyApp", "Android < 6.0");
}
}
然后
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
然后
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_REQUEST_CODE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
criarFoto();
} else {
Toast.makeText(this, "You did not allow camera usage :(", Toast.LENGTH_SHORT).show();
noFotoTaken();
}
return;
}
}
}
预期的行为是,万一用户错误地检查了 "Never ask again" 您的应用会卡住(未显示请求对话框)并且用户可能会感到沮丧。这样一条消息告诉他他需要这个权限。
我的这种方法不仅检查相机,还检查我的应用程序在启动期间所需的所有权限...我的 Helper.java 文件中有这个,另请注意,对于对话框,我正在使用此库: https://github.com/afollestad/material-dialogs
///check camera permission
public static boolean hasPermissions(final Activity activity){
//add your permissions here
String[] AppPermissions = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
//ungranted permissions
ArrayList<String> ungrantedPerms = new ArrayList<String>();
//loop
//lets set a boolean of hasUngrantedPerm to false
Boolean needsPermRequest = false;
//permissionGranted
int permGranted = PackageManager.PERMISSION_GRANTED;
//permission required content
String permRequestStr = activity.getString(R.string.the_following_perm_required);
//loop
for(String permission : AppPermissions){
//check if perm is granted
int checkPerm = ContextCompat.checkSelfPermission(activity,permission);
//if the permission is not granted
if(ContextCompat.checkSelfPermission(activity,permission) != permGranted){
needsPermRequest = true;
//add the permission to the ungranted permission list
ungrantedPerms.add(permission);
//permssion name
String[] splitPerm = permission.split(Pattern.quote("."));
String permName = splitPerm[splitPerm.length-1].concat("\n");
permRequestStr = permRequestStr.concat(permName);
}//end if
}//end loop
//if all permission is granted end exec
//then continue code exec
if(!needsPermRequest) {
return true;
}//end if
//convert array list to array string
final String[] ungrantedPermsArray = ungrantedPerms.toArray(new String[ungrantedPerms.size()]);
//show alert Dialog requesting permission
new MaterialDialog.Builder(activity)
.title(R.string.permission_required)
.content(permRequestStr)
.positiveText(R.string.enable)
.negativeText(R.string.cancel)
.onPositive(new MaterialDialog.SingleButtonCallback(){
@Override
public void onClick(@NonNull MaterialDialog dialog,@NonNull DialogAction which){
//request the permission now
ActivityCompat.requestPermissions(activity,ungrantedPermsArray,0);
}
})
.show();
//return false so that code exec in that script will not be allowed
//to continue
return false;
}//end checkPermissions
所以您将在此处添加或删除您的权限列表:
//add your permissions here
String[] AppPermissions = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
在我的 activity 文件中,我这样检查权限,助手 class 是我保留 hasPermissions 方法的地方
if(Helper.hasPermissions(this) == false){
return;
}//end if
意味着如果没有权限我们不需要继续执行..
同样,我们需要在完成后收听权限请求,为此,将下面的代码添加到您的 activity 文件(可选)
//Listen to Permission request completion
//put in your activity file
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
int permGranted = PackageManager.PERMISSION_GRANTED;
Boolean permissionRequired = false;
for(int perm : grantResults){
if(perm != permGranted){
permissionRequired = true;
}
}
//if permission is still required
if(permissionRequired){
//recheck and enforce permission again
Helper.hasPermissions(this);
}//end if
}//end method
有点晚了。但我想再补充一件事。每当您调用包含相机功能的方法时,请在 try catch 块中使用它。如果不是,应用程序将在某些设备上崩溃,例如 Moto G4 plus 或 one plus。
private static final int CAMERA_REQUEST_CODE = 10;
//TODO add camera opening functionality here.
try {
captureImage();
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent,CAMERA_REQUEST_CODE);
} catch (Exception e){
e.printStackTrace();
}
private void captureImage(){
if( ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.CAMERA},
CAMERA_REQUEST_CODE);
}
else {
// Open your camera here.
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Now user should be able to use camera
}
else {
// Your app will not have this permission. Turn off all functions
// that require this permission or it will force close like your
// original question
}
}
}
P.S: 确保不要复制粘贴覆盖的方法。
您必须启用应用权限才能使用相机。我更喜欢添加此方法添加激活相机的命令:
public static async Task<bool> HasPermission()
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (status == PermissionStatus.Granted) return true;
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Camera))
{
ShowDialogOk("Error", "Please allow access to the camera.");//that is my custom method for allert
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera);
status = results[Permission.Camera];
return status == PermissionStatus.Granted;
}
我正在尝试让我的应用程序为新的 Android M 权限更改做好准备,但发现了一些奇怪的行为。我的应用程序使用相机意图机制来允许用户从相机获取照片。但在另一个 activity 中,需要使用具有相机权限的相机本身(因为库依赖性 card.io 需要这个)。
但是 activity 中的 M 只需要一个相机意图,当我尝试启动相机意图时,我看到了以下崩溃(如果我从清单中删除相机权限,则不会发生这种情况),
> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0
> {act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras)} from uid 10098 on display 0 09-25
> 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting
> Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: Unable to launch as uid 10098 package
> com.example.me.mycamerselectapp, while running in android:ui 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:
> java.lang.SecurityException: Permission Denial: starting Intent {
> act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity$ChooserRowAdapter.onClick(ChooserActivity.java:990)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.view.View.performClick(View.java:5198) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityThread.main(ActivityThread.java:5417) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25
> 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25
> 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown
> buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript:
> 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/?
> E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240
这是 Android M 的已知问题吗?更重要的是我该如何解决这个问题?
在清单中我有以下内容,
<uses-permission android:name="android.permission.CAMERA" />
这是我用来让用户用相机点击图片的代码and/or select 图片
public static Intent openImageIntent(Context context, Uri cameraOutputFile) {
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = context.getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile);
cameraIntents.add(intent);
}
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
我在 activity 中点击按钮调用 openImageIntent()
。当我在我的应用程序中没有 CAMERA 权限时,它工作正常,但添加后我得到上面发布的异常。
@Override
public void onClick(View v) {
Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this));
try {
startActivityForResult(picCaptureIntenet, 100);
} catch(Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
如果您使用的是AndroidM权限模型,您首先需要在运行时检查该应用程序是否具有该权限,并且必须在运行时提示用户该权限。 您在清单中定义的权限不会在安装时自动授予。
if (checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA},
MY_REQUEST_CODE);
}
MY_REQUEST_CODE 是您可以定义的静态常量,将再次用于 requestPermission 对话框回调。
您将需要对话结果的回调:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Now user should be able to use camera
}
else {
// Your app will not have this permission. Turn off all functions
// that require this permission or it will force close like your
// original question
}
}
}
编辑
从堆栈跟踪中读取,Google 相机似乎没有启用 CAMERA 权限。毕竟,这实际上看起来像是向后兼容的东西。
让我们假设 Google 相机(或处理您的 ACTION 意图的任何其他应用程序)需要特定权限。
当您的应用没有 CAMERA 权限时,它只是让 Google 相机使用旧权限模型执行操作。
但是,在清单中声明了 CAMERA 权限后,它还在 Google 相机(没有 Android M 权限模型)中强制执行 CAMERA 权限以使用 Android M 权限模型(我认为。)
所以这意味着使用上述方法,您需要在运行时提供您的应用程序权限,这意味着它的子任务(在本例中 Google 相机)现在也将具有该权限。
我遇到了同样的问题并从 google 找到了这个文档: https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE
"Note: if you app targets M and above and declares as using the CAMERA permission which is not granted, then atempting to use this action will result in a SecurityException."
这真的很奇怪。 完全没有意义。 该应用程序使用意图和操作 IMAGE_CAPTURE 仅 运行 将相机权限声明为 SecurityException。但是,如果您的应用未使用 intent with action IMAGE_CAPTURE 声明相机权限,则可以毫无问题地启动相机应用。
解决方法是检查应用程序是否在清单中包含摄像头权限,如果是,请在启动 intent 之前请求摄像头权限。
这里是检查权限是否包含在清单中的方法,不管是否授予权限。
public boolean hasPermissionInManifest(Context context, String permissionName) {
final String packageName = context.getPackageName();
try {
final PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
final String[] declaredPermisisons = packageInfo.requestedPermissions;
if (declaredPermisisons != null && declaredPermisisons.length > 0) {
for (String p : declaredPermisisons) {
if (p.equals(permissionName)) {
return true;
}
}
}
} catch (NameNotFoundException e) {
}
return false;
}
至于你的问题 'Is this a known problem in M?' 一位 google 开发人员回应了将此问题报告为错误的人。
看这里: https://issuetracker.google.com/issues/37063818#comment8
下面是来自 Google 的人的话:“这是一种旨在避免用户沮丧的行为,因为他们撤销了应用的相机许可,而该应用仍然可以通过 intent 拍照。用户不知道权限撤销后拍摄的照片是通过不同的机制发生的,并且会质疑权限模型的正确性。这适用于 MediaStore.ACTION_IMAGE_CAPTURE、MediaStore.ACTION_VIDEO_CAPTURE 和 Intent.ACTION_CALL 文档,其中记录了针对 M." 的应用程序的行为更改。"
由于 Google 不介意从您的用户那里抽象出使用相机的机制,您不妨策略性地触发相机许可的第一个请求并引用 Activity 的功能使用相机作为请求的推理。如果您允许您的应用在用户只是尝试拍照时首先发出此权限请求,用户可能会认为您的应用行为异常,因为拍照通常不需要授予权限。
如果您使用的是 Google M,请转至 设置 -> 应用 -> 您的应用 -> 并赋予适当的权限。
我删除了:
uses-permission android:name="android.permission.CAMERA"
并且只依赖于:
uses-feature android:name="android.hardware.camera" android:required="true"
在清单文件中。
我在这个问题上卡住了,我已经在使用 JTY 的答案了。问题是在某些时候请求权限对话框在 "Never ask again" 上被选中。我正在开发 SDK 24。
我处理权限(在我的例子中是相机)的完整代码如下:
public void checksCameraPermission(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.d("MyApp", "SDK >= 23");
if (this.checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
Log.d("MyApp", "Request permission");
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
MY_REQUEST_CODE);
if (! shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
showMessageOKCancel("You need to allow camera usage",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(FotoPerfil.this, new String[] {Manifest.permission.CAMERA},
MY_REQUEST_CODE);
}
});
}
}
else {
Log.d("MyApp", "Permission granted: taking pic");
takePicture();
}
}
else {
Log.d("MyApp", "Android < 6.0");
}
}
然后
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
然后
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_REQUEST_CODE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
criarFoto();
} else {
Toast.makeText(this, "You did not allow camera usage :(", Toast.LENGTH_SHORT).show();
noFotoTaken();
}
return;
}
}
}
预期的行为是,万一用户错误地检查了 "Never ask again" 您的应用会卡住(未显示请求对话框)并且用户可能会感到沮丧。这样一条消息告诉他他需要这个权限。
我的这种方法不仅检查相机,还检查我的应用程序在启动期间所需的所有权限...我的 Helper.java 文件中有这个,另请注意,对于对话框,我正在使用此库: https://github.com/afollestad/material-dialogs
///check camera permission
public static boolean hasPermissions(final Activity activity){
//add your permissions here
String[] AppPermissions = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
//ungranted permissions
ArrayList<String> ungrantedPerms = new ArrayList<String>();
//loop
//lets set a boolean of hasUngrantedPerm to false
Boolean needsPermRequest = false;
//permissionGranted
int permGranted = PackageManager.PERMISSION_GRANTED;
//permission required content
String permRequestStr = activity.getString(R.string.the_following_perm_required);
//loop
for(String permission : AppPermissions){
//check if perm is granted
int checkPerm = ContextCompat.checkSelfPermission(activity,permission);
//if the permission is not granted
if(ContextCompat.checkSelfPermission(activity,permission) != permGranted){
needsPermRequest = true;
//add the permission to the ungranted permission list
ungrantedPerms.add(permission);
//permssion name
String[] splitPerm = permission.split(Pattern.quote("."));
String permName = splitPerm[splitPerm.length-1].concat("\n");
permRequestStr = permRequestStr.concat(permName);
}//end if
}//end loop
//if all permission is granted end exec
//then continue code exec
if(!needsPermRequest) {
return true;
}//end if
//convert array list to array string
final String[] ungrantedPermsArray = ungrantedPerms.toArray(new String[ungrantedPerms.size()]);
//show alert Dialog requesting permission
new MaterialDialog.Builder(activity)
.title(R.string.permission_required)
.content(permRequestStr)
.positiveText(R.string.enable)
.negativeText(R.string.cancel)
.onPositive(new MaterialDialog.SingleButtonCallback(){
@Override
public void onClick(@NonNull MaterialDialog dialog,@NonNull DialogAction which){
//request the permission now
ActivityCompat.requestPermissions(activity,ungrantedPermsArray,0);
}
})
.show();
//return false so that code exec in that script will not be allowed
//to continue
return false;
}//end checkPermissions
所以您将在此处添加或删除您的权限列表:
//add your permissions here
String[] AppPermissions = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
在我的 activity 文件中,我这样检查权限,助手 class 是我保留 hasPermissions 方法的地方
if(Helper.hasPermissions(this) == false){
return;
}//end if
意味着如果没有权限我们不需要继续执行.. 同样,我们需要在完成后收听权限请求,为此,将下面的代码添加到您的 activity 文件(可选)
//Listen to Permission request completion
//put in your activity file
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
int permGranted = PackageManager.PERMISSION_GRANTED;
Boolean permissionRequired = false;
for(int perm : grantResults){
if(perm != permGranted){
permissionRequired = true;
}
}
//if permission is still required
if(permissionRequired){
//recheck and enforce permission again
Helper.hasPermissions(this);
}//end if
}//end method
有点晚了。但我想再补充一件事。每当您调用包含相机功能的方法时,请在 try catch 块中使用它。如果不是,应用程序将在某些设备上崩溃,例如 Moto G4 plus 或 one plus。
private static final int CAMERA_REQUEST_CODE = 10;
//TODO add camera opening functionality here.
try {
captureImage();
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent,CAMERA_REQUEST_CODE);
} catch (Exception e){
e.printStackTrace();
}
private void captureImage(){
if( ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.CAMERA},
CAMERA_REQUEST_CODE);
}
else {
// Open your camera here.
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Now user should be able to use camera
}
else {
// Your app will not have this permission. Turn off all functions
// that require this permission or it will force close like your
// original question
}
}
}
P.S: 确保不要复制粘贴覆盖的方法。
您必须启用应用权限才能使用相机。我更喜欢添加此方法添加激活相机的命令:
public static async Task<bool> HasPermission()
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (status == PermissionStatus.Granted) return true;
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Camera))
{
ShowDialogOk("Error", "Please allow access to the camera.");//that is my custom method for allert
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera);
status = results[Permission.Camera];
return status == PermissionStatus.Granted;
}