你好 - FileProvider的世界

Cal*_*laf 3 android android-fileprovider

这个问题包含几个子问题.从这个问题开始,我正在分叉这些.我最终会通过删除这个问题进行清理.

理论上,以下程序将共享一个hello-world文本文件.代码运行,但共享到Dropbox或Gmail(仅通过两个具体示例)失败.

public class MainActivity extends Activity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String filename = "hellow.txt";
        String fileContents = "Hello, World!\n";
        byte[] bytes = fileContents.getBytes();
        FileOutputStream fos = null;
        try {
            fos = this.openFileOutput(filename, MODE_PRIVATE);
            fos.write(bytes);
        } catch (IOException e) {                       
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {                       
                e.printStackTrace();
            }
        } 

        File file = new File(filename);
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
        shareIntent.setType("application/txt");
        startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));

        file.delete();
    }
}
Run Code Online (Sandbox Code Playgroud)

除了send_to在res/values/strings.xml中添加值之外,我对Hello, WorldEclipse创建的泛型所做的唯一另一对更改是<provider>在AndroidManifest.xml中添加以下标记:

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.mycorp.helloworldtxtfileprovider.MainActivity"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

    <activity
        android:name="com.mycorp.helloworldtxtfileprovider.MainActivity"
        ...
Run Code Online (Sandbox Code Playgroud)

...并在res/xml/my_paths.xml中添加以下内容

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="." />
</paths>
Run Code Online (Sandbox Code Playgroud)

我的主要问题是第一个,但是当你在这个主题时,对问题2-4的讨论也会很有趣.

  1. 为什么上面的程序失败了?
  2. 确实如此,如果需要一个自定义ContentProvider,那么需要扩展该类,但如果只需要一个FileProvider,那么可以使用该类而不进行派生吗?
  3. 在这段代码中,我需要使用filename两次 - 一次使用openFileOutput,另一次使用new File().有没有办法避免这种重复(这将保证引用相同的文件)?
  4. startActivity(..)调用之后立即删除文件是否安全,或者是否有必要设计回调以等待知道文件已被上载/共享.(真实文件可能需要一些时间来共享/上传.)

编辑

代码运行正常,并显示要发送到的应用程序列表.

如果我选择Dropbox,我可以选择好的位置.Dropbox发送通知"Uploading to Dropbox",然后是"Upload failed:my_file.txt".

如果我选择Gmail,我可以填写收件人并且该文件似乎已附加,但在"发送邮件..."后,我收到"无法发送附件".

vol*_*ley 6

1.

使用FileProvider.getUriForFile(...)构建URI.这会将已启动的活动定向到您的FileProvider(然后可以从您应用的私人文件目录中提供该文件).Uri.fromFile(...)不起作用,因为已启动的活动将尝试直接访问私人目录.

进行设置,FLAG_GRANT_READ_URI_PERMISSION以便为已启动的活动授予对FileProvider构造的URI的读取权限.

最后,"text/plain"可能比"application/txt"更好地作为MIME类型.

我遇到了一些问题,让它在各种设备上保持一致.这是我到目前为止最好的选择(如果我改进它将会编辑):

    final String auth = "org.volley.sndcard_android.fileprovider";
    final Uri uri = FileProvider.getUriForFile(activity, auth, file);
    final String type = activity.getContentResolver().getType(uriForFile);

    final Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setDataAndType(uri, type);
    shareIntent.putExtra(Intent.EXTRA_STREAM, uriForFile);
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    final Intent chooser = Intent.createChooser(shareIntent, "<title>");
    activity.startActivity(chooser);
Run Code Online (Sandbox Code Playgroud)

仅设置类型适用于我的Nexus 5,但不适用于我的三星平板电脑.似乎三星平板电脑需要URI作为数据才能授予权限.另请注意,intent.getData()取消之前的所有调用,intent.setType()反之亦然,因此您必须使用组合方法,如上所述.

Gmail似乎将其他数据解释为默认的To-address.非常讨厌!如果有人有更好的解决方案,请分享(双关语,我来自哥德堡).