Paylaşılan depolama alanındaki medya dosyalarına erişme

Birçok uygulama, daha zengin bir kullanıcı deneyimi sağlamak için kullanıcıların ve harici depolama biriminde bulunan medyaya erişebilirsiniz. Çerçeve medya mağazası adı verilen, medya koleksiyonları için optimize edilmiş bir dizin sağlar. Bu da kullanıcıların bu medya dosyalarını daha kolay almasına ve güncellemesine olanak tanır. Eşit Uygulamanız kaldırıldıktan sonra bu dosyalar kullanıcının cihazında kalır.

Fotoğraf seçici

Medya mağazasına alternatif olarak, Android fotoğraf seçici kullanıcıların ihtiyaç duymadan medya dosyalarını seçmeleri için güvenli ve yerleşik bir yol sağlar Bu izin, uygulamanızın medya kitaplığının tamamına erişmesine izin verir. Bu yalnızca kullanılabilir desteklenen cihazlarda kullanabilirsiniz. Daha fazla bilgi için fotoğraf seçici rehberini inceleyin.

Medya mağazası

Medya mağazası özetiyle etkileşimde bulunmak için ContentResolver nesne alın:

Kotlin

val projection = arrayOf(media-database-columns-to-retrieve)
val selection = sql-where-clause-with-placeholder-variables
val selectionArgs = values-of-placeholder-variables
val sortOrder = sql-order-by-clause

applicationContext.contentResolver.query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)?.use { cursor ->
    while (cursor.moveToNext()) {
        // Use an ID column from the projection to get
        // a URI representing the media item itself.
    }
}

Java

String[] projection = new String[] {
        media-database-columns-to-retrieve
};
String selection = sql-where-clause-with-placeholder-variables;
String[] selectionArgs = new String[] {
        values-of-placeholder-variables
};
String sortOrder = sql-order-by-clause;

Cursor cursor = getApplicationContext().getContentResolver().query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
);

while (cursor.moveToNext()) {
    // Use an ID column from the projection to get
    // a URI representing the media item itself.
}

Sistem otomatik olarak harici depolama birimini tarar ve medya dosyaları ekler şu iyi tanımlanmış koleksiyonlara dönüştürüldü:

  • Görseller (fotoğraflar ve ekran görüntüleri dahil) DCIM/ ve Pictures/ dizinleri. Sistem bu dosyaları MediaStore.Images tablosuna ekler.
  • DCIM/, Movies/ ve Pictures/ dizinlerinde depolanan videolar. Sistem bu dosyaları MediaStore.Video tablosu.
  • Alarms/, Audiobooks/, Music/ ve şurada depolanan ses dosyaları: Notifications/, Podcasts/ ve Ringtones/ dizinleri. Ayrıca, sistem Music/ veya Movies/ içinde olan şarkı listelerini tanır Recordings/ içindeki ses kayıtlarının yanı sıra dizinler dizin. Sistem bu dosyaları MediaStore.Audio tablosu. Recordings/ dizini Android 11'de (API düzeyi 30) kullanılamaz ve daha düşük olur.
  • Download/ dizininde depolanan indirilen dosyalar. Şu tarihte: Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran cihazlarda, bu dosyalar şurada depolanır: MediaStore.Downloads tablosunu oluşturalım. Bu tablo, Android 9 (API düzeyi 28) ve önceki sürümlerde kullanılamaz.

Medya mağazası MediaStore.Files. İçeriği uygulamanızın kapsamlı depolama alanını hedefleyen uygulamalarda kullanılabilir. Android 10 veya sonraki sürümler.

  • Kapsamlı depolama alanı etkinleştirilirse koleksiyonda yalnızca fotoğraf, video ve ve uygulamanızın oluşturduğu ses dosyaları. Çoğu geliştiricinin Diğer uygulamalardaki medya dosyalarını görüntülemek için MediaStore.Files (ancak uygulamanız varsa) bunun için özel bir şart varsa READ_EXTERNAL_STORAGE izni gerekir. Ancak MediaStore kullanmanızı öneririz Uygulamanızın oluşturmadığı dosyaları açmak için kullanılan API'ler.
  • Kapsamlı depolama alanı kullanılamıyorsa veya kullanılmıyorsa koleksiyonda izin vermiş olursunuz.

Gerekli izinleri isteme

Medya dosyaları üzerinde işlem gerçekleştirmeden önce, uygulamanızın bu dosyalara erişmesi için gereken izinlere sahip olmalıdır. Ancak şunları yapmamaya dikkat edin: uygulamanızın ihtiyaç duymadığı veya kullanmadığı izinleri beyan etme

Depolama izinleri

Uygulamanızın depolama alanına erişmek için izinlere ihtiyacı olup olmadığı, yalnızca kendi medya dosyalarına mı yoksa diğer uygulamalar tarafından oluşturulan dosyalara mı eriştiğine bağlıdır.

Kendi medya dosyalarınıza erişme

Android 10 veya sonraki sürümleri çalıştıran cihazlarda bu dosyalara erişmek ve bunları değiştirmek için MediaStore.Downloads klasöründeki dosyalar dahil olmak üzere uygulamanızın sahibi olduğu koleksiyonudur. Örneğin, bir kamera uygulaması geliştiriyorsanız uygulamanızın medya mağazasına yazdığınız resimlerin sahibi olduğu için çektiği fotoğraflara erişmek için depolamayla ilgili izinler istemeniz gerekmez.

Diğer uygulamaların erişimi medya dosyaları

Diğer uygulamaların oluşturduğu medya dosyalarına erişmek için depolama alanıyla ilgili uygun izinleri belirtmeniz ve dosyaların aşağıdaki medya koleksiyonlarından birinde bulunması gerekir:

Bir dosya MediaStore.Images, MediaStore.Video veya MediaStore.Audio sorgularından görüntülenebildiği sürece MediaStore.Files sorgusu kullanılarak da görüntülenebilir.

Aşağıdaki kod snippet'i, uygun depolama alanının nasıl bildirileceğini göstermektedir. izinler:

<!-- Required only if your app needs to access images or photos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<!-- Required only if your app needs to access videos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- Required only if your app needs to access audio files
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="29" />

Eski cihazlarda çalışan uygulamalar için ek izinler gerekiyor

Uygulamanız Android 9 veya önceki sürümleri çalıştıran bir cihazda kullanılıyorsa ya da uygulamanız geçici olarak kapsam dışında kalmayı seçti depolama alanı kullanıyorsanız, şu istekte bulun: READ_EXTERNAL_STORAGE herhangi bir medya dosyasına erişim izniniz yok. Medya dosyalarını değiştirmek istiyorsanız şu istekte bulun: WRITE_EXTERNAL_STORAGE izin de gereklidir.

Diğer uygulamaların indirmelerine erişmek için Depolama Erişimi Çerçevesi gerekiyor

Uygulamanız, MediaStore.Downloads koleksiyonundaki bir dosyaya erişmek istiyorsa Depolama Erişim Çerçevesi'ni kullanmanız gerekir. Öğrenmek için hakkında daha fazla bilgi için bkz. paylaşılan depolama alanı.

Medya konum izni

Uygulamanız Android 10 (API düzeyi 29) veya sonraki bir sürümü hedefliyorsa ve fotoğraflardan çıkartılmamış EXIF meta verilerini almak için ACCESS_MEDIA_LOCATION izni ile birlikte çalışma zamanında bu izni isteyebilirsiniz.

Medya mağazasındaki güncellemeleri kontrol etme

Medya dosyalarına daha güvenilir şekilde erişmek için, özellikle uygulamanız URI'ları veya medya mağazası sürümünün değişip değişmediğini kontrol edin. son senkronizasyon zamanına kıyasla bir puan alırsınız. Güncelleme olup olmadığını kontrol etmek için getVersion() numaralı telefonu arayın. Döndürülen sürüm, medya deposunda her zaman değişen benzersiz bir dizedir önemli ölçüde değişiyor. Döndürülen sürüm, son senkronize edilen sürümden farklıysa uygulamanızın medya önbelleğini yeniden tarayıp senkronize edin.

Uygulama işlemi başlatılırken bu kontrolü tamamlayın. Medya mağazasını her sorguladığınızda sürümü kontrol etmeniz gerekmez.

Sürüm numarasıyla ilgili uygulama ayrıntılarını varsaymayın.

Medya koleksiyonunu sorgulama

Süre gibi belirli bir koşul grubunu karşılayan medyayı bulmak için uzun bir süre kullanıyorsanız, kararınıza benzer SQL benzeri bir seçim ifadesi kullanın. aşağıdaki kod snippet'inde gösterilmektedir:

Kotlin

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
data class Video(val uri: Uri,
    val name: String,
    val duration: Int,
    val size: Int
)
val videoList = mutableListOf<Video>()

val collection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Video.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL
        )
    } else {
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    }

val projection = arrayOf(
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
)

// Show only videos that are at least 5 minutes in duration.
val selection = "${MediaStore.Video.Media.DURATION} >= ?"
val selectionArgs = arrayOf(
    TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString()
)

// Display videos in alphabetical order based on their display name.
val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC"

val query = ContentResolver.query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query?.use { cursor ->
    // Cache column indices.
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
    val nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
    val durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
    val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        val id = cursor.getLong(idColumn)
        val name = cursor.getString(nameColumn)
        val duration = cursor.getInt(durationColumn)
        val size = cursor.getInt(sizeColumn)

        val contentUri: Uri = ContentUris.withAppendedId(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            id
        )

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList += Video(contentUri, name, duration, size)
    }
}

Java

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
class Video {
    private final Uri uri;
    private final String name;
    private final int duration;
    private final int size;

    public Video(Uri uri, String name, int duration, int size) {
        this.uri = uri;
        this.name = name;
        this.duration = duration;
        this.size = size;
    }
}
List<Video> videoList = new ArrayList<Video>();

Uri collection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
    collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}

String[] projection = new String[] {
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
};
String selection = MediaStore.Video.Media.DURATION +
        " >= ?";
String[] selectionArgs = new String[] {
    String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES));
};
String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC";

try (Cursor cursor = getApplicationContext().getContentResolver().query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)) {
    // Cache column indices.
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
    int nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
    int durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
    int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE);

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        long id = cursor.getLong(idColumn);
        String name = cursor.getString(nameColumn);
        int duration = cursor.getInt(durationColumn);
        int size = cursor.getInt(sizeColumn);

        Uri contentUri = ContentUris.withAppendedId(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList.add(new Video(contentUri, name, duration, size));
    }
}

Uygulamanızda bu tür bir sorgu gerçekleştirirken aşağıdakileri göz önünde bulundurun:

  • Bir çalışan iş parçacığında query() yöntemini çağırın.
  • Çağrı yapmanıza gerek kalmadan sütun dizinlerini önbelleğe alın getColumnIndexOrThrow() sorgu sonucundan her satır işlediğinizde.
  • Kimliği, bu örnekte gösterildiği gibi içerik URI'sine ekleyin.
  • Android 10 ve sonraki sürümleri çalıştıran cihazlarda sütunu gerekir şurada tanımlanan adlar: MediaStore API. Uygulamanızdaki bağımlı bir kitaplık, sütun bekliyorsa API'de tanımlanmamış bir ad (ör. "MimeType") Dinamik olarak CursorWrapper uygulamanızın işlemdeki sütun adını çevirin.

Dosya küçük resimlerini yükle

Uygulamanız birden fazla medya dosyası gösteriyor ve kullanıcının aşağıdakilerden birini seçmesini istiyorsa önizlemeyi yüklemek daha verimli bir süreç olduğu için resmi veya küçük resim dosyalar değil de dosyalar olabilir.

Belirli bir medya dosyasının küçük resmini yüklemek için loadThumbnail() ve yüklemek istediğiniz küçük resmin boyutunu şu kod snippet'ini kullanabilirsiniz:

Kotlin

// Load thumbnail of a specific media item.
val thumbnail: Bitmap =
        applicationContext.contentResolver.loadThumbnail(
        content-uri, Size(640, 480), null)

Java

// Load thumbnail of a specific media item.
Bitmap thumbnail =
        getApplicationContext().getContentResolver().loadThumbnail(
        content-uri, new Size(640, 480), null);

Medya dosyası açma

Bir medya dosyasını açmak için kullandığınız mantık, dosyanın medya içeriğini dosya tanımlayıcısı, dosya akışı veya dosya akışı doğrudan dosya yolunu izleyin.

Dosya açıklayıcı

Dosya tanımlayıcı kullanarak bir medya dosyasını açmak için aşağıda gösterilene benzer bir mantık kullanın: şu kod snippet'ini kullanabilirsiniz:

Kotlin

// Open a specific media item using ParcelFileDescriptor.
val resolver = applicationContext.contentResolver

// "rw" for read-and-write.
// "rwt" for truncating or overwriting existing file contents.
val readOnlyMode = "r"
resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd ->
    // Perform operations on "pfd".
}

Java

// Open a specific media item using ParcelFileDescriptor.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// "rw" for read-and-write.
// "rwt" for truncating or overwriting existing file contents.
String readOnlyMode = "r";
try (ParcelFileDescriptor pfd =
        resolver.openFileDescriptor(content-uri, readOnlyMode)) {
    // Perform operations on "pfd".
} catch (IOException e) {
    e.printStackTrace();
}

Dosya akışı

Bir medya dosyasını dosya akışı kullanarak açmak için aşağıdaki kod snippet'inde gösterilene benzer bir mantık kullanın:

Kotlin

// Open a specific media item using InputStream.
val resolver = applicationContext.contentResolver
resolver.openInputStream(content-uri).use { stream ->
    // Perform operations on "stream".
}

Java

// Open a specific media item using InputStream.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();
try (InputStream stream = resolver.openInputStream(content-uri)) {
    // Perform operations on "stream".
}

Doğrudan dosya yolları

Uygulamanızın üçüncü taraf medya kitaplıklarıyla daha sorunsuz çalışmasına yardımcı olmak için Android 11 (API düzeyi 30) ve sonraki sürümler, paylaşılan depolama alanındaki medya dosyalarına erişmek amacıyla MediaStore API'si dışındaki API'leri kullanmanıza olanak tanır. Bunun yerine, aşağıdaki API'lerden birini kullanarak medya dosyalarına doğrudan erişebilirsiniz:

  • File API
  • fopen() gibi yerel kitaplıklar

Depolamayla ilgili izinleriniz yoksa File API'yi kullanarak uygulamanıza özel dizininizdeki dosyalara ve uygulamanızla ilişkilendirilmiş medya dosyalarına erişebilirsiniz.

Uygulamanız File API'yi kullanarak bir dosyaya erişmeye çalışıyor ve gerekli izinler, FileNotFoundException gerçekleşir.

Android 10 (API) çalıştıran bir cihazda paylaşılan depolama alanındaki diğer dosyalara erişmek için düzeyi 29'u içeriyorsa) kapsamlı olarak kapsam dışında kalmayı depolama alanını requestLegacyExternalStorage Uygulamanızın manifest dosyasında true adresine. Medya dosyalarına erişmek için yerel dosya yöntemlerinden yararlanmak için READ_EXTERNAL_STORAGE izni gerekir.

