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, kullanıcıların bu medya dosyalarını daha kolay almasına ve güncellemesine olanak tanıyan medya mağazası adlı, medya koleksiyonlarında optimize edilmiş bir dizin sağlar. Eşit Uygulamanız kaldırıldıktan sonra bu dosyalar kullanıcının cihazında kalır.
Fotoğraf seçici
Android fotoğraf seçici aracı, medya mağazasını kullanmaya alternatif olarak kullanıcıların uygulamanıza medya kitaplıklarının tamamına erişim izni vermelerine gerek kalmadan medya dosyaları seçmeleri için güvenli ve yerleşik bir yöntem sunar. Bu yalnızca kullanılabilir desteklenen cihazlarda kullanabilirsiniz. Daha fazla bilgi için fotoğraf seçici kılavuzuna bakın.
Medya mağazası
Medya mağazası soyutlamayla etkileşimde bulunmak için uygulamanızın bağlamından aldığınız bir ContentResolver
nesnesi kullanın:
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. } }
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, harici depolama birimini otomatik olarak tarar ve medya dosyalarını aşağıdaki iyi tanımlanmış koleksiyonlara ekler:
- Görseller (fotoğraflar ve ekran görüntüleri dahil)
DCIM/
vePictures/
dizinleri. Sistem bu dosyalarıMediaStore.Images
tablosuna ekler. DCIM/
,Movies/
vePictures/
dizinlerinde depolanan videolar. Sistem bu dosyalarıMediaStore.Video
tablosuna ekler.Alarms/
,Audiobooks/
,Music/
,Notifications/
,Podcasts/
veRingtones/
dizinlerinde depolanan ses dosyaları. Ayrıca, sistemMusic/
veyaMovies/
içinde olan şarkı listelerini tanırRecordings/
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. Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran cihazlarda bu dosyalarMediaStore.Downloads
tablosunda depolanır. Bu tablo, Android 9 (API düzeyi 28) ve önceki sürümlerde kullanılamaz.
Medya mağazasında MediaStore.Files
adlı bir koleksiyon da bulunur. İçeriği
uygulamanızın kapsamlı
depolama alanını hedefleyen uygulamalarda kullanılabilir.
Android 10 veya sonraki sürümler.
- Kapsamlı depolama alanı etkinse koleksiyonda yalnızca uygulamanızın oluşturduğu fotoğraflar, videolar ve ses dosyaları gösterilir. Çoğu geliştiricinin
Diğer uygulamalardaki medya dosyalarını görüntülemek için
MediaStore.Files
(ancak uygulamanız varsa) özel bir şart varsaREAD_EXTERNAL_STORAGE
izni gerekir. Bununla birlikte, uygulamanızın oluşturmadığı dosyaları açmak içinMediaStore
API'lerini kullanmanızı öneririz. - 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 uygulamanızın ihtiyaç duymadığı veya kullanmadığı izinleri belirtmemeye dikkat edin.
Depolama alanı 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ümlerin yüklü olduğu cihazlarda, MediaStore.Downloads
koleksiyonundaki dosyalar da dahil olmak üzere uygulamanızın sahibi olduğu medya dosyalarına erişmek ve bu dosyaları değiştirmek için depolamayla ilgili izinlere ihtiyacınız yoktur. Örneğin bir kamera uygulaması geliştiriyorsanız
çünkü
uygulama, medya mağazasına yazdığınız resimlerin sahibi olur.
Diğer uygulamaların medya dosyalarına erişme
Diğer uygulamaların oluşturduğu medya dosyalarına erişmek için ilgili izinlere sahip olmalı ve dosyalar şu medya koleksiyonları:
Bir dosya MediaStore.Images
üzerinden görüntülenebildiği sürece,
MediaStore.Video
veya MediaStore.Audio
sorgularında,
MediaStore.Files
sorgu.
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 gerekli
Uygulamanız Android 9 veya daha eski bir sürümün yüklü olduğu bir cihazda kullanılıyorsa ya da uygulamanız geçici olarak kapsamlı depolama özelliğini devre dışı bıraktıysa herhangi bir medya dosyasına erişmek için READ_EXTERNAL_STORAGE
iznini istemeniz gerekir. Medya dosyalarını değiştirmek istiyorsanız
şu istekte bulun:
WRITE_EXTERNAL_STORAGE
kabul etmenizi de sağlar.
Diğer uygulamaların indirmelerine erişmek için Depolama Erişimi Çerçevesi gerekli
Uygulamanız, MediaStore.Downloads
koleksiyonundaki bir dosyaya erişmek istiyorsa
Depolama Erişim Çerçevesi'ni kullanmanız gerekir. Öğrenmek için
daha fazla bilgi için bkz.
paylaşılan depolama alanı.
Medya konumu izni
Uygulamanız Android 10 (API düzeyi 29) veya sonraki sürümleri hedefliyorsa ve fotoğraflardan çıkartılmamış EXIF meta verilerini alması gerekiyorsa ACCESS_MEDIA_LOCATION
iznini uygulamanızın manifest dosyasında beyan etmeniz ve ardından bu izni çalışma zamanında istemeniz gerekir.
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. Bu kontrolü yapmak için
güncellemeler, çağrı
getVersion()
.
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
sürümünü indirin, uygulamanızın medya önbelleğini yeniden tarayın ve yeniden senkronize edin.
Bu kontrolü uygulama işleminin başlatıldığı sırada 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:
// 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) } }
// 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ş akışında
query()
yöntemini çağırın. - Sorgu sonucundan bir satır işlediğinizde her seferinde
getColumnIndexOrThrow()
çağırmanız gerekmemesi için sütun dizinlerini önbelleğe alın. - Bu örnekte gösterildiği gibi kimliği 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, API'de tanımlanmamış bir sütun adı (ör."MimeType"
) bekliyorsa sütun adını uygulamanızın işleminde dinamik olarak çevirmek içinCursorWrapper
kullanın.
Dosya küçük resimlerini yükleme
Uygulamanız birden fazla medya dosyası gösteriyorsa ve kullanıcıdan bu dosyalardan birini seçmesini istiyorsa dosyaların önizleme sürümlerini veya küçük resimlerini yüklemek daha verimlidir.
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:
// Load thumbnail of a specific media item. val thumbnail: Bitmap = applicationContext.contentResolver.loadThumbnail(content-uri , Size(640, 480), null)
// 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 tanımlayıcı
Dosya tanımlayıcısı kullanarak bir medya dosyasını açmak için aşağıdaki kod snippet'inde gösterilene benzer bir mantık kullanın:
// 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". }
// 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 aktarımı
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:
// Open a specific media item using InputStream. val resolver = applicationContext.contentResolver resolver.openInputStream(content-uri ).use { stream -> // Perform operations on "stream". }
// 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- Yerel kitaplıklar (ör.
fopen()
)
Depolama alanıyla ilgili izniniz yoksa
uygulamaya özel dizin ve medya
uygulamanızla ilişkilendirilen dosyalar File
API kullanılarak oluşturulur.
Uygulamanız File
API'sini kullanarak bir dosyaya erişmeye çalışırsa ve gerekli izinlere sahip değilse FileNotFoundException
hatası oluşur.
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. Android 10'da yerel dosya yöntemlerini kullanarak medya dosyalarına erişmek için READ_EXTERNAL_STORAGE
iznine de ihtiyacınız vardır.
Medya içeriğine erişirken dikkat edilmesi gereken noktalar
Medya içeriğine erişirken aşağıdaki bölümlerde açıklanan hususları göz önünde bulundurun.
Önbelleğe alınan veriler
Uygulamanız medya mağazasındaki URI'leri veya verileri önbelleğe alıyorsa düzenli olarak medya mağazasındaki güncellemeleri kontrol edin. Bu kontrol, uygulama tarafındaki önbelleğe alınmış verilerinizin sistem tarafındaki sağlayıcı verileriyle senkronize kalmasını sağlar.
Performans
Doğrudan dosya yollarını kullanarak medya dosyalarını sıralı bir şekilde okumanız halinde
ABD'deki yaklaşık
MediaStore
API.
Ancak doğrudan dosya yollarını kullanarak medya dosyalarında rastgele okuma ve yazma işlemleri gerçekleştirdiğinizde süreç iki kat daha yavaş olabilir. 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ılabileceğini varsaymayın. Dosya tabanlı ve alakalı tüm dosyalarınızı
oluşan G/Ç hataları.
Öte yandan, medya dosyası oluşturmak veya güncellemek için DATA
sütununun değerini kullanmayın. 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, sistemin her harici depolama birimine atadığı benzersiz ada erişebilir. 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:
- İlgili içeriği oluşturmak için kullanılan
VOLUME_EXTERNAL
birimin görünümü, cihazdaki tüm paylaşılan depolama birimlerini gösterir. Bu sentetik birimin içeriğini okuyabilirsiniz ancak içeriği değiştiremezsiniz. VOLUME_EXTERNAL_PRIMARY
hacmi, cihazdaki birincil paylaşılan depolama alanını temsil eder. Şunları yapabilirsiniz: okuma ve değiştirme işlemlerini yapabilir.
MediaStore.getExternalVolumeNames()
düğmesini kullanarak diğer ciltleri keşfedebilirsiniz:
val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context) val firstVolumeName = volumeNames.iterator().next()
Set<String> volumeNames = MediaStore.getExternalVolumeNames(context); String firstVolumeName = volumeNames.iterator().next();
Medyanın çekildiği konum
Bazı fotoğraf ve videoların meta verilerinde, fotoğrafın çekildiği veya videonun kaydedildiği yeri gösteren konum bilgileri bulunur.
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:
- Uygulamanızın manifest dosyasında
ACCESS_MEDIA_LOCATION
iznini isteyin. MediaStore
nesnenizdesetRequireOriginal()
işlevini çağırarak ve fotoğrafın URI'sini ileterek fotoğrafın tam baytlarını alın. Aşağıdaki kod snippet'inde gösterildiği gibi: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) } }
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 aşağıdaki kod snippet'inde gösterildiği gibi MediaMetadataRetriever
sınıfını kullanın. Uygulamanızın bu sınıfı kullanmak için ek izin istemesi gerekmez.
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) }
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 medya dosyalarını birbirleriyle paylaşmasına olanak tanır. Ö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 şunlara yönelik rehberde açıklandığı gibi bir content://
URI kullanın:
içerik sağlayıcı oluşturma hakkında daha fazla bilgi edinin.
Medya dosyalarının 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 uygulamanızın başlangıçta oluşturduğu dosyalara erişmek için READ_EXTERNAL_STORAGE
izin isteğinde bulunmanız gerekir. Sistem, dosyayı yeni yüklenmiş sürüm yerine daha önce yüklenmiş sürümle ilişkilendirdiği için bu izin isteği gereklidir.
Öğ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.
// 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)
// 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ının bekleme durumunu değiştirme
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. IS_PENDING
değerini 0 olarak değiştirene kadar dosyayı yalnızca uygulamanız görüntüleyebilir.
Aşağıdaki kod snippet'i, önceki kod snippet'ini temel alır. Bu
snippet'inde uzun bir şarkıyı saklarken IS_PENDING
işaretinin nasıl kullanılacağını gösterilmektedir
MediaStore.Audio
koleksiyonuna karşılık gelen dizin:
// 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)
// 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ığı zaman medya varsayılan olarak 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 konumun farkındaysa (ör.
Pictures/MyVacationPictures
adlı bir fotoğraf albümü olarak şunları ayarlayabilirsiniz:
MediaColumns.RELATIVE_PATH
yeni yazılan dosyaların nerede saklanacağıyla ilgili olarak sisteme ipucu sağlar.
Öğ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:
// 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)
// 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üncelleme
Medya dosyalarını yerel kitaplıkları kullanarak yazmanız gerekiyorsa dosyanın ilişkili dosya tanımlayıcısını JavaScript veya Kotlin tabanlı kodunuzdan yerel koda karşılık gelir.
Aşağıdaki kod snippet'i, bir medya nesnesinin dosya tanımlayıcısının nasıl iletileceğini gösterir uygulamanızın yerel koduna ekleyin:
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.
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 kullanıyorsa genellikle medya mağazasına farklı bir uygulamanın katkıda bulunduğu bir medya dosyasını güncelleyemez.
Ancak platformun gönderdiği RecoverableSecurityException
değerini yakalayarak dosyayı değiştirmek için kullanıcı izni alabilirsiniz. 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:
// 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) } }
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, içerdiği bir medya dosyasını her değiştirmesi gerektiğinde bu işlemi tamamlayın oluşturulmadı.
Alternatif olarak, uygulamanız Android 11 veya sonraki bir sürümde çalışıyorsa kullanıcıların uygulamanıza bir medya dosyası grubuna yazma erişimi vermesine izin verebilirsiniz. Medya dosyası gruplarını yönetme ile ilgili bölümde açıklanan createWriteRequest()
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.
Öğ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:
// 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)
// 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 sürümleri çalıştırıyorsa kullanıcıların, kaldırılacak bir medya dosyası grubu seçmesine izin verebilirsiniz. Medya dosyası gruplarını yönetme ile ilgili bölümde açıklandığı şekilde createTrashRequest()
yöntemini veya createDeleteRequest()
yöntemini kullanın.
Uygulamanızda, kapsamlı depolama alanı kapsamında olmayan başka bir kullanım alanı varsa özellik isteği gönderin ve kapsamlı depolama alanını geçici olarak devre dışı bırakın.
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 yöntemin döndürdüğü değer zaman içinde monoton bir şekilde artar.
Özellikle getGeneration()
, DATE_ADDED
ve DATE_MODIFIED
gibi medya sütunlarındaki tarihlerden daha sağlamdır.
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üncelleme" işlevini sağlayan yöntemler şunlardır:
createWriteRequest()
- Kullanıcının uygulamanıza belirtilen grup için yazma erişimi vermesini isteyin medya dosyaları olabilir.
createFavoriteRequest()
- Kullanıcının, belirtilen medya dosyalarını cihazdaki "favori" medyalar arasından işaretlemesini isteyin. 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:
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)
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 takdirde, uygulamanızın neden bu izne ihtiyacı olduğunu kullanıcıya açıklayın:
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. */ } } }
@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 sürümleri hedefliyorsa kullanıcılardan uygulamanıza medya yönetimi özel iznini vermesini isteyebilirsiniz. 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ı:
- Dosyaları değiştirmek için
createWriteRequest()
. createTrashRequest()
simgesini kullanarak dosyaları çöp kutusuna taşıyın veya çöp kutusundan çıkarın.createDeleteRequest()
simgesini kullanarak dosyaları silin.
Bunun için aşağıdaki adımları uygulayın:
Uygulamanızın manifest dosyasında
MANAGE_MEDIA
veREAD_EXTERNAL_STORAGE
izinlerini beyan edin.Onay iletişim kutusu göstermeden
createWriteRequest()
'ü aramak içinACCESS_MEDIA_LOCATION
iznini de belirtin.Uygulamanızda, kullanıcıya uygulamanıza medya yönetimi erişimi vermesinin nedenlerini açıklayan bir kullanıcı arayüzü gösterin.
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 uygulamaya erişim izni verebilir.
Medya mağazasına alternatif bir yöntem 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çeriği içermeyen dokümanlar ve dosyalarla (ör. EPUB veya PDF dosya uzantısını kullanan dosyalar) çalışıyorsa dokümanları ve diğer dosyaları depolama ve bunlara erişme kılavuzunda açıklandığı gibi ACTION_OPEN_DOCUMENT
intent işlemini kullanın.
Tamamlayıcı uygulamalarda dosya paylaşımı
Mesajlaşma uygulaması ve
profil uygulaması, dosya paylaşımını ayarlama
(content://
URI kullanılıyor.) Ayrıca, bu iş akışını güvenlikle ilgili en iyi
alıştırma da yapın.
Ek kaynaklar
Medyaları depolama ve medyalara erişme hakkında daha fazla bilgi için aşağıdakilere bakın kaynaklar.
Örnekler
- GitHub'da bulunan MediaStore