6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

共有ファイルのリクエスト

あるアプリによって共有されているファイルに別のアプリがアクセスする必要がある場合、リクエスト側のアプリ(クライアント)は、通常、ファイルを共有しているアプリにリクエストを送信します。通常はこのリクエストによって、共有ファイルが表示されているサーバーアプリで Activity が開始されます。ユーザーがファイルを選択すると、サーバーアプリはファイルのコンテンツ URI をクライアント アプリに返します。

このレッスンでは、クライアント アプリがサーバーアプリにファイルをリクエストし、サーバーアプリからファイルのコンテンツ URI を受け取って、そのコンテンツ URI を使用してファイルを開く方法を説明します。

ファイルのリクエストを送信する

サーバーアプリにファイルをリクエストするために、クライアント アプリは ACTION_PICK などのアクションを含む Intent と、そのクライアント アプリが処理できる MIME タイプを使用して startActivityForResult を呼び出します。

たとえば、次のコード スニペットは、ファイルの共有で説明されている Activity を開始するために、サーバーアプリに Intent を送信する方法を示しています。

Kotlin

    class MainActivity : Activity() {
        private lateinit var requestFileIntent: Intent
        private lateinit var inputPFD: ParcelFileDescriptor
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            requestFileIntent = Intent(Intent.ACTION_PICK).apply {
                type = "image/jpg"
            }
            ...
        }
        ...
        private fun requestFile() {
            /**
             * When the user requests a file, send an Intent to the
             * server app.
             * files.
             */
            startActivityForResult(requestFileIntent, 0)
            ...
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        private Intent requestFileIntent;
        private ParcelFileDescriptor inputPFD;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            requestFileIntent = new Intent(Intent.ACTION_PICK);
            requestFileIntent.setType("image/jpg");
            ...
        }
        ...
        protected void requestFile() {
            /**
             * When the user requests a file, send an Intent to the
             * server app.
             * files.
             */
                startActivityForResult(requestFileIntent, 0);
            ...
        }
        ...
    }
    

リクエストされたファイルにアクセスする

サーバーアプリは、ファイルのコンテンツ URI を Intent でクライアント アプリに送り返します。この Intent は、その onActivityResult() のオーバーライドでクライアント アプリに渡されます。ファイルのコンテンツ URI を取得したクライアント アプリは、ファイルにアクセスするためにそのファイルの FileDescriptor を取得します。

このプロセスでは、クライアント アプリが受け取る唯一のデータがコンテンツ URI であるため、ファイルのセキュリティが確保されます。この URI にはディレクトリ パスが含まれていません。したがって、クライアント アプリではサーバーアプリにある他のファイルを検出することも、開くこともできません。クライアント アプリのみが、サーバーアプリによって付与された権限でのみ目的のファイルにアクセスできます。権限は一時的なものであるため、クライアント アプリのタスクスタックが終了すると、サーバーアプリ外からファイルにアクセスできなくなります。

次のスニペットは、クライアント アプリがサーバーアプリから送信された Intent を処理する方法、およびクライアント アプリがコンテンツ URI を使用して FileDescriptor を取得する方法を示しています。

Kotlin

    /*
     * When the Activity of the app that hosts files sets a result and calls
     * finish(), this method is invoked. The returned Intent contains the
     * content URI of a selected file. The result code indicates if the
     * selection worked or not.
     */
    public override fun onActivityResult(requestCode: Int, resultCode: Int, returnIntent: Intent) {
        // If the selection didn't work
        if (resultCode != Activity.RESULT_OK) {
            // Exit without doing anything else
            return
        }
        // Get the file's content URI from the incoming Intent
        returnIntent.data?.also { returnUri ->
            /*
             * Try to open the file for "read" access using the
             * returned URI. If the file isn't found, write to the
             * error log and return.
             */
            inputPFD = try {
                /*
                 * Get the content resolver instance for this context, and use it
                 * to get a ParcelFileDescriptor for the file.
                 */
                contentResolver.openFileDescriptor(returnUri, "r")
            } catch (e: FileNotFoundException) {
                e.printStackTrace()
                Log.e("MainActivity", "File not found.")
                return
            }

            // Get a regular file descriptor for the file
            val fd = inputPFD.fileDescriptor
            ...
        }
    }
    

Java

        /*
         * When the Activity of the app that hosts files sets a result and calls
         * finish(), this method is invoked. The returned Intent contains the
         * content URI of a selected file. The result code indicates if the
         * selection worked or not.
         */
        @Override
        public void onActivityResult(int requestCode, int resultCode,
                Intent returnIntent) {
            // If the selection didn't work
            if (resultCode != RESULT_OK) {
                // Exit without doing anything else
                return;
            } else {
                // Get the file's content URI from the incoming Intent
                Uri returnUri = returnIntent.getData();
                /*
                 * Try to open the file for "read" access using the
                 * returned URI. If the file isn't found, write to the
                 * error log and return.
                 */
                try {
                    /*
                     * Get the content resolver instance for this context, and use it
                     * to get a ParcelFileDescriptor for the file.
                     */
                    inputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    Log.e("MainActivity", "File not found.");
                    return;
                }
                // Get a regular file descriptor for the file
                FileDescriptor fd = inputPFD.getFileDescriptor();
                ...
            }
        }
    

メソッド openFileDescriptor() は、ファイルの ParcelFileDescriptor を返します。クライアント アプリは、このオブジェクトから FileDescriptor オブジェクトを取得し、これを使用してファイルを読み取ることができます。

その他の関連情報については、以下をご覧ください。