Medya içeriğine erişirken dikkat edilmesi gereken noktalar

Medya içeriklerine erişirken, şurada açıklanan hususlara dikkat edin: bu bölümde bulabilirsiniz.

Önbelleğe alınan veriler

Uygulamanız medya mağazasındaki URI'leri veya verileri önbelleğe alıyorsa düzenli olarak güncelleme hakkında daha fazla bilgi edinin. Bu denetim, uygulama tarafında önbelleğe alınan veriler, sistem tarafı sağlayıcı verileriyle senkronize durumda kalır.

Performans

Doğrudan dosya yollarını kullanarak medya dosyalarını sıralı bir şekilde okumanız halinde ABD'deki yaklaşık MediaStore API.

Doğrudan dosya yollarını kullanarak medya dosyalarında rastgele okuma ve yazma işlemleri gerçekleştirdiğinizde Ancak süreç iki kata kadar uzayabilir. Böyle durumlarda bunun yerine MediaStore API'yi kullanmanızı öneririz.

DATA sütunu

Mevcut bir medya dosyasına eriştiğinizde DATA sütunu düşünmelisiniz. Bunun nedeni, bu değerin geçerli bir dosya yoluna sahip olmasıdır. Ancak dosyanın her zaman kullanılabilir olduğunu varsayın. Dosya tabanlı ve alakalı tüm dosyalarınızı oluşan G/Ç hataları.

Öte yandan, bir medya dosyası oluşturmak veya güncellemek için DATA sütunu. Bunun yerine, DISPLAY_NAME ve RELATIVE_PATH sütunlarının değerlerini kullanın.

Depolama birimleri

Android 10 veya sonraki sürümleri hedefleyen uygulamalar benzersiz ada erişebilir atayan her harici depolama birimine atanır. Bu adlandırma sistemi içeriği verimli şekilde düzenlemenize ve dizine eklemenize yardımcı olur, aynı zamanda üzerinde yeni medya dosyalarının depolandığı yerdir.

Özellikle aşağıdaki hacimleri göz önünde bulundurmak önemlidir:

  • VOLUME_EXTERNAL hacmi, cihazdaki tüm paylaşılan depolama birimlerinin görünümünü sağlar. Okuyabilirsiniz içeriği değiştirir, ancak içeriği değiştiremezsiniz.
  • İlgili içeriği oluşturmak için kullanılan VOLUME_EXTERNAL_PRIMARY cihazdaki birincil paylaşılan depolama hacmini temsil eder. Şunları yapabilirsiniz: okuma ve değiştirme işlemlerini yapabilir.

MediaStore.getExternalVolumeNames() düğmesini kullanarak diğer ses seviyelerini keşfedebilirsiniz:

Kotlin

val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context)
val firstVolumeName = volumeNames.iterator().next()

Java

Set<String> volumeNames = MediaStore.getExternalVolumeNames(context);
String firstVolumeName = volumeNames.iterator().next();

Medyanın yakalandığı konum

Bazı fotoğraf ve videoların meta verilerinde konum bilgisi bulunur. veya bir videonun çekildiği yeri gösteren kaydedilir.

Uygulamanızda bu konum bilgilerine nasıl erişeceğiniz, fotoğraf veya video için konum bilgilerine erişmesi gerekiyor.

Fotoğraflar

Uygulamanız kapsamlı depolama alanı kullanıyorsa sistem varsayılan olarak konum bilgilerini gizler. Bu bilgilere erişmek için aşağıdaki adımları uygulayın:

  1. Şu isteği gönder: ACCESS_MEDIA_LOCATION izin vermiş olursunuz.
  2. MediaStore nesnenizden fotoğrafın tam baytlarını şu şekilde elde edin: arama setRequireOriginal() ve aşağıdaki kod snippet'inde gösterildiği gibi fotoğrafın URI'sını iletmelidir:

    Kotlin

    val photoUri: Uri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex)
    )
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri)
    contentResolver.openInputStream(photoUri)?.use { stream ->
        ExifInterface(stream).run {
            // If lat/long is null, fall back to the coordinates (0, 0).
            val latLong = latLong ?: doubleArrayOf(0.0, 0.0)
        }
    }
    

    Java

    Uri photoUri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex));
    
    final double[] latLong;
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri);
    InputStream stream = getContentResolver().openInputStream(photoUri);
    if (stream != null) {
        ExifInterface exifInterface = new ExifInterface(stream);
        double[] returnedLatLong = exifInterface.getLatLong();
    
        // If lat/long is null, fall back to the coordinates (0, 0).
        latLong = returnedLatLong != null ? returnedLatLong : new double[2];
    
        // Don't reuse the stream associated with
        // the instance of "ExifInterface".
        stream.close();
    } else {
        // Failed to load the stream, so return the coordinates (0, 0).
        latLong = new double[2];
    }
    

Videolar

Bir videonun meta verilerindeki konum bilgilerine erişmek için şunu kullanın: MediaMetadataRetriever sınıfını kullanır. Uygulamanızın istekte bulunmasına gerek yoktur bu sınıfı kullanmak için ek izinler var.

Kotlin

val retriever = MediaMetadataRetriever()
val context = applicationContext

// Find the videos that are stored on a device by querying the video collection.
val query = ContentResolver.query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query?.use { cursor ->
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
    while (cursor.moveToNext()) {
        val id = cursor.getLong(idColumn)
        val videoUri: Uri = ContentUris.withAppendedId(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            id
        )
        extractVideoLocationInfo(videoUri)
    }
}

private fun extractVideoLocationInfo(videoUri: Uri) {
    try {
        retriever.setDataSource(context, videoUri)
    } catch (e: RuntimeException) {
        Log.e(APP_TAG, "Cannot retrieve video file", e)
    }
    // Metadata uses a standardized format.
    val locationMetadata: String? =
            retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
}

Java

MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Context context = getApplicationContext();

// Find the videos that are stored on a device by querying the video collection.
try (Cursor cursor = context.getContentResolver().query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)) {
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
    while (cursor.moveToNext()) {
        long id = cursor.getLong(idColumn);
        Uri videoUri = ContentUris.withAppendedId(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
        extractVideoLocationInfo(videoUri);
    }
}

private void extractVideoLocationInfo(Uri videoUri) {
    try {
        retriever.setDataSource(context, videoUri);
    } catch (RuntimeException e) {
        Log.e(APP_TAG, "Cannot retrieve video file", e);
    }
    // Metadata uses a standardized format.
    String locationMetadata = retriever.extractMetadata(
            MediaMetadataRetriever.METADATA_KEY_LOCATION);
}

Paylaşılıyor

Bazı uygulamalar, kullanıcıların birbirleriyle medya dosyaları paylaşmasına izin verir. Örneğin, sosyal medya medya uygulamaları, kullanıcıların arkadaşlarıyla fotoğraf ve video paylaşmasına olanak tanır.

Medya dosyalarını paylaşmak için içerik sağlayıcı oluşturma kılavuzunda önerilen content:// URI'sini kullanın.

Medya dosyaları için uygulama ilişkilendirmesi

Bir Android 10 veya sonraki bir sürümü hedefleyen uygulamalar için sistem özellikleri ve uygulamanızı her medya dosyasına ekler. Bu dosya, uygulamanızın hiçbir depolama izni istememiştir. Her dosya yalnızca bir uygulama. Bu nedenle, uygulamanız fotoğraf, video veya ses dosyası medya koleksiyonunu içeriyorsa uygulamanızın dosyası olarak kaydedebilirsiniz.

Ancak kullanıcı, uygulamanızı kaldırıp yeniden yüklerse READ_EXTERNAL_STORAGE uygulamanızın orijinal olarak oluşturduğu dosyalara erişmek için. Bu izin isteği olması gerekir çünkü sistem dosyanın ilişkili uygulamanın yeni yüklenen sürümü yerine önceden yüklenmiş sürümü.

Öğe ekleyin

Mevcut bir koleksiyona medya öğesi eklemek için şuna benzer bir kod kullanın: takip ediliyor. Bu kod snippet'i VOLUME_EXTERNAL_PRIMARY bölümüne erişir Android 10 veya sonraki sürümleri çalıştıran cihazlarda. Çünkü bu cihazlarda bir birimin içeriğini değiştirebilirsiniz. Bunun için Depolama birimleri bölümünde açıklanmıştır.

Kotlin

// Add a specific media item.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
val audioCollection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Audio.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY
        )
    } else {
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    }

// Publish a new song.
val newSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3")
}

// Keep a handle to the new song's URI in case you need to modify it
// later.
val myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails)

Java

// Add a specific media item.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
Uri audioCollection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    audioCollection = MediaStore.Audio.Media
            .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
    audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}

// Publish a new song.
ContentValues newSongDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Song.mp3");

// Keep a handle to the new song's URI in case you need to modify it
// later.
Uri myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails);

Medya dosyaları için bekleme durumunu aç/kapat

Uygulamanızda zaman alan işlemler (ör. Google Cloud) yapılıyorsa medya dosyalarını açmak için dosyaya özel bir işlendi. Android 10 veya sonraki sürümleri çalıştıran cihazlarda uygulamanız, IS_PENDING işaretinin değerini 1 olarak ayarlayarak bu özel erişimi elde edebilir. Uygulamanız IS_PENDING değeri 0 değerine döndürülüyor.

Aşağıdaki kod snippet'i, önceki kod snippet'inin üzerine kurulmuştur. Bu snippet'inde uzun bir şarkıyı depolarken IS_PENDING işaretinin nasıl kullanılacağını MediaStore.Audio koleksiyonuna karşılık gelen dizin:

Kotlin

// Add a media item that other apps don't see until the item is
// fully written to the media store.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
val audioCollection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Audio.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY
        )
    } else {
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    }

val songDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3")
    put(MediaStore.Audio.Media.IS_PENDING, 1)
}

val songContentUri = resolver.insert(audioCollection, songDetails)

