这个问题包含几个子问题。我正在分拣这些,从this question开始。我最终会通过删除这个问题进行清理。
理论上,以下程序将共享一个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();
}
}
除了在res / values / strings.xml中添加send_to
的值之外,我对Eclipse创建的通用Hello, World
所做的唯一另一对更改是添加以下{{1} AndroidManifest.xml中的标记:
<provider>
...并在res / xml / my_paths.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"
...
我的主要问题是第一个,但是当你在这个主题时,对问题2-4的讨论也会很有趣。
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="files" path="." />
</paths>
,那么需要扩展该类,但是如果只需要ContentProvider
,那么可以使用该类而不进行派生吗? / LI>
FileProvider
两次使用filename
,另一次使用openFileOutput
。有没有办法避免这种重复(这可以保证引用相同的文件)?new File()
之后立即删除文件是否安全,或者是否需要设计回调以等待了解文件已上传/共享。 (真实文件可能需要一些时间来共享/上传。)修改
代码运行正常并显示要发送到的应用列表。
如果我选择Dropbox,我可以选择好的位置。 Dropbox发送通知“Uploading to Dropbox”,然后是“Upload failed:my_file.txt”。
如果我选择Gmail,我可以填写收件人并且该文件似乎已附加,但在“发送邮件...”后,我收到“无法发送附件”。
答案 0 :(得分: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);
仅设置该类型适用于我的Nexus 5,但不适用于我的三星平板电脑。似乎三星平板电脑需要URI作为数据才能授予权限。另请注意,intent.getData()
取消之前对intent.setType()
的所有调用,反之亦然,因此您必须使用组合方法,如上所述。
Gmail似乎将其他数据解释为默认的To-address。非常讨厌!如果有人有更好的解决方案,请分享(双关语,我来自哥德堡)。
答案 1 :(得分:0)
2.是的,确实如此。您看到ContentProvider
是一个抽象类,因此要使用自定义内容提供程序,必须对其进行扩展。由于FileProvider
是ContentProvider
的子类(不是抽象的),程序员可以使用FileProvider
而无需对其进行子类化。
3.为了确保相同的文件,您可以按照下面的示例代码 -
String fileName = "my_file.txt";
String fileData = "sample file content";
// Get the file
File file = new File(this.getFilesDir(), fileName);
if (!file.exists()) {
file.createNewFile();
}
// Write data to file
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(fileData);
fileWriter.close();
// Get the uri
Uri uri = Uri.fromFile(file);
// Share the file with corresponding uri
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.setType("application/txt");
startActivity(Intent.createChooser(shareIntent, "Send To"));
4.不,在您致电startActivity()
后立即删除文件是不安全的。因为startActivity()
是非阻塞函数调用,它会立即返回。您必须等待文件共享的时间。您可以使用startActivityForResult()
来完成此操作。看看它是否有用。