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

Xamarin.Forms Android中FileProvider跨应用返回文件失败问题

跨Xamarin Forms Android应用实现文件共享(替代废弃的sharedUserId)

我有两个Xamarin Forms Android应用,App1启动App2中的Activity,需要将App2生成的文件返回给App1。之前通过在两个应用的AndroidManifest.xml中设置相同的sharedUserId,直接返回文件路径就能实现共享,但sharedUserId已被废弃,且与Microsoft Defender按应用VPN不兼容,因此必须更换方案。

一、App2的现有配置

1. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.app2">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
    <application android:label="App2.Android" android:theme="@style/MainTheme">
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.companyname.app2.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>
</application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>

2. file_paths.xml

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="." />
</paths>

3. MainApplication代码

using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;

namespace App2.Droid
{
    [Application]
    public partial class MainApplication : Application, Application.IActivityLifecycleCallbacks
    {
        internal static Context CurrentContext { get; private set; }

        internal static String FileProviderAuthority
        {
            get => MainApplication.CurrentContext.ApplicationContext.PackageName + ".fileprovider";
        }

        public MainApplication(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();
            RegisterActivityLifecycleCallbacks(this);
        }

        public override void OnTerminate()
        {
            base.OnTerminate();
            UnregisterActivityLifecycleCallbacks(this);
        }

        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
        {
            CurrentContext = activity;
        }

        public void OnActivityDestroyed(Activity activity)
        {

        }

        public void OnActivityPaused(Activity activity)
        {

        }

        public void OnActivityResumed(Activity activity)
        {
            CurrentContext = activity;
        }

        public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
        {

        }

        public void OnActivityStarted(Activity activity)
        {
            CurrentContext = activity;
        }

        public void OnActivityStopped(Activity activity)
        {

        }
    }
}

二、App2测试代码及问题

在App2的MainActivity中编写了测试方法,用于创建文件、生成URI、自行读取文件并尝试分享URI。尝试过多种Context,但目前存在以下问题:

  • 选择WhatsApp分享时直接崩溃
  • 选择Gmail时,邮件能附上test1.txt且显示正确大小,但无法下载文件

测试代码如下:

private void test()
{
    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.SetVmPolicy(builder.Build());

    string savefilePath = Path.Combine($"{Android.App.Application.Context.GetExternalFilesDir("")}", "test1.txt");
    System.IO.File.WriteAllText(savefilePath, "Hello from FileProvider 2!");
    _file = new Java.IO.File(savefilePath);
    Context context = Android.App.Application.Context;
    //Context context = this;
    //Context context = this.ApplicationContext;
    //Context context = MainApplication.CurrentContext;
    Android.Net.Uri contentUri = MyFileProvider.GetUriForFile(context, "com.companyname.app2.fileprovider", _file);

    using (var contentResolver = this.ContentResolver)
    using (var inputStream = contentResolver.OpenInputStream(Android.Net.Uri.Parse(contentUri.ToString())))
    {
        if (inputStream != null)
        {
            byte[] buffer;

            buffer = new byte[40000 * 2];
            int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
            System.Console.WriteLine($"Read {bytesRead} from the file"); // 此处读取正常
        }
        else
        {
            System.Console.WriteLine("Failed to open input stream for URI");
        }
    }

    var shareIntent = new Intent();
    shareIntent.SetAction(Intent.ActionSend);
    shareIntent.SetType("text/plain");
    shareIntent.PutExtra(Intent.ExtraStream, contentUri);
    shareIntent.AddFlags(ActivityFlags.GrantReadUriPermission);

    StartActivityForResult(Intent.CreateChooser(shareIntent, "Share file"), RequestCode);
}

三、跨应用读取的困境

将URI返回给App1后,用相同的contentResolver.OpenInputStream代码读取时,会出现以下问题之一:

  • 权限错误
  • 找不到内容提供程序
  • 提示文件不存在

试过Uri.FromFile、各种旧版Xamarin/Android代码,均无法解决问题。也未找到Xamarin.Essentials FileProvider的相关使用示例。

需求目标

需要在App2的Activity返回的JSON结果中包含文件URI,让App1能正常读取该文件。当前返回结果的代码如下:

var rtn = new Intent();
string json = "...somthing with uri files...";
rtn.PutExtra("result", json);
SetResult(Result.Ok, rtn);
Finish();

URI能正常返回,但App1无法读取文件内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:44:55