12

バックグラウンド

ファイルの同期に SD カードを多用するアプリがいくつかあります。Kitkat での外部 SD カード アクセスの破損は依然として大きな問題ですが、Lollipop で利用できる新しい API を使用してこれを解決しようとしています。

SD カードへのアクセス許可を正常に要求して保持し、アクセス許可アクティビティから返されたルート Uri にファイルを一覧表示できます。

これがどのように行われるかについての詳細は、こちらを参照してください: how-to-use-the-new-sd-card-access-api-presented-for-lollipop

その後、ユーザーは任意のフォルダー/サブフォルダーを選択して同期することができ、フォルダー ドキュメント Uri を文字列としてデータベースに保存します。

問題

後で、アプリが再起動された後、ファイルの同期が開始される可能性があります。次に、サブフォルダー内のファイルを一覧表示しようとします (覚えておいてください、私には適切なアクセス許可が付与されており、この永続性が機能し、すべての子へのアクセスも許可されます)。

次に、保存された文字列から DocumentFile の新しいインスタンスを作成し、ファイルを一覧表示しようとします。

  DocumentFile dir = DocumentFile.fromTreeUri(ctx, Uri.parse(storedUri));
  dir.listFiles();

問題は、listFiles が常に付与されたルート Uri の子を返し、DocumentFile.fromTreeUri メソッドで指定した実際の Uri の子を返さないことです。

DocumentFileのソース コードを調べたところ、そこにバグがあるようです。具体的には、Uri をさらに変更する必要はありません。

public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
  final int version = Build.VERSION.SDK_INT;
  if (version >= 21) {
    return new TreeDocumentFile(null, context,
      DocumentsContractApi21.prepareTreeUri(treeUri));
  } else {
  return null;
}

DocumentsContractApi21.prepareTreeUri のソースを見ると、Uri が再構築されていることがわかります。

 public static Uri prepareTreeUri(Uri treeUri) {
   return DocumentsContract.buildDocumentUriUsingTree(treeUri,
     DocumentsContract.getTreeDocumentId(treeUri));
 }

そしてそれが呼び出すメソッド:

 public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
   return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
                 .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
                 .appendPath(documentId).build();
 }

 public static String getTreeDocumentId(Uri documentUri) {
   final List<String> paths = documentUri.getPathSegments();
   if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) {
     return paths.get(1);
   }
   throw new IllegalArgumentException("Invalid URI: " + documentUri);
 }

getTreeDocumentId によって検出されたドキュメント ID は、メソッドが呼び出される Uri に関係なく、常にルート Uri ID に対応します。これにより、提供されたフレームワーク メソッドを使用してサブ フォルダーの子を一覧表示することができなくなります。

解決

fromTreeUri メソッドを修正して、ルート ドキュメントの Uri ID を常に使用しないようにしてください。

次の醜いハックを実行すると、問題が修正されますが、これは避けたいと思います。

  Class<?> c = Class.forName("android.support.v4.provider.TreeDocumentFile");
  Constructor<?> constructor = c.getDeclaredConstructor(DocumentFile.class, Context.class, Uri.class);
  constructor.setAccessible(true);

  DocumenFile dir = (DocumentFile) constructor.newInstance(null, mCtx, treeUri);
  dir.listFiles();
4

1 に答える 1