2013-09-25 203 views
3

这个问题包含几个子问题。我正在分叉这些,从this question开始。我最终会通过删除这个问题来清理。Hello-World的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(); 
    } 
} 

除了在res /价值/ strings.xml中添加值send_to,只有另一对我没有到Eclipse将创建通用Hello, World的变化是增加了以下<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" 
     ... 

...并增加在res以下/ XML/my_paths.xml

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

我的主要问题是杉木st,但是当你在这个话题上时,关于问题2-4的讨论也会很有趣。

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

编辑

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

如果我选择Dropbox,我可以选择位置很好。 Dropbox发送通知“上传到Dropbox”,然后发送“上传失败:my_file.txt”。

如果我选择Gmail,我可以填充收件人并显示文件附加,但在“发送邮件..”后,我收到“无法发送附件”。

+3

我们希望您不要通过并删除其他人花时间回答的问题。问题不仅仅是为了你的利益,而且对于未来很多人可能正在寻找类似的东西。 –

+0

@BradLarson我打破了一个问题/一个职位的规则,并付出了代价。我这样做使得它对于一些善良的灵魂敞开心扉,就像那些回答在不重要问题的答案中给予明智的答案,而没有解决问题的症结(第1部分)的人一样。正如我在评论中提到的那样,我将把问题分解成几个子部分。我尝试了一个重点放在第1部分的问题。没有人提出答案。我宁愿删除它。最终,由于提出这个问题,我意识到问题更复杂,例如用户处于飞行模式时。 TBC。 – Calaf

回答

0

2.是的,的确如此。您看到ContentProvider是一个抽象类,因此要使用自定义内容提供者,必须扩展它。由于FileProviderContentProvider(不是抽象的)的子类,所以程序员可以使用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.No,这不是安全地删除该文件,你叫startActivity()之后。因为startActivity()是非阻塞函数调用,它会立即返回。您必须等待文件共享的时间。你可以使用startActivityForResult()来做到这一点。看看它是否有用。

+0

我很高兴能够以这种形式问及第2部分。在任何情况下,用你的代码替换我的代码都无济于事。你可以在你身边运行并确认它是否有效?你错过了一个try-catch块,所以我猜你没有运行它。 – Calaf

+0

代码是为了得到您可以使用相同的文件对象的想法。你很近,我成功编译了代码。我运行了代码,它显示了我可以选择与之共享文件的应用程序列表。我在发布时删除了try-catch块。 – imranhasanhira

+0

我发布的代码也运行良好,并提供了要发送到的应用程序列表。我使用Dropbox和Gmail提供的通知更新了问题。你能确认你能够上传到你的一方或双方吗? – Calaf

6

1.

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

设置FLAG_GRANT_READ_URI_PERMISSION,以便启动的活动被授予对FileProvider构造的URI的读取权限。

最后,“text/plain”可能比MIME类型的“application/txt”更好。

我遇到了一些问题,使它能够跨设备一致地工作。这是我最好的选择到目前为止,(如果我完善它会编辑):

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似乎将附加数据解释为默认的收件人地址。非常讨厌!如果有人有更好的解决方案,请分享(双关语,我来自哥德堡)。

+1

我能够从我的应用程序的安全存储使用FileProvider附加文件的唯一方法是手动授予和撤销我的文件的权限,因为在这个答案http://stackoverflow.com/a/18332000/1082344(工作在三星Galaxy Tab 10.1与Android 3.1版本,Galaxy Ace与Android 2.3.4和Galaxy S3与Android 4.1.2) – IsaacCisneros