2013-09-25 206 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