// "w" for write.
resolver.openFileDescriptor(songContentUri, "w", null).use { pfd ->
    // Write data into the pending audio file.
}

// Now that you're finished, release the "pending" status and let other apps
// play the audio track.
songDetails.clear()
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0)
resolver.update(songContentUri, songDetails, null, null)

Java

// Add a media item that other apps don't see until the item is
// fully written to the media store.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
Uri audioCollection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    audioCollection = MediaStore.Audio.Media
            .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
    audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}

ContentValues songDetails = new ContentValues();
songDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Workout Playlist.mp3");
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 1);

Uri songContentUri = resolver
        .insert(audioCollection, songDetails);

// "w" for write.
try (ParcelFileDescriptor pfd =
        resolver.openFileDescriptor(songContentUri, "w", null)) {
    // Write data into the pending audio file.
}

// Now that you're finished, release the "pending" status and let other apps
// play the audio track.
songDetails.clear();
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0);
resolver.update(songContentUri, songDetails, null, null);

Dosya konumu için ipucu verme

Uygulamanız Android 10 çalıştıran bir cihazda medya depoladığında: medya, türüne göre düzenlenir. Örneğin, varsayılan olarak, resim dosyaları Environment.DIRECTORY_PICTURES etiketidir; MediaStore.Images koleksiyonu.

Uygulamanız, dosyaların depolanabileceği belirli bir konumu (ör. Pictures/MyVacationPictures adlı bir fotoğraf albümü) biliyorsa sisteme yeni yazılan dosyaların nereye depolanacağıyla ilgili bir ipucu vermek için MediaColumns.RELATIVE_PATH parametresini ayarlayabilirsiniz.

Öğe güncelleme

Uygulamanızın sahibi olduğu bir medya dosyasını güncellemek için aşağıdakine benzer bir kod kullanın:

Kotlin

// Updates an existing media item.
val mediaId = // MediaStore.Audio.Media._ID of item to update.
val resolver = applicationContext.contentResolver

// When performing a single item update, prefer using the ID.
val selection = "${MediaStore.Audio.Media._ID} = ?"

// By using selection + args you protect against improper escaping of // values.
val selectionArgs = arrayOf(mediaId.toString())

// Update an existing song.
val updatedSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3")
}

// Use the individual song's URI to represent the collection that's
// updated.
val numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs)

Java

// Updates an existing media item.
long mediaId = // MediaStore.Audio.Media._ID of item to update.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// When performing a single item update, prefer using the ID.
String selection = MediaStore.Audio.Media._ID + " = ?";

// By using selection + args you protect against improper escaping of
// values. Here, "song" is an in-memory object that caches the song's
// information.
String[] selectionArgs = new String[] { getId().toString() };

// Update an existing song.
ContentValues updatedSongDetails = new ContentValues();
updatedSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Favorite Song.mp3");

// Use the individual song's URI to represent the collection that's
// updated.
int numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs);

Kapsamlı depolama kullanılamıyorsa veya etkinleştirilmediyse önceki kod snippet'i, uygulamanızın sahip olmadığı dosyalarda da çalışır.

Yerel kodda güncelle

Medya dosyalarını yerel kitaplıkları kullanarak yazmanız gerekiyorsa dosyanın ilişkili dosya tanımlayıcıyı JavaScript veya Kotlin tabanlı kodunuzdan kopyalayıp yerel koda karşılık gelir.

Aşağıdaki kod snippet'inde, bir medya nesnesinin dosya tanımlayıcısının uygulamanızın yerel koduna nasıl iletileceği gösterilmektedir:

Kotlin

val contentUri: Uri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(BaseColumns._ID))
val fileOpenMode = "r"
val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode)
val fd = parcelFd?.detachFd()
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.

Java

Uri contentUri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(Integer.parseInt(BaseColumns._ID)));
String fileOpenMode = "r";
ParcelFileDescriptor parcelFd =
        resolver.openFileDescriptor(contentUri, fileOpenMode);
if (parcelFd != null) {
    int fd = parcelFd.detachFd();
    // Pass the integer value "fd" into your native code. Remember to call
    // close(2) on the file descriptor when you're done using it.
}

Diğer uygulamaları güncelle medya dosyaları

Uygulamanız kapsamlı depolama alanı kullanıyorsa farklı bir uygulamanın katkıda bulunduğu medya dosyasının güncellenmesi medya mağazasına dokunun.

Ancak, kullanıcı rızasını almak için RecoverableSecurityException tek bir fonksiyona dönüştü. Ardından, kullanıcıdan uygulamanıza erişim izni vermesini isteyebilirsiniz aşağıdaki kod snippet'inde gösterildiği gibi, söz konusu öğeye yazma erişimi verme:

Kotlin

// Apply a grayscale filter to the image at the given content URI.
try {
    // "w" for write.
    contentResolver.openFileDescriptor(image-content-uri, "w")?.use {
        setGrayscaleFilter(it)
    }
} catch (securityException: SecurityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val recoverableSecurityException = securityException as?
            RecoverableSecurityException ?:
            throw RuntimeException(securityException.message, securityException)

        val intentSender =
            recoverableSecurityException.userAction.actionIntent.intentSender
        intentSender?.let {
            startIntentSenderForResult(intentSender, image-request-code,
                    null, 0, 0, 0, null)
        }
    } else {
        throw RuntimeException(securityException.message, securityException)
    }
}

Java

try {
    // "w" for write.
    ParcelFileDescriptor imageFd = getContentResolver()
            .openFileDescriptor(image-content-uri, "w");
    setGrayscaleFilter(imageFd);
} catch (SecurityException securityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        RecoverableSecurityException recoverableSecurityException;
        if (securityException instanceof RecoverableSecurityException) {
            recoverableSecurityException =
                    (RecoverableSecurityException)securityException;
        } else {
            throw new RuntimeException(
                    securityException.getMessage(), securityException);
        }
        IntentSender intentSender =recoverableSecurityException.getUserAction()
                .getActionIntent().getIntentSender();
        startIntentSenderForResult(intentSender, image-request-code,
                null, 0, 0, 0, null);
    } else {
        throw new RuntimeException(
                securityException.getMessage(), securityException);
    }
}

Uygulamanızın, oluşturmadığı bir medya dosyasını değiştirmesi gerektiğinde her seferinde bu işlemi tamamlayın.

Alternatif olarak, uygulamanız Android 11 veya sonraki bir sürümde çalışıyorsa şunu da yapabilirsiniz: Kullanıcıların, uygulamanıza bir medya dosyası grubuna yazma erişimi vermesine izin verin. Şunu kullanın: createWriteRequest() yöntemi kullanıldığında , medya gruplarının yönetimiyle ilgili dosyalarını kullanın.

Uygulamanızın, kapsamlı depolama alanı kapsamında olmayan başka bir kullanım alanı varsa bir özellik isteği ve kapsam dışında kalmayı geçici olarak devre dışı bırak depolama.

Öğe kaldırma

Uygulamanızın artık medya mağazasında ihtiyaç duymadığı bir öğeyi kaldırmak için mantığı kullanın aşağıdaki kod snippet'inde gösterilene benzer:

Kotlin

// Remove a specific media item.
val resolver = applicationContext.contentResolver

// URI of the image to remove.
val imageUri = "..."

// WHERE clause.
val selection = "..."
val selectionArgs = "..."

// Perform the actual removal.
val numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs)

Java

// Remove a specific media item.
ContentResolver resolver = getApplicationContext()
        getContentResolver();

// URI of the image to remove.
Uri imageUri = "...";

// WHERE clause.
String selection = "...";
String[] selectionArgs = "...";

// Perform the actual removal.
int numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs);

Kapsamlı depolama alanı kullanılamıyorsa veya etkinleştirilmemişse önceki kod snippet'ini kullanabilirsiniz. Kapsamlı depolama etkinleştirilirse ancak şunu yapan her dosya için bir RecoverableSecurityException yakalamanız gerekir: medyaları güncelleme ile ilgili bölümde açıklandığı gibi, uygulamanızı kaldırmak istiyorsa öğeleri kontrol edin.

Uygulamanız Android 11 veya sonraki bir sürümde çalışıyorsa kullanıcıların kaldırılacak medya dosyaları grubunu seçin. Medya dosyası gruplarını yönetme ile ilgili bölümde açıklandığı şekilde createTrashRequest() yöntemini veya createDeleteRequest() yöntemini kullanın.

Uygulamanızın, kapsamlı depolama alanı kapsamında olmayan başka bir kullanım alanı varsa bir özellik isteği ve kapsam dışında kalmayı geçici olarak devre dışı bırak depolama.

Medya dosyalarındaki güncellemeleri algılama

Uygulamanızın, kullandığı medya dosyalarını içeren depolama birimlerini tanımlaması gerekebilir. eklenen veya değiştirilen Bu değişiklikleri tespit etmek için en güvenilir şekilde, ilgilenen depolama hacmini getGeneration(). Medya mağazası sürümü değişmediği sürece, bu zamanla monoton bir şekilde artar.

Özellikle getGeneration(), medya sütunlarındaki tarihlere göre daha güvenilirdir. örneğin: DATE_ADDED ve DATE_MODIFIED. Bunun nedeni, bir uygulama setLastModified() veya şu zaman: Kullanıcı sistem saatini değiştirir.

Medya dosyası gruplarını yönetme

Android 11 ve sonraki sürümlerde kullanıcıdan grup seçmesini isteyebilirsiniz. güncelleyip bu medya dosyalarını tek bir işlemle güncelleyebilirsiniz. Bu yöntemler, cihazlar arasında daha iyi tutarlılık sağlar ve yöntemler, Böylece kullanıcılar medya koleksiyonlarını yönetebilir.

Bu "toplu güncellemeyi" sağlayan yöntemler işlevleri arasında takip etmek için:

createWriteRequest()
Kullanıcının uygulamanıza belirtilen grup için yazma erişimi vermesini isteyin medya dosyaları olabilir.
createFavoriteRequest()
Kullanıcıdan belirtilen medya dosyalarını "favori" . Bu dosyaya okuma erişimi olan tüm uygulamalar kullanıcının dosyayı "favori" olarak işaretlediğini görebilirsiniz.
createTrashRequest()

Kullanıcıdan, belirtilen medya dosyalarını cihazın çöp kutusuna yerleştirmesini isteyin. Çöp kutusundaki öğeler sistem tarafından tanımlanan bir sürenin sonunda kalıcı olarak silinir geçerlidir.

createDeleteRequest()

Kullanıcıdan belirtilen medya dosyalarını kalıcı olarak silmesini isteyin göndermeden hemen önce çalışmanızı sağlar.

Bu yöntemlerden herhangi biri çağrıldıktan sonra, sistem bir PendingIntent nesnesini tanımlayın. Uygulamanız bu intent'i çağırdıktan sonra kullanıcılar, uygulamanızın belirtilen medya dosyalarını güncellemesi veya silme işlemi için izinlerini isteyen bir iletişim kutusu görür.

Örneğin, bir createWriteRequest() çağrısının nasıl yapılandırılacağı aşağıda açıklanmıştır:

Kotlin

val urisToModify = /* A collection of content URIs to modify. */
val editPendingIntent = MediaStore.createWriteRequest(contentResolver,
        urisToModify)

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,
    null, 0, 0, 0)

Java

List<Uri> urisToModify = /* A collection of content URIs to modify. */
PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver,
                  urisToModify);

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.getIntentSender(),
    EDIT_REQUEST_CODE, null, 0, 0, 0);

Kullanıcının yanıtını değerlendirin. Kullanıcı izin verdiyse medya işlemine devam edin. Aksi halde, kullanıcıya uygulamanızın neden izin:

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int,
                 data: Intent?) {
    ...
    when (requestCode) {
        EDIT_REQUEST_CODE ->
            if (resultCode == Activity.RESULT_OK) {
                /* Edit request granted; proceed. */
            } else {
                /* Edit request not granted; explain to the user. */
            }
    }
}

Java

@Override
protected void onActivityResult(int requestCode, int resultCode,
                   @Nullable Intent data) {
    ...
    if (requestCode == EDIT_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            /* Edit request granted; proceed. */
        } else {
            /* Edit request not granted; explain to the user. */
        }
    }
}

Aynı genel kalıbı createFavoriteRequest() createTrashRequest(), ve createDeleteRequest().

Medya yönetimi izni

Kullanıcılar, medya yönetimi gerçekleştirmek için belirli bir uygulamaya güvenebilir. Örneğin, medya dosyalarında sık sık düzenleme yapmak. Uygulamanız bunu hedefliyorsa Android 11 veya sonraki bir sürüm yüklüyse ve cihazın varsayılan galeri uygulaması değilse uygulamanız her denediğinde kullanıcıya bir onay iletişim kutusu göstermelisiniz. bir dosyayı değiştirebilir veya silebilirsiniz.

Uygulamanız Android 12 (API düzeyi 31) veya sonraki bir sürümü hedefliyorsa şunları isteyebilirsiniz: Kullanıcılar, uygulamanıza medya yönetimi özel izni için erişim izni verirler. Bu izni, uygulamanızın herhangi bir istemle karşılaşmadan aşağıdakilerden her birini yapmasına olanak tanır her dosya işlemi için kullanıcı:

Bunun için aşağıdaki adımları uygulayın:

  1. Tanımlayın MANAGE_MEDIA izni ve READ_EXTERNAL_STORAGE izni bulunmalıdır.

    Onay göstermeden createWriteRequest() numaralı telefonu aramak için beyan etmek istiyorsanız ACCESS_MEDIA_LOCATION izni de verebilirsiniz.

  2. Uygulamanızda, kullanıcıya neden izin vermek isteyebileceğini açıklayan bir kullanıcı arayüzü gösterin uygulamanıza medya yönetimi erişimi.

  3. ACTION_REQUEST_MANAGE_MEDIA intent işlemidir. Bu işlem, kullanıcıları sistem ayarlarındaki Medya yönetimi uygulamaları ekranına yönlendirir. Kullanıcılar buradan özel uygulama erişimi verebilir.

Medya mağazasına alternatif gerektiren kullanım alanları

Uygulamanız temel olarak aşağıdaki rollerden birini gerçekleştiriyorsa şunları göz önünde bulundurun: MediaStore API'lerine alternatiftir.

Diğer dosya türleriyle çalışma

Uygulamanız yalnızca medya içermeyen dokümanlarla ve dosyalarla çalışıyorsa kullanan dosyalar ve diğer içerikler için Depolama rehberinde açıklandığı gibi, ACTION_OPEN_DOCUMENT intent işlemi ve diğer dokümanlara ve diğer dokümanlara dosyalarına ekleyin.

Tamamlayıcı uygulamalarda dosya paylaşımı

Mesajlaşma uygulaması ve profil uygulaması, dosya paylaşımını ayarlama (content:// URI kullanılıyor.) Bu iş akışını güvenlik en iyi uygulaması olarak da öneririz.

Ek kaynaklar

Medyaların nasıl depolanacağı ve bunlara nasıl erişileceği hakkında daha fazla bilgi için aşağıdaki kaynaklara göz atın.

Örnekler

Videolar