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

Android 13/12调用相机拍照时OnActivityResult返回Result.Canceled求助

问题分析与解决方案

你的问题核心是Android 12/13的Scoped Storage限制,以及直接使用Uri.FromFile生成的file://Uri无法被相机应用跨进程访问,导致相机返回取消状态。同时,当指定MediaStore.ExtraOutput时,相机不会在返回的Intent中携带data,这是正常行为,需要直接使用预先指定的文件路径。


解决方案步骤

1. 改用FileProvider生成可跨进程访问的Content Uri

Android 7.0+禁止跨应用直接访问file://Uri,必须使用FileProvider生成content://格式的Uri,授予相机应用临时访问权限。

步骤1.1 在AndroidManifest中注册FileProvider

在<application>标签内添加以下配置:

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

步骤1.2 创建文件路径配置文件

在Resources/xml目录下新建file_paths.xml(没有xml目录则手动创建):

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 映射应用外部私有存储下的Inventory目录 -->
    <external-files-path name="inventory_images" path="Inventory" />
</paths>

2. 修改拍照触发代码

替换Uri.FromFile为FileProvider.GetUriForFile,并调整Intent的Flag和文件路径:

TakePicture.Click += delegate
{
    if (slabDetails != null)
    {
        // 使用应用外部私有存储目录,避免Scoped Storage限制
        var inventoryDir = new File(ApplicationContext.GetExternalFilesDir(null), "Inventory");
        if (!inventoryDir.Exists())
        {
            inventoryDir.Mkdirs(); // 创建目录(如果不存在)
        }
        File SlabFile = new File(inventoryDir, slabDetails.ExtSlabNo + ".jpg");
        // 用FileProvider生成Content Uri
        imageUri = FileProvider.GetUriForFile(this, PackageName + ".fileprovider", SlabFile);

        Intent camIntent = new Intent(MediaStore.ActionImageCapture);
        // 移除不必要的ClearTop/SingleTop Flag
        camIntent.PutExtra(MediaStore.ExtraOutput, imageUri);
        // 授予相机临时读写权限
        camIntent.AddFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission);
        
        // 检查相机应用是否可用
        if (camIntent.ResolveActivity(PackageManager) != null)
        {
            StartActivityForResult(camIntent, 2);
        }
    }
};

3. 调整OnActivityResult处理逻辑

当指定MediaStore.ExtraOutput时,相机返回的data必然为null,需要直接使用预先保存的imageUri或文件路径:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    switch (requestCode)
    {
        case 2:// 相机返回
            if (resultCode == Result.Ok)
            {
                try
                {
                    // 直接使用imageUri对应的文件,而非data
                    new SyncDataTask(this, imageUri, 1, db).Execute();
                }
                catch (Exception ex)
                {
                    var method = System.Reflection.MethodBase.GetCurrentMethod();
                    var methodName = method.Name;
                    var className = method.ReflectedType.Name;

                    MainActivity.SaveLogReport(className, methodName, ex);
                }
                catch (OutOfMemoryError ex)
                {
                    ex.PrintStackTrace();
                } 
           }
     }
}

4. 清理冗余权限

Android 12/13中,WRITE_EXTERNAL_STORAGE和MANAGE_EXTERNAL_STORAGE对于你的场景是冗余的(使用应用私有存储无需这些权限),可以从AndroidManifest中移除:

<!-- 保留必要权限即可 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:51:18