13

コンテンツ プロバイダーに保存する必要がある大きなファイル (画像とビデオ) がいくつかあります。アンドロイドのドキュメントは示しています...

テーブル自体に入れるには大きすぎるバイト データ (大きなビットマップ ファイルなど) を公開している場合、データをクライアントに公開するフィールドには、実際には content: URI 文字列が含まれている必要があります。これは、クライアントがデータ ファイルにアクセスできるようにするフィールドです。レコードには、そのファイルのデバイス上の正確なファイル パスをリストする「_data」という名前の別のフィールドも必要です。このフィールドは、クライアントによる読み取りではなく、ContentResolver による読み取りを意図しています。クライアントは、アイテムの URI を保持するユーザー向けフィールドで ContentResolver.openInputStream() を呼び出します。ContentResolver はそのレコードの「_data」フィールドを要求し、クライアントよりも高い権限を持っているため、そのファイルに直接アクセスして、ファイルの読み取りラッパーをクライアントに返すことができるはずです。-- http://developer.android.com/guide/topics/providers/content-providers.html#creating

例を見つけるのに苦労しています。特に、ImageView のコンテキストでビットマップを使用したいと考えています。次のコードの準コードを検討してください(機能しません)...

ImageView iv = ....
String iconUri = cursor.getString(cursor.getColumnIndex(Table.ICON));
iv.setImageURI(Uri.parse(iconUri));

観察/問題...

  1. 保存/回復されたURIを正しく再構築するにはどうすればよいですか? (表中のテキストです)
  2. setImageURI 実装はコンテンツ解決 openInputStream を利用するため、これは機能するはずです。

    String scheme = mUri.getScheme();
    ...
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
            || ContentResolver.SCHEME_FILE.equals(scheme)) {
      try {
        d = Drawable.createFromStream(
                mContext.getContentResolver().openInputStream(mUri),
                null);
    

    --frameworks/base/core/java/android/widget/ImageView.java

私はそれを働かせました。MediaStore と MediaProvider からヒントを得ました。データを含むファイルには、コンテンツ プロバイダー (ディレクトリ)、列名、行 ID、およびメディア タイプに基づいて名前が付けられます。コンテンツ リゾルバーは、次のようにファイル記述子を取得します...

Uri iconUri = Uri.withAppendedPath(Table.getUri(cursor), Table.ICON);
ib.setImageURI(iconUri);

...そして、コンテンツ プロバイダーは親切に対応します...

@Override
public ParcelFileDescriptor openFile (Uri uri, String mode) {
int imode = 0;
if (mode.contains("w")) imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;
List<String> pseg = uri.getPathSegments();
if (pseg.size() < 3) return null;

try {
    File filePath = filePathFromRecord(pseg.get(2), pseg.get(1));
    return ParcelFileDescriptor.open(filePath, imode);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
return null;
}
4

3 に答える 3

12

質問の下半分でphreedが与える解決策は、基本的に正しいです。ここにさらに詳細を追加してみます。

するとgetContentResolver().openInputStream(...)、コンテンツ リゾルバーはコンテンツ プロバイダーにアクセスし、そのopenFileメソッドを呼び出します。これはContentProvider.javaopenFileでどのように見えるかです:

public ParcelFileDescriptor openFile(Uri uri, String mode)
     throws FileNotFoundException {
 throw new FileNotFoundException("No files supported by provider at "
         + uri);
}

したがって、これは「サポートされているファイルがありません...」エラーの正確な原因を説明しています! openFileサブクラスでメソッドをオーバーライドし、独自の実装を提供することで、これを回避できます。それは素晴らしいです: クライアントがopenInputStreamまたはopenOutputStream.

Phreed の質問のコード サンプルは、実装がどのように見えるかのヒントを提供します。これは、必要に応じてディレクトリとファイルも作成する、わずかに変更されたバージョンです。私はこのようなことに初心者なので、これは最適な方法ではないかもしれませんが、アイデアは得られます。まず、おそらく外部ストレージが利用可能かどうかを確認する必要があります。

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File root = new File(Environment.getExternalStorageDirectory(), 
            "/Android/data/com.example.myapp/cache");
    root.mkdirs();
    File path = new File(root, uri.getEncodedPath());
    // So, if the uri was content://com.example.myapp/some/data.xml,
    // we'll end up accessing /Android/data/com.example.myapp/cache/some/data.xml

    int imode = 0;
    if (mode.contains("w")) {
            imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
            if (!path.exists()) {
                try {
                    path.createNewFile();
                } catch (IOException e) {
                    // TODO decide what to do about it, whom to notify...
                    e.printStackTrace();
                }
            }
    }
    if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
    if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;        

    return ParcelFileDescriptor.open(path, imode);
}
于 2010-12-02T14:25:30.907 に答える
1

Android には、openFile() メソッドの実装を非常に簡単にするヘルパー メソッド openFileHelper() が用意されています。この方法を使用するには、「_data」という名前の列にファイルの場所を指定するだけです。

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
      throws FileNotFoundException {
   if (URI_MATCHER.match(uri) != PHOTO_ID) {
      throw new IllegalArgumentException
            ("URI invalid. Use an id-based URI only.");
   }
   return openFileHelper(uri, mode);
}

詳細はこちら

于 2013-02-17T10:05:52.493 に答える
0

ただし、ファイルの書き込みには問題があります。コンテンツ プロバイダーは、書き込みが完了したことをどのように認識しますか?

ContentResolver.openOutputStream()を介して取得した OutputStream を閉じるとき、対応する (カスタム) ContentProviderが何らかのカスタム アクションを実行することを望みます。現在、一時ファイルにFileObserverを作成して います が、それを行う方法が間違っているようです。私の最初の考えは、 ContentProvider.openFile()メソッドによって生成されたParcelFileDescriptorをサブタイプ化することでしたが、それはうまくいかないようでした。

そうは言っても、MyParcelFileDescriptor の使用につながるいくつかのエラーがありました。

  • ファイル記述子が時期尚早にガベージ コレクションされないことを保証しませんでした。
  • 私はfinally()をオーバーライドしようとはしませんでした(ドキュメンテーションはこれを行う場所であることを示唆しているようですが、close()は私にとって正しい選択であるように思われました.

要するに、 ContentResolverから見たファイル オブジェクトとContentProviderから見たファイル オブジェクトの間に相互作用はありますか?

于 2010-11-13T20:19:29.237 に答える