Android 11 在应用程序之间共享文件

Cho*_*ong 8 storage android

Android 11 强制执行了一些存储规则,链接请参阅:Android 11 中的存储更新

用例:我有 2 个应用程序,应用程序 A 将文件(.txt)写入外部存储,应用程序 B 将从外部存储读取文件,无需用户交互。但在Android 11上读写时抛出异常,提示权限被拒绝。

所以我做了一些研究,发现只有MediaStore API存储访问框架允许访问其他应用程序创建的文件,链接参考:数据和文件存储概述

但这两种方法都不适合我的用例:

  • MediaStore API只能访问媒体文件(图像、音频文件、视频)
  • 存储访问框架需要用户交互

那么,有没有其他方法可以访问 Android 11 上不同应用程序创建的外部存储上的非媒体文件?

尽管我进行了所有研究,但我没有找到解决我的问题的方法。

感谢您的帮助。

更新

我尝试了FileProvider但当我尝试启动活动时,它总是显示错误

E/AndroidRuntime:致命异常:主进程:com.example.testapp,PID:20141 android.content.ActivityNotFoundException:找不到处理 Intent { act=com.example.app2.action.RECEIVE dat=content://com 的活动.example.testapp.fileprovider/myfiles/default_user.txt flg=0x1 } 在 android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2067) 在 android.app.Instrumentation.execStartActivity(Instrumentation.java:1727) 在 android.app .Activity.startActivityForResult(Activity.java:5320)

这就是我从应用程序 1 启动应用程序 2 活动的方式

File filePath = new File(getFilesDir(), "files");
File newFile = new File(filePath, "default_user.txt");
Intent intent = new Intent();
intent.setAction("com.example.app2.action.RECEIVE");
intent.setData(FileProvider.getUriForFile(this, "com.example.testapp.fileprovider", newFile));
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
Run Code Online (Sandbox Code Playgroud)

应用程序 1 清单

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
    android:allowBackup="true"
    android:requestLegacyExternalStorage="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.TestApp">
    <activity android:name=".StorageActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".MainActivity">
    </activity>

    <service
        android:name=".service.TestService"
        android:enabled="true"
        android:exported="true" />

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.example.testapp.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>
    
</application>
Run Code Online (Sandbox Code Playgroud)

应用程序 2 清单

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:requestLegacyExternalStorage="true"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.TestApp">
    <activity android:name=".ReceiverActivity">
        <intent-filter>
            <action android:name="com.example.app2.action.RECEIVE"/>

            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"
                android:host="com.example.testapp" />
        </intent-filter>
    </activity>
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
Run Code Online (Sandbox Code Playgroud)

bla*_*pps 0

您应该使用启动意图直接启动 app2。

                        try {
                            File file = new File( .... );
                            Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileprovider", file);

                            String apkPackage = "com.example.app2";

                            Intent intent = context.getPackageManager().getLaunchIntentForPackage(apkPackage);

                            if ( intent==null )
                            {
                                Toast.makeText(context, "Sorry, could not get launch intent for: " + apkPackage, Toast.LENGTH_LONG).show();

                                return;
                            }

                            intent.setAction(Intent.ACTION_VIEW);
                            intent.setDataAndType(uri, mimeType);

                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                            context.startActivity(intent);
                        }
                        catch ( IllegalArgumentException e)
                        {
                            e.printStackTrace();
                            Toast.makeText(context, "IllegalArgumentException: " + e.getMessage(), Toast.LENGTH_LONG).show();

                        }
                        catch ( Exception e)
                        {
                            e.printStackTrace();

                            Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
                        }
Run Code Online (Sandbox Code Playgroud)

您不需要在 app2 的清单中使用意图过滤器。

接收方可以通过以下方式获取 uri:

Uri uri = getIntent().getData();
Run Code Online (Sandbox Code Playgroud)