Wysyłanie prośby o udostępnienie pliku

Gdy aplikacja chce uzyskać dostęp do pliku udostępnionego przez inną aplikację, aplikacja wysyłająca żądanie (klient) zwykle wysyła żądanie do aplikacji, która udostępnia pliki (serwera). W większości przypadków żądanie uruchamia Activity w aplikacji serwera, która wyświetla pliki, które użytkownik może udostępnić. Użytkownik wybiera plik, a aplikacja serwera zwraca identyfikator URI treści pliku do aplikacji klienckiej.

Z tej lekcji dowiesz się, jak aplikacja kliencka żąda dostępu do pliku z aplikacji serwera, otrzymuje identyfikator URI treści pliku od aplikacji serwera i otwiera plik za pomocą tego identyfikatora.

Wyślij prośbę o dostęp do pliku

Aby zażądać pliku z aplikacji serwera, aplikacja kliencka wywołuje startActivityForResult za pomocą Intent zawierającego działanie, na przykład ACTION_PICK, oraz typ MIME, jaki może obsłużyć aplikacja kliencka.

Na przykład ten fragment kodu pokazuje, jak wysłać Intent do aplikacji serwera w celu uruchomienia Activity opisanego w sekcji Udostępnianie pliku:

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);
        ...
    }
    ...
}

Dostęp do żądanego pliku

Aplikacja serwera wysyła identyfikator URI treści pliku z powrotem do aplikacji klienckiej w obiekcie Intent. Ten Intent jest przekazywany do aplikacji klienckiej w zastąpieniu onActivityResult(). Gdy aplikacja kliencka ma identyfikator URI treści pliku, może uzyskać do niego dostęp, korzystając ze swojego identyfikatora FileDescriptor.

Ten proces zachowuje bezpieczeństwo plików, ponieważ identyfikator URI treści jest jedynym fragmentem danych, który otrzymuje aplikacja kliencka. Ten identyfikator URI nie zawiera ścieżki katalogu, dlatego aplikacja kliencka nie może wykryć ani otworzyć żadnych innych plików w aplikacji serwera. Dostęp do pliku ma tylko aplikacja kliencka i tylko w przypadku uprawnień przyznanych przez aplikację serwera. Uprawnienia są tymczasowe, więc po zakończeniu stosu zadań aplikacji klienckiej plik nie jest już dostępny poza aplikacją serwerową.

Następny fragment kodu pokazuje, jak aplikacja kliencka obsługuje żądania Intent wysyłane z aplikacji serwera i jak aplikacja kliencka pobiera FileDescriptor za pomocą identyfikatora URI treści:

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();
            ...
        }
    }

Metoda openFileDescriptor() zwraca ParcelFileDescriptor dla pliku. Aplikacja kliencka otrzymuje z tego obiektu obiekt FileDescriptor, którego może użyć do odczytu pliku.

Dodatkowe informacje na ten temat znajdziesz tutaj: