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

Daha zengin bir kullanıcı deneyimi sağlamak için birçok uygulama, kullanıcıların harici depolama birimindeki medyaya katkıda bulunmasına ve medyaya erişmesine izin verir. Çerçeve, medya koleksiyonlarına medya mağazası adı verilen optimize edilmiş bir dizin sağlar. Bu sayede, kullanıcılar söz konusu medya dosyalarını daha kolay alıp güncelleyebilir. Bu dosyalar, uygulamanızın yüklemesi kaldırıldıktan sonra bile kullanıcının cihazında kalır.

Fotoğraf seçici

Medya mağazasını kullanmaya alternatif olarak Android fotoğraf seçici aracı, kullanıcıların uygulamanızın tüm medya kitaplıklarına erişmesine izin vermek zorunda kalmadan medya dosyalarını seçmeleri için güvenli ve yerleşik bir yol sağlar. Bu özellik yalnızca desteklenen cihazlarda kullanılabilir. Daha fazla bilgi için fotoğraf seçici kılavuzuna bakın.

Medya mağazası

Medya mağazası soyutlamasıyla etkileşim kurmak için uygulamanızın bağlamından aldığınız bir ContentResolver nesnesini kullanı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ını aşağıdaki iyi tanımlanmış koleksiyonlara ekler:

  • Fotoğraflar ve ekran görüntüleri de dahil olmak üzere DCIM/ ve Pictures/ dizinlerinde depolanan resimler. Sistem bu dosyaları MediaStore.Images tablosuna ekler.
  • DCIM/, Movies/ ve Pictures/ dizinlerinde depolanan videolar. Sistem bu dosyaları MediaStore.Video tablosuna ekler.
  • Alarms/, Audiobooks/, Music/, Notifications/, Podcasts/ ve Ringtones/ dizinlerinde depolanan ses dosyaları. Ayrıca sistem, Music/ veya Movies/ dizinlerindeki sesli şarkı listelerinin yanı sıra Recordings/ dizinindeki ses kayıtlarını da tanır. Sistem bu dosyaları MediaStore.Audio tablosuna ekler. Recordings/ dizini, Android 11 (API düzeyi 30) ve önceki sürümlerde kullanılamaz.
  • Download/ dizininde depolanan indirilen dosyalar. Bu dosyalar, Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran cihazlarda MediaStore.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 Android 10 veya sonraki sürümleri hedefleyen uygulamalarda kullanılabilen kapsamlı depolama alanını kullanıp kullanmadığına bağlıdır.

  • Kapsamlı depolama etkinleştirildiyse koleksiyon yalnızca uygulamanızın oluşturduğu fotoğrafları, videoları ve ses dosyalarını gösterir. Çoğu geliştiricinin diğer uygulamalardaki medya dosyalarını görüntülemek için MediaStore.Files kullanmasına gerek yoktur ancak böyle bir şartınız varsa READ_EXTERNAL_STORAGE iznini beyan edebilirsiniz. Bununla birlikte, uygulamanızın oluşturmadığı dosyaları açmak için MediaStore API'lerini kullanmanızı öneririz.
  • Kapsamlı depolama kullanılamıyorsa veya kullanılmıyorsa koleksiyon her tür medya dosyasını gösterir.

Gerekli izinleri isteyin

Medya dosyaları üzerinde işlem gerçekleştirmeden önce, uygulamanızın bu dosyalara erişmek için ihtiyaç duyduğu izinleri beyan ettiğinden emin olun. 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 izne ihtiyaç duyması, yalnızca kendi medya dosyalarına mı yoksa başka 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, MediaStore.Downloads koleksiyonundaki dosyalar da dahil olmak üzere uygulamanızın sahip olduğu medya dosyalarına erişmek ve bunları değiştirmek için depolama alanıyla ilgili izinlere ihtiyacınız yoktur. Örneğin, bir kamera uygulaması geliştiriyorsanız çekilen fotoğraflara erişmek için depolamayla ilgili izinler istemenize gerek yoktur. Çünkü medya mağazasında yazdığınız resimler uygulamanız uygulamaya aittir.

Diğer uygulamaların medya dosyalarına erişme

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

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

Aşağıdaki kod snippet'inde, uygun depolama izinlerinin nasıl tanımlanacağı gösterilmektedir:

<!-- 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 kapsamlı depolama alanını devre dışı bıraktıysa tüm medya dosyalarına erişebilmek için READ_EXTERNAL_STORAGE izni istemeniz gerekir. Medya dosyalarını değiştirmek istiyorsanız WRITE_EXTERNAL_STORAGE iznini de almanız gerekir.

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

Uygulamanız, uygulamanızın oluşturmadığı MediaStore.Downloads koleksiyonundaki bir dosyaya erişmek istiyorsa Depolama Erişim Çerçevesi'ni kullanmanız gerekir. Bu çerçevenin nasıl kullanılacağı hakkında daha fazla bilgi için Paylaşılan depolama alanından dokümanlara ve diğer dosyalara erişme bölümüne bakın.

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 alması gerekiyorsa uygulamanızın manifest dosyasında ACCESS_MEDIA_LOCATION iznini beyan etmeniz ve ardından çalışma zamanında bu izni istemeniz gerekir.

Medya mağazasındaki güncellemeleri kontrol edin

Medya dosyalarına daha güvenilir bir şekilde erişmek için, özellikle uygulamanız URI'ları veya medya deposundaki verileri önbelleğe alıyorsa, medya mağazası sürümünün, medya verilerinizi en son senkronize ettiğiniz zamana kıyasla değişip değişmediğini kontrol edin. Güncelleme olup olmadığını kontrol etmek için getVersion() numaralı telefonu arayın. Döndürülen sürüm, medya mağazası önemli ölçüde değiştiğinde değişen benzersiz bir dizedir. Döndürülen sürüm, son senkronize edilmiş sürümden farklıysa uygulamanızın medya önbelleğini yeniden tarayın ve yeniden senkronize edin.

Bu kontrolü, uygulama işlemi başlatılırken tamamlayın. Medya mağazasını her sorguladığınızda sürümü kontrol etmenize gerek yoktur.

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

Medya koleksiyonunu sorgulama

5 dakika veya daha uzun bir süre gibi belirli bir koşul grubunu karşılayan medyaları bulmak için aşağıdaki kod snippet'inde gösterilene benzer SQL benzeri bir seçim ifadesi kullanın:

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 böyle bir sorgu gerçekleştirirken aşağıdakileri aklınızda bulundurun:

  • Çalışan ileti dizisinde query() yöntemini çağırın.
  • Sorgu sonucundaki bir satırı her işlediğinizde getColumnIndexOrThrow() yöntemini çağırmanız gerekmemesi için sütun dizinlerini önbelleğe alın.
  • Kimliği, bu örnekte gösterildiği gibi içerik URI'sine ekleyin.
  • Android 10 ve sonraki sürümleri çalıştıran cihazlar MediaStore API'de tanımlanmış sütun adları gerektirir. Uygulamanızdaki bağımlı bir kitaplık, API'de tanımlanmamış bir sütun adı (ör. "MimeType") bekliyorsa uygulamanızın işlemindeki sütun adını dinamik olarak çevirmek için CursorWrapper işlevini kullanın.

Dosya küçük resimlerini yükle

Uygulamanız birden çok medya dosyası gösteriyorsa ve kullanıcıdan bu dosyalardan birini seçmesini istiyorsa dosyaların kendisi yerine önizleme sürümlerini (veya küçük resimleri) yüklemek daha verimli olur.

Belirli bir medya dosyasının küçük resmini yüklemek için loadThumbnail() kullanın ve yüklemek istediğiniz küçük resmin boyutunu aşağıdaki kod snippet'inde gösterildiği gibi iletin:

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, medya içeriğinin en iyi şekilde bir dosya tanımlayıcısı, dosya akışı veya doğrudan dosya yolu olarak temsil edilmesine bağlıdır.

Dosya açıklayıcı

Bir medya dosyasını dosya tanımlayıcı 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 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ı

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

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

Depolama alanıyla ilgili izniniz yoksa File API'yi kullanarak uygulamaya özgü dizininizdeki dosyalara ve uygulamanızla ilişkilendirilen medya dosyalarına erişebilirsiniz.

Uygulamanız File API'yi kullanarak bir dosyaya erişmeye çalışıyorsa ve gerekli izinlere sahip değilse FileNotFoundException ortaya çıkar.

Android 10 (API düzeyi 29) çalıştıran bir cihazda, paylaşılan depolama alanındaki diğer dosyalara erişmek için uygulamanızın manifest dosyasında requestLegacyExternalStorage değerini true olarak ayarlayarak kapsamlı depolama alanını geçici olarak devre dışı bırakmanızı öneririz. Android 10'da medya dosyalarına yerel dosya yöntemlerini kullanarak erişmek için READ_EXTERNAL_STORAGE iznini de istemeniz gerekir.

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

Medya içeriğine erişirken, aşağıdaki bölümlerde ele alınan noktaları aklınızda bulundurun.

Önbelleğe alınan veriler

Uygulamanız URI'ları veya medya deposundaki verileri önbelleğe alıyorsa, düzenli olarak medya deposundaki güncellemeleri kontrol edin. Bu kontrol, uygulama tarafında önbelleğe alınan verilerinizin sistem tarafındaki sağlayıcı verileriyle senkronize olmasını sağlar.

Performans

Doğrudan dosya yollarını kullanarak medya dosyalarını sıralı şekilde okuduğunuzda, performans MediaStore API'ninkiyle karşılaştırılabilir olur.

Ancak doğrudan dosya yollarını kullanarak medya dosyalarında rastgele okuma ve yazma işlemleri gerçekleştirdiğinizde, işlem iki kata kadar yavaş olabilir. Bu tür durumlarda bunun yerine MediaStore API'yi kullanmanızı öneririz.

DATA sütunu

Mevcut bir medya dosyasına eriştiğinizde mantığınızdaki DATA sütununun değerini kullanabilirsiniz. Bunun nedeni, bu değerin geçerli bir dosya yoluna sahip olmasıdır. Bununla birlikte, dosyanın her zaman kullanılabilir olduğunu varsaymayın. Ortaya çıkabilecek dosya tabanlı G/Ç hatalarına hazırlıklı olun.

Diğer yandan, bir 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çerikleri verimli bir şekilde düzenleyip dizine eklemenize yardımcı olur ve yeni medya dosyalarının nerede saklanacağı konusunda kontrol sahibi olmanızı sağlar.

Özellikle aşağıdaki ciltler göz önünde bulundurulmalıdır:

  • VOLUME_EXTERNAL birimi, cihazdaki tüm paylaşılan depolama alanı hacimlerini gösterir. Bu sentetik cildin içeriğini okuyabilir ancak değiştiremezsiniz.
  • VOLUME_EXTERNAL_PRIMARY hacmi, cihazdaki birincil paylaşılan depolama alanı hacmini temsil eder. Bu cildin içeriğini okuyabilir ve değiştirebilirsiniz.

MediaStore.getExternalVolumeNames() numarasını arayarak diğer hacimleri 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 çekildiği konum

Bazı fotoğraf ve videoların meta verileri, fotoğrafın çekildiği yeri veya videonun kaydedildiği yeri gösteren konum bilgilerini içerir.

Uygulamanızda bu konum bilgilerine erişme şekliniz, bir fotoğraf için mi yoksa video için mi konum bilgilerine erişmeniz gerektiğine bağlıdır.

Fotoğraflar

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

  1. Uygulamanızın manifest dosyasında ACCESS_MEDIA_LOCATION iznini isteyin.
  2. Aşağıdaki kod snippet'inde gösterildiği gibi, MediaStore nesnenizden setRequireOriginal() çağırıp fotoğrafın URI'sını geçirerek fotoğrafın tam baytlarını alın:

    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 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 istemesine gerek yoktur.

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 olanak tanır. Örneğin, sosyal 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 önerildiği gibi bir content:// URI'si kullanın.

Medya dosyalarının uygulamayla ilişkilendirilmesi

Android 10 veya sonraki sürümleri hedefleyen bir uygulama için kapsamlı depolama etkinleştirildiğinde sistem, bir uygulamayı her medya dosyasıyla ilişkilendirir. Böylece, uygulamanız herhangi bir depolama izni istemediğinde uygulamanızın erişebileceği dosyaları belirler. Her dosya yalnızca bir uygulamayla ilişkilendirilebilir. Bu nedenle, uygulamanız fotoğraflar, videolar veya ses dosyaları medya koleksiyonunda depolanan bir medya dosyası oluşturursa uygulamanız söz konusu dosyaya erişebilir.

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 isteğinde bulunmanız gerekir. Sistem, dosyanın yeni yüklenen sürüm yerine uygulamanın daha önce yüklenmiş sürümüyle ilişkilendirildiğini kabul ettiği için bu izin isteği gereklidir.

Öğe ekleyin

Mevcut bir koleksiyona medya öğesi eklemek için aşağıdakine benzer bir kod kullanın. Bu kod snippet'i, Android 10 veya sonraki sürümleri çalıştıran cihazlarda VOLUME_EXTERNAL_PRIMARY hacmine erişir. Bunun nedeni, Depolama birimleri bölümünde açıklandığı gibi, bu cihazlarda yalnızca birincil birim olması durumunda birimin içeriğini değiştirebilmenizdir.

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ının beklemede durumunu aç/kapat

Uygulamanız, medya dosyalarına yazma gibi zaman alıcı işlemler gerçekleştiriyorsa, işlenmekte olan dosyaya özel erişime sahip olmak faydalı olur. Uygulamanız, Android 10 veya sonraki sürümleri çalıştıran cihazlarda IS_PENDING işaretinin değerini 1 olarak ayarlayarak bu özel erişime sahip olabilir. Uygulamanız IS_PENDING değerini tekrar 0'a 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, MediaStore.Audio koleksiyonuna karşılık gelen dizinde uzun bir şarkı depolarken IS_PENDING işaretinin nasıl kullanılacağını gösterir:

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

Dosyanın konumu için ipucu verin

Uygulamanız Android 10 çalıştıran bir cihazda medya depoladığında, medya varsayılan olarak türüne göre düzenlenir. Örneğin, yeni görüntü dosyaları varsayılan olarak MediaStore.Images koleksiyonuna karşılık gelen Environment.DIRECTORY_PICTURES dizinine yerleştirilir.

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 nerede depolanacağı konusunda ipucu sağlamak için MediaColumns.RELATIVE_PATH ayarlayabilirsiniz.

Öğe güncelleme

Uygulamanıza ait 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ştirilmemişse önceki kod snippet'inde gösterilen işlem, uygulamanızın sahibi olmadığı dosyalar için de çalışır.

Yerel kodda güncelleme

Medya dosyalarını yerel kitaplıkları kullanarak yazmanız gerekiyorsa dosyayla ilişkili dosya tanımlayıcısını Java tabanlı veya Kotlin tabanlı kodunuzdan yerel kodunuza iletin.

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

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ın medya dosyalarını güncelleme

Uygulamanız kapsamlı depolama alanını kullanıyorsa normalde, farklı bir uygulamanın medya mağazasına katkıda bulunduğu medya dosyalarını güncelleyemez.

Bununla birlikte, platformun gönderdiği RecoverableSecurityException öğesini yakalayarak kullanıcıdan dosyayı değiştirmek için izin alabilirsiniz. Daha sonra, aşağıdaki kod snippet'inde gösterildiği gibi kullanıcıdan bu belirli öğe için uygulamanıza yazma erişimi vermesini isteyebilirsiniz:

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 bu işlemi tamamlayın.

Alternatif olarak, uygulamanız Android 11 veya sonraki bir sürümde çalışıyorsa kullanıcıların bir grup medya dosyasına, uygulamanıza yazma erişimi vermesine izin verebilirsiniz. Medya dosyası gruplarını yönetme ile ilgili bölümde açıklandığı gibi createWriteRequest() yöntemini kullanın.

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

Öğe kaldırma

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

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 kullanılamıyorsa veya etkinleştirilmemişse diğer uygulamaların sahip olduğu dosyaları kaldırmak için önceki kod snippet'ini kullanabilirsiniz. Ancak kapsamlı depolama alanı etkinse medya öğelerini güncelleme hakkındaki bölümde açıklandığı gibi, uygulamanızın kaldırmak istediği her dosya için bir RecoverableSecurityException yakalamanız gerekir.

Uygulamanız Android 11 veya sonraki bir sürümde çalışıyorsa kullanıcıların kaldırılacak medya dosyası grubunu seçmelerine izin verebilirsiniz. Medya dosyası gruplarını yönetme bölümünde açıklandığı gibi createTrashRequest() veya createDeleteRequest() yöntemini kullanın.

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

Medya dosyalarında yapılan güncellemeleri algılama

Uygulamanızın, önceki bir zamana kıyasla, uygulamaların eklediği veya değiştirdiği medya dosyalarını içeren depolama hacimlerini tanımlaması gerekebilir. Bu değişiklikleri en güvenilir şekilde tespit etmek için ilgili depolama alanı hacmini getGeneration()'e iletin. Medya mağazası sürümü değişmediği sürece, bu yöntemin döndürülen değeri zamanla tek tip olarak artar.

Özellikle getGeneration(), DATE_ADDED ve DATE_MODIFIED gibi medya sütunlarındaki tarihlerden daha sağlamdır. Bunun nedeni, bir uygulama setLastModified() aracını çağırdığında veya kullanıcı sistem saatini değiştirdiğinde bu medya sütunu değerlerinin değişebilmesidir.

Medya dosyası gruplarını yönetin

Android 11 ve sonraki sürümlerde kullanıcıdan bir grup medya dosyası seçmesini, ardından bu medya dosyalarını tek bir işlemle güncellemesini isteyebilirsiniz. Bu yöntemler cihazlar arasında daha iyi tutarlılık sağlar ve kullanıcıların medya koleksiyonlarını yönetmesini kolaylaştırır.

Bu "toplu güncelleme" işlevini sağlayan yöntemler aşağıdakileri içerir:

createWriteRequest()
Kullanıcıdan, uygulamanız için belirtilen medya dosyası grubuna yazma erişimi vermesini isteyin.
createFavoriteRequest()
Kullanıcıdan belirtilen medya dosyalarını cihazda "favori" medya dosyaları olarak işaretlemesini isteyin. Bu dosyaya okuma erişimi olan tüm uygulamalar, kullanıcının dosyayı "favori" olarak işaretlediğini görebilir.
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üre sonunda kalıcı olarak silinir.

createDeleteRequest()

Kullanıcıdan belirtilen medya dosyalarını önceden çöp kutusuna taşımadan, kalıcı olarak hemen silmesini isteyin.

Bu yöntemlerden herhangi birini çağırdıktan sonra sistem bir PendingIntent nesnesi oluşturur. Uygulamanız bu amacı çağırdıktan sonra kullanıcılar, uygulamanızın belirtilen medya dosyalarını güncellemesi veya silmesi için rızalarını isteyen bir iletişim kutusu görür.

Örneğin, 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 takdirde, kullanıcıya uygulamanızın neden bu izne ihtiyacı olduğunu açıklayın:

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() için de kullanabilirsiniz.

Medya yönetimi izni

Kullanıcılar, medya dosyalarını sık sık düzenleme gibi medya yönetimini gerçekleştirmek için belirli bir uygulamaya güvenebilirler. Uygulamanız Android 11 veya sonraki bir sürümü hedefliyorsa ve cihazın varsayılan galeri uygulaması değilse uygulamanız bir dosyayı her değiştirmeye veya silmeye çalıştığında kullanıcıya onay iletişim kutusu göstermelisiniz.

Uygulamanız Android 12 (API düzeyi 31) veya sonraki sürümleri hedefliyorsa kullanıcılardan uygulamanıza medya yönetimi özel izni için erişim izni vermesini isteyebilirsiniz. Bu izin, uygulamanızın her dosya işlemi için kullanıcıyı sormasına gerek kalmadan aşağıdakilerin her birini yapmasına olanak tanır:

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

  1. Uygulamanızın manifest dosyasında MANAGE_MEDIA iznini ve READ_EXTERNAL_STORAGE iznini beyan edin.

    Onay iletişim kutusu göstermeden createWriteRequest() çağırmak için ACCESS_MEDIA_LOCATION iznini de beyan edin.

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

  3. ACTION_REQUEST_MANAGE_MEDIA amaç işlemini çağırın. 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 verebilirler.

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

Uygulamanız öncelikli olarak aşağıdaki rollerden birini gerçekleştiriyorsa MediaStore API'lerine bir alternatifi kullanmayı düşünün.

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ı gibi bir tamamlayıcı uygulama paketi sağladığınız durumlarda, content:// URI'larını kullanarak dosya paylaşımını ayarlayın. Ayrıca bu iş akışını bir güvenlik en iyi uygulaması olarak da öneririz.

Ek kaynaklar

Medya depolama ve medyaya erişme hakkında daha fazla bilgi için aşağıdaki kaynaklara başvurun.

Sana Özel

Videolar