3

この質問にはいくつかのサブ質問が含まれていました。この質問から始めて、これらをフォークしています。この質問を削除して、最終的にクリーンアップします。

次のプログラムは、理論上、hello-world テキスト ファイルを共有します。コードは実行されますが、Dropbox または Gmail への共有 (具体的な例を 2 つだけ示します) は失敗します。

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 に値を追加する以外に、Eclipse が作成send_toするジェネリックに対して行った唯一の他の変更は、AndroidManifest.xml に次のタグを追加することです。Hello, World<provider>

<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>

私の主な質問は最初の質問ですが、このトピックについて話している間に、質問 2 から 4 についての議論も興味深いでしょう。

  1. 上記のプログラムが失敗するのはなぜですか?
  2. custom が必要な場合はContentProviderそのクラスを拡張する必要がありますが、 だけが必要な場合はFileProvider、派生なしでそのクラスを使用できますか?
  3. このコードでは、 を 1filename回と で 2 回使用する必要がopenFileOutputありましたnew File()。この重複を回避する方法はありますか (同じファイルが参照されていることを保証します)?
  4. が呼び出された直後にファイルを削除しても安全ですstartActivity(..)か、それともファイルがアップロード/共有されたことを知るのを待つコールバックを考案する必要がありますか? (実際のファイルは、共有/アップロードに時間がかかる場合があります。)

編集

コードは正常に実行され、送信先のアプリのリストが表示されます。

Dropbox を選択すると、問題なく場所を選択できます。Dropbox は、「Dropbox にアップロードしています」という通知に続いて「アップロードに失敗しました: my_file.txt」という通知を送信します。

Gmailを選択すると、受信者を入力でき、ファイルが添付されているように見えますが、「メッセージを送信しています..」の後に「添付ファイルを送信できませんでした」と表示されます。

4

2 に答える 2

6

1.

FileProvider.getUriForFile(...)URI の作成に使用します。これにより、開始されたアクティビティが FileProvider に送信されます (その後、アプリのプライベート ファイル ディレクトリからファイルを提供できます)。Uri.fromFile(...)開始されたアクティビティがプライベート ディレクトリに直接アクセスしようとするため、機能しません。

FileProviderFLAG_GRANT_READ_URI_PERMISSIONが構築した URI に対して、開始されたアクティビティに読み取り権限が付与されるように設定します。

最後に、MIME タイプとしては、「application/txt」よりも「text/plain」の方がうまく機能する可能性があります。

これをデバイス間で一貫して機能させるには、いくつかの問題がありました。これはこれまでの私の最善の策です(改良した場合は編集します):

    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 では機能しますが、Samsung タブレットでは機能しません。権限を付与するために、Samsung タブレットは URI をデータとして必要とするようです。またintent.getData()、以前の呼び出しをキャンセルしintent.setType()たり、その逆の呼び出しをキャンセルしたりするため、上記のように組み合わせた方法を使用する必要があることに注意してください。

Gmail は追加データをデフォルトの宛先として解釈しているようです。非常に迷惑です!誰かがより良い解決策を持っている場合は、それを共有してください(しゃれが意図されています、私はヨーテボリ出身です)。

于 2013-12-28T21:47:28.897 に答える
0

2.はい、そうです。これContentProviderは抽象クラスであるため、カスタム コンテンツ プロバイダーを使用するには、それを拡張する必要があります。(抽象ではない)FileProviderのサブクラスであるため、プログラマーはサブクラス化せずに使用できます。ContentProviderFileProvider

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()。それが目的に役立つかどうかを確認してください。

于 2013-09-25T17:40:08.877 に答える