Dosya paylaşma

Uygulamanızı içerik URI'leri kullanarak dosya paylaşacak şekilde ayarladıktan sonra, diğer uygulamaların bu dosyalar için istek gönderebilir. Bu isteklere yanıt vermenin bir yolu da diğer uygulamaların çağırabileceği sunucu uygulamasından gelen bir arayüz oluşturur. Bu yaklaşım sayesinde kullanıcıların sunucu uygulamasından bir dosya seçmelerini ve daha sonra seçilen dosyanın içerik URI'si.

Bu derste, uygulamanızda dosya seçimi (Activity) nasıl oluşturulacağı açıklanmaktadır. yanıt veren bir reklam öğesi ekler.

Dosya istekleri alma

İstemci uygulamalarından dosya istekleri almak ve içerik URI'si ile yanıt vermek için uygulamanız bir dosya seçimi Activity sağlayın. İstemci uygulamaları bunu başlatır İşlemi içeren bir Intent ile startActivityForResult() çağrısı yaparak Activity ACTION_PICK. İstemci uygulaması aradığında startActivityForResult(), uygulamanız şunları yapabilir: istemci uygulamasına, kullanıcının seçtiği dosya için içerik URI'si biçiminde bir sonuç döndürür.

İstemci uygulamasında bir dosya isteğini nasıl uygulayacağınızı öğrenmek için Paylaşılan dosya isteğinde bulunma.

Dosya seçimi etkinliği oluşturma

Activity dosya seçimini ayarlamak için öncelikle Manifest'inizde Activity ile birlikte bir intent filtresi ACTION_PICK ve CATEGORY_DEFAULT ve CATEGORY_OPENABLE. MIME türü filtreleri de ekle diğer uygulamalara sunduğu dosyalar için Google Etiket Yöneticisi'ni kullanabilirsiniz. Aşağıdaki snippet, yeni Activity ve intent filtresi:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
        <application>
        ...
            <activity
                android:name=".FileSelectActivity"
                android:label="@File Selector" >
                <intent-filter>
                    <action
                        android:name="android.intent.action.PICK"/>
                    <category
                        android:name="android.intent.category.DEFAULT"/>
                    <category
                        android:name="android.intent.category.OPENABLE"/>
                    <data android:mimeType="text/plain"/>
                    <data android:mimeType="image/*"/>
                </intent-filter>
            </activity>

Kodda dosya seçimi etkinliğini tanımlama

Sonra, mevcut dosyaları gösteren bir Activity alt sınıfı tanımlayın dahili depolama alanındaki files/images/ dizinini destekler ve kullanıcının seçin. Aşağıdaki snippet, bunun nasıl tanımlanacağını gösterir Activity ve kullanıcının seçimine yanıt verin:

Kotlin

class MainActivity : Activity() {

    // The path to the root of this app's internal storage
    private lateinit var privateRootDir: File
    // The path to the "images" subdirectory
    private lateinit var imagesDir: File
    // Array of files in the images subdirectory
    private lateinit var imageFiles: Array<File>
    // Array of filenames corresponding to imageFiles
    private lateinit var imageFilenames: Array<String>

    // Initialize the Activity
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE")
        // Get the files/ subdirectory of internal storage
        privateRootDir = filesDir
        // Get the files/images subdirectory;
        imagesDir = File(privateRootDir, "images")
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles()
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null)
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
        ...
    }
    ...
}

Java

public class MainActivity extends Activity {
    // The path to the root of this app's internal storage
    private File privateRootDir;
    // The path to the "images" subdirectory
    private File imagesDir;
    // Array of files in the images subdirectory
    File[] imageFiles;
    // Array of filenames corresponding to imageFiles
    String[] imageFilenames;
    // Initialize the Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent =
                new Intent("com.example.myapp.ACTION_RETURN_FILE");
        // Get the files/ subdirectory of internal storage
        privateRootDir = getFilesDir();
        // Get the files/images subdirectory;
        imagesDir = new File(privateRootDir, "images");
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles();
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null);
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
         ...
    }
    ...
}

Dosya seçimine yanıt verme

Uygulamanız, paylaşılan bir dosyayı seçtiğinde hangi dosyanın seçildiğini belirlemeli ve daha sonra dosya için bir içerik URI'si oluşturabilirsiniz. Activity, Kullanıcı bir dosya adını tıkladığında ListView içindeki kullanılabilir dosyaların listesi sistem, seçili dosyayı alabileceğiniz onItemClick() yöntemini çağırır.

Bir dosyanın URI'sini bir uygulamadan diğerine göndermek için bir intent kullanırken başka birinin kullandığı URI'ları alırken okuyabiliyor. Android 6.0 (API düzeyi 23) ve sonraki sürümleri çalıştıran cihazlarda bunu yapma özel Android'in bu sürümünde izin modelinde yapılan değişiklikler nedeniyle, READ_EXTERNAL_STORAGE olmak tehlikeli izni kapsayan bir açık kaynak belirtin.

Bu noktaları göz önünde bulundurarak, Uri.fromFile(), bazı dezavantajlar içerir. Bu yöntem:

  • Profiller arasında dosya paylaşımına izin vermez.
  • Uygulamanızda WRITE_EXTERNAL_STORAGE. izni.
  • Alıcı uygulamaların READ_EXTERNAL_STORAGE izni. Bu, Gmail gibi bu izne sahip olmayan önemli paylaşım hedeflerinde başarısız olur.

Uri.fromFile() yerine diğer uygulamalara izin vermek için URI izinlerini kullanabilirsiniz belirli URI'lere erişim. file:// URI'lerinde URI izinleri çalışmasa da Uri.fromFile() tarafından oluşturulan İçerik Sağlayıcılarla ilişkili URI'lar üzerinde çalışır. İlgili içeriği oluşturmak için kullanılan FileProvider API şunları yapabilir: bu tür URI'ler oluşturmanıza yardımcı olur. Bu yaklaşım, bunu harici depolamada ancak niyeti gönderen uygulamanın yerel depolama alanında bulabilirsiniz.

onItemClick() uygulamasında bir File nesnesini ayarlayın ve bunu bağımsız değişken olarak getUriForFile() ve ilgili yetki belgesinde belirttiğiniz FileProvider için <provider> öğesi. Sonuçta ortaya çıkan içerik URI'si; yetkiliyi, yani dosyanın dizinini (XML meta verilerinde belirtildiği şekliyle) ve uzantısına sahip olur. FileProvider, dizinleri yola nasıl eşler? XML meta verilerine dayalı segmentler başlıklı bölümde açıklanmıştır Paylaşılabilir dizinleri belirtin.

Aşağıdaki snippet, seçilen dosyanın nasıl algılanacağını ve dosya için içerik URI'sinin nasıl alınacağını gösterir:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            /*
             * Get a File for the selected file name.
             * Assume that the file names are in the
             * imageFilename array.
             */
            val requestFile = File(imageFilenames[position])
            /*
             * Most file-related method calls need to be in
             * try-catch blocks.
             */
            // Use the FileProvider to get a content URI
            val fileUri: Uri? = try {
                FileProvider.getUriForFile(
                        this@MainActivity,
                        "com.example.myapp.fileprovider",
                        requestFile)
            } catch (e: IllegalArgumentException) {
                Log.e("File Selector",
                        "The selected file can't be shared: $requestFile")
                null
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            /*
             * When a filename in the ListView is clicked, get its
             * content URI and send it to the requesting app
             */
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                /*
                 * Get a File for the selected file name.
                 * Assume that the file names are in the
                 * imageFilename array.
                 */
                File requestFile = new File(imageFilename[position]);
                /*
                 * Most file-related method calls need to be in
                 * try-catch blocks.
                 */
                // Use the FileProvider to get a content URI
                try {
                    fileUri = FileProvider.getUriForFile(
                            MainActivity.this,
                            "com.example.myapp.fileprovider",
                            requestFile);
                } catch (IllegalArgumentException e) {
                    Log.e("File Selector",
                          "The selected file can't be shared: " + requestFile.toString());
                }
                ...
            }
        });
        ...
    }

Yalnızca dizinde bulunan dosyalar için içerik URI'leri oluşturabileceğinizi unutmayın. <paths> öğesini içeren meta veri dosyasında belirttiğiniz Paylaşılabilir dizinleri belirtme bölümünde açıklanmıştır. Arama yaptığınızda Şunun için getUriForFile(): File belirtirken belirtmediğiniz bir yolda IllegalArgumentException.

Dosya için gerekli izinleri verme

Artık başka bir uygulamayla paylaşmak istediğiniz dosyanın İçerik URI'sına sahip olduğunuza göre istemci uygulamasının dosyaya erişmesine izin vermelidir. Erişime izin vermek için aşağıdaki adımları uygulayarak istemci uygulamasına izin verin: içerik URI'sini bir Intent öğesine eklemek ve ardından Intent. Verdiğiniz izinler geçicidir ve süresi dolar. ve alıcı uygulamanın görev yığını tamamlandığında otomatik olarak oluşturulur.

Aşağıdaki kod snippet'i, dosya için okuma izninin nasıl ayarlanacağını gösterir:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                // Grant temporary read permission to the content URI
                resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                ...
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    // Grant temporary read permission to the content URI
                    resultIntent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
                ...
             }
             ...
        });
    ...
    }

Dikkat: Tek yapmanız gereken setFlags() numaralı telefonu aramak Bu da, geçici erişim izinlerini kullanarak dosyalarınıza güvenli bir şekilde erişim izni vermenin bir yoludur. Telefon etmekten kaçının Context.grantUriPermission() yöntem için dosyanın içerik URI'sini kullanın, çünkü bu yöntem yalnızca Context.revokeUriPermission() aranıyor.

Uri.fromFile() kullanmayın. Uygulamaları almayı zorunlu kılar READ_EXTERNAL_STORAGE iznini almak için kullanıcılar arasında ve farklı sürümlerde hiç çalışmayacaktır Android 4.4'ten (API düzeyi 19) düşük bir sürüme sahip olmak için uygulamanın WRITE_EXTERNAL_STORAGE olmasını sağlayın. Gmail uygulaması gibi gerçekten önemli paylaşım hedeflerinde, READ_EXTERNAL_STORAGE nedeniyle çağrının başarısız olmasına neden olabilir. Bunun yerine, diğer uygulamaların belirli URI'lere erişmesine izin vermek için URI izinlerini kullanabilirsiniz. URI izinleri, Uri.fromFile(), öyle İçerik Sağlayıcılarla ilişkili Uris'te çalışma. Sırf bu amaçla kendi uygulamanızı uygulamak yerine, FileProvider kullanabilirsiniz ve kullanmanız gerekir Dosya paylaşımı bölümünde açıklandığı gibi.

Dosyayı, istekte bulunan uygulamayla paylaşın

Dosyayı isteyen uygulamayla paylaşmak için Intent hem de içerik URI'sini ve setResult() izinlerini içerir. Tanımladığınız Activity tamamlandığında, sistem, içerik URI'sini içeren Intent öğesini istemci uygulamasına gönderir. Aşağıdaki kod snippet'inde bunu nasıl yapacağınız gösterilmektedir:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                ...
                // Put the Uri and MIME type in the result Intent
                resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri))
                // Set the result
                setResult(Activity.RESULT_OK, resultIntent)
            } else {
                resultIntent.setDataAndType(null, "")
                setResult(RESULT_CANCELED, resultIntent)
            }
        }
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    ...
                    // Put the Uri and MIME type in the result Intent
                    resultIntent.setDataAndType(
                            fileUri,
                            getContentResolver().getType(fileUri));
                    // Set the result
                    MainActivity.this.setResult(Activity.RESULT_OK,
                            resultIntent);
                    } else {
                        resultIntent.setDataAndType(null, "");
                        MainActivity.this.setResult(RESULT_CANCELED,
                                resultIntent);
                    }
                }
        });

Kullanıcılara, dosya seçtiklerinde istemci uygulamasına hemen geri dönebilecekleri bir yol sunun. Bunu yapmanın bir yolu, onay işareti veya Bitti düğmesi sağlamaktır. Şununla bir yöntem ilişkilendir: düğmenin android:onClick özelliği için de kullanılmaktadır. Yöntemde finish() Örnek:

Kotlin

    fun onDoneClick(v: View) {
        // Associate a method with the Done button
        finish()
    }

Java

    public void onDoneClick(View v) {
        // Associate a method with the Done button
        finish();
    }

Daha fazla ilgili bilgi için aşağıdaki kaynakları inceleyebilirsiniz: