Xamarin.Android:在 Android 10+ 中使用 Intents 打开 Excel 文档失败

Posted

技术标签:

【中文标题】Xamarin.Android:在 Android 10+ 中使用 Intents 打开 Excel 文档失败【英文标题】:Xamarin.Android: Opening an Excel document using Intents in Android 10+ fails 【发布时间】:2021-08-15 03:37:17 【问题描述】:

我有以下用于导出 Excel 文件的代码。最终结果在 Excel 应用程序中打开。这是打开文件的代码:

if (file.Exists())
    Intent intent = new Intent(Intent.ActionView);

    if (Build.VERSION.SdkInt < BuildVersionCodes.N)
    
        intent.SetDataAndType(android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
    
    else
    
        intent.AddFlags(ActivityFlags.GrantReadUriPermission);
        intent.SetDataAndType(Android.Net.Uri.Parse(file.Path), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
    
    context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));

我知道文件已创建,因为我没有收到任何错误并且正确地到达了这一点。另外,我已经添加了标志FLAG_GRANT_READ_URI_PERMISSION/GrantReadUriPermission,当我尝试打开文件时,我收到以下消息:

无法打开文件 |尝试将文件保存在设备上,然后打开 它

这是我的 AndroidManifest.xml 的一部分

<application android:theme="@style/Launcher" android:icon="@drawable/icon" android:label="@string/app_name" android:requestLegacyExternalStorage="true">
    <activity android:name="tk.supernova.tmtimer.MainActivity" android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
    </activity>
</application>

而且我已经申请了读/写权限:

private readonly string[] PermissionToCheck = 
    Android.Manifest.Permission.WriteExternalStorage,
    Android.Manifest.Permission.ReadExternalStorage
;
private const int REQUEST_ID = 0;
private const int STORAGE_PERMISSION_CODE = 23;

protected override void OnCreate(Bundle savedInstanceState)

    Platform.Init(this, savedInstanceState);
    if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
    
        RequestPermissions(PermissionToCheck, REQUEST_ID);
    

    if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
    
        try
        
            StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
                                .PenaltyDeathOnFileUriExposure()
                                .DetectFileUriExposure()
                                .Build();
            StrictMode.SetVmPolicy(policy);
        
        catch  
    


public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)

    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

知道我做错了什么吗?谢谢。

【问题讨论】:

使用 FileProvider 来提供您的文件。取消严格模式的东西。 不清楚为什么要显示清单文件的一部分。还不清楚为什么要为这些运行时权限显示代码,因为所有这些都与提供您的应用程序之前创建的文件无关。 我要测试@blackapps。我分享了我认为可能有用的东西。我过去遇到过棘手的 cmets。 嗨@blackapps,谢谢,现在可以了。我分享了我的更改。 【参考方案1】:

根据@blackapps 的建议,我做了以下更改。

#1。在AndroidManifest.xml 中添加新部分:

<provider android:name="androidx.core.content.FileProvider" android:authorities="tk.supernova.tmtimer.tk.supernova.tmtimer.fileprovider" android:exported="false" android:grantUriPermissions="true">
    <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>

#2。在名为 file_paths 的 XML 文件夹中创建一个新文件,其中包含:

<?xml version="1.0" encoding="UTF-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_meetings" path="meetings/" />
</paths>

#3。这将更改为正确打开和保存文件:

private File CreateDirFile(string fileName)

    var meetingPath = new File(Xamarin.Essentials.FileSystem.AppDataDirectory, "meetings");
    meetingPath.Mkdir();
    var file = new File(meetingPath, fileName);

    if (file.Exists())
    
        file.Delete();
    

    file.CreateNewFile();
    return file;


private void Save(string fileName, string contentType, MemoryStream stream, Context context)

    var file = CreateDirFile(fileName);
    try
    
        FileOutputStream outs = new FileOutputStream(file, false);
        outs.Write(stream.ToArray());
        outs.Flush();
        outs.Close();
    
    catch
    
        Toast.MakeText(context, context.GetString(Resource.String.LblStorageIssue), ToastLength.Long).Show();
    
    if (file.Exists() && contentType != APP_TYPE)
    
        Intent intent = new Intent(Intent.ActionView);

        if (Build.VERSION.SdkInt < BuildVersionCodes.N)
        
            intent.SetDataAndType(Android.Net.Uri.FromFile(file), MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
        
        else
        
            intent.AddFlags(ActivityFlags.GrantReadUriPermission);
            intent.AddFlags(ActivityFlags.GrantWriteUriPermission);

            var contentUri = FileProvider.GetUriForFile(Application.Context, FILE_PROVIDER, file);

            intent.SetDataAndType(contentUri, MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString())));
        
        context.StartActivity(Intent.CreateChooser(intent, context.GetString(Resource.String.LblChooseApp)));
    

#4。我删除了严格部分。

【讨论】:

很高兴您通过这个小提示解决了所有问题。但是...var file = CreateDirFile(fileName); 我看到你删除了这个函数的代码。也取消该功能。您不需要先删除现有文件并创建一个新文件,因为 new FileOutputStream(file) 将完成所有操作。您只需要以正确的方式定义您的 File 实例。文件文件……嗯,你知道它应该是什么…… 嗨@blackapps 当我这样做时它崩溃了:/。它说该文件不存在,这就是我这样写的原因。 好吧再试一次。不做会更好。调试真正发生的事情.. 你有一个吐司在那个捕获。哪个好。但在那之后你使用一个file.exists()。但是,您将不知道新 FileOutputStream 是否在您之前自己创建文件时失败。该代码流不是很健壮。

以上是关于Xamarin.Android:在 Android 10+ 中使用 Intents 打开 Excel 文档失败的主要内容,如果未能解决你的问题,请参考以下文章

Android(Xamarin):BottomNavigationView 没有出现在正确的位置

如何在 Xamarin.android 中更改条目光标颜色

Xamarin.Android:在 Android 10+ 中使用 Intents 打开 Excel 文档失败

Xamarin.Form与Xamarin.Android或Xamarin.IOS的区别简述

将Xamarin.Android应用程序迁移到Xamarin.Forms应用程序

如何在 Xamarin.Android 中实现 RewardedAdLoadCallback?