You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Android应用至API 33后,相机Intent返回Result.Cancelled问题排查

问题:升级到API 33后调用相机OnActivityResult始终返回Result.Cancelled

我正在将原本适配API 9(Pie)的可用应用升级至API 33(Tiramisu),但调用相机后,OnActivityResult始终返回Result.Cancelled。开发环境为Windows 11专业版上的Visual Studio 2022 17.3.6。

相关代码

相机点击事件代码

camerabutton.Click += (sender, evt) =>
{
    var cameraispresent = checkCameraHardware(this);
    if (cameraispresent)
    {
        try
        {
            CreateDirectoryForPictures();
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(",", "");
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(" ", "");
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(".", "");
            var filename = MySubjectInfo.Name + ".jpg";
            Intent intent = new Intent(MediaStore.ActionImageCapture);
            App._file = new File(App._dir, String.Format(filename, Guid.NewGuid()));
            intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(App._file));
            StartActivityForResult(intent, TakePictureRequestCode);
        }
        catch (Exception e)
        {
            var result = e.Message;
        };
    }
};

创建目录代码

private void CreateDirectoryForPictures()
{
    int version = (int)Android.OS.Build.VERSION.SdkInt;

    var root = "";

    if (Android.OS.Environment.IsExternalStorageEmulated)
    {
        root = Android.OS.Environment.ExternalStorageDirectory.ToString();
    }
    else
    {
        root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
    }

    if (version >= Convert.ToInt32(BuildVersionCodes.Q))
    {
        App._dir = new File(root + "/PhotoManager");
    }
    else
    {
        App._dir = new File(
            Environment.GetExternalStoragePublicDirectory(
                Environment.DirectoryPictures), "PhotoManager");
    }

    if (!App._dir.Exists())
    {
        App._dir.Mkdirs();
    }
}

存储数据的App类

public static class App
{
    public static File _file;
    public static File _dir;
    public static Bitmap bitmap;
}

OnActivityResult代码

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == TakePictureRequestCode  && resultCode == Result.Ok)
    {
        //Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
        //Uri contentUri = Uri.FromFile(App._file);
        //mediaScanIntent.SetData(contentUri);
        //SendBroadcast(mediaScanIntent);

        Bitmap photo = (Bitmap)data.GetByteArrayExtra("data");
        App.bitmap = photo;
        //int height = 160; //Resources.DisplayMetrics.HeightPixels;
        //int width = 100; //MyImageView.Height;
        //App.bitmap = App._file.Path.LoadAndResizeBitmap(width, height);
        if (App.bitmap != null)
        {
            MyImageView.SetImageBitmap(App.bitmap);

            
            Bitmap bitmap = App.bitmap;
            var filePath = App._file.AbsolutePath;
            var stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
            bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
            stream.Close();

            bitmap = null;
            App.bitmap = null;
            PictureTaken = true;
        }

        // Dispose of the Java side bitmap.
        GC.Collect();

    }
}

问题分析与修复方案

1. API 29+ 禁止直接使用Uri.FromFile()传递文件路径

Android 10(API 29)启用Scoped Storage后,直接通过Uri.FromFile()生成的Uri无法被其他应用(如相机)访问,相机因权限不足直接取消操作。

修复步骤:

  • 在AndroidManifest.xml中注册FileProvider:
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    
  • 在Resources/xml目录创建file_paths.xml,配置共享路径:
    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-files-path name="my_images" path="Pictures/PhotoManager" />
    </paths>
    
  • 修改相机点击事件中的Uri生成逻辑:
    // 替换Uri.FromFile(App._file)
    Uri photoUri = FileProvider.GetUriForFile(this, PackageName + ".fileprovider", App._file);
    intent.PutExtra(MediaStore.ExtraOutput, photoUri);
    // 添加临时访问权限
    intent.AddFlags(ActivityFlags.GrantReadUriPermission);
    intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
    

2. API 33 需单独申请相机权限

Android 13(API 33)新增了CAMERA权限的单独申请要求,未申请时相机调用会被系统拦截。

修复步骤:

  • 在AndroidManifest.xml添加权限声明:
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • 调用相机前动态申请权限:
    if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != Permission.Granted)
    {
        ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.Camera }, 1001);
        return;
    }
    // 权限通过后再执行相机调用逻辑
    
  • 处理权限申请结果:
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
    {
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1001)
        {
            if (grantResults.Length > 0 && grantResults[0] == Permission.Granted)
            {
                // 重新触发相机调用
                OpenCamera(); // 将原相机点击逻辑封装为OpenCamera方法
            }
        }
    }
    

3. 目录创建不符合Scoped Storage规范

原目录创建逻辑使用ExternalStorageDirectory,在API 29+下会导致权限问题,且文件管理器无法访问该目录。

修复步骤:
修改CreateDirectoryForPictures方法,使用应用专属外部存储目录:

private void CreateDirectoryForPictures()
{
    // 应用私有外部存储图片目录,无需额外权限
    App._dir = new File(GetExternalFilesDir(Android.OS.Environment.DirectoryPictures), "PhotoManager");
    if (!App._dir.Exists())
    {
        App._dir.Mkdirs();
    }
}

4. OnActivityResult图片获取逻辑错误

指定MediaStore.ExtraOutput后,相机不会在data中返回缩略图,直接从指定路径读取图片即可,无需从data中获取再保存。

修复步骤:
修改OnActivityResult代码:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == TakePictureRequestCode && resultCode == Result.Ok)
    {
        // 从指定路径加载图片,避免内存溢出
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.InJustDecodeBounds = true;
        BitmapFactory.DecodeFile(App._file.AbsolutePath, options);
        options.InSampleSize = CalculateInSampleSize(options, MyImageView.Width, MyImageView.Height);
        options.InJustDecodeBounds = false;
        App.bitmap = BitmapFactory.DecodeFile(App._file.AbsolutePath, options);

        if (App.bitmap != null)
        {
            MyImageView.SetImageBitmap(App.bitmap);
            PictureTaken = true;
        }

        GC.Collect();
    }
}

// 计算图片缩放比例,防止内存溢出
private int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
    int height = options.OutHeight;
    int width = options.OutWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth)
    {
        int halfHeight = height / 2;
        int halfWidth = width / 2;
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth)
        {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

内容的提问来源于stack exchange,提问作者Prescott Chartier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:35:19