Bulut medya sağlayıcı, Android fotoğraf seçiciye ek bulut medya içeriği sağlar. Bir uygulama, kullanıcıdan medya dosyası istemek için ACTION_PICK_IMAGES veya ACTION_GET_CONTENT kullandığında kullanıcılar, bulut medya sağlayıcısı tarafından sunulan fotoğraf veya videoları seçebilir. Bulut medya sağlayıcıları, Android fotoğraf seçicide göz atılabilecek albümler hakkında da bilgi verebilir.
Başlamadan önce
Bulut medya sağlayıcınızı oluşturmaya başlamadan önce aşağıdaki öğeleri göz önünde bulundurun.
Uygunluk
Android, OEM tarafından belirlenen uygulamaların bulut medya sağlayıcısı olmasına olanak tanıyan bir pilot program yürütmektedir. Şu anda Android için bulut medya sağlayıcısı olmak üzere bu programa yalnızca OEM'ler tarafından aday gösterilen uygulamalar katılabilir. Her OEM, en fazla 3 uygulama aday gösterebilir. Onaylandıktan sonra bu uygulamalara, yüklendikleri tüm GMS Android destekli cihazlarda bulut medya sağlayıcıları olarak erişilebilir.
Android, uygun tüm bulut sağlayıcıların sunucu tarafı listesini tutar. Her OEM, yapılandırılabilir bir yer paylaşımı kullanarak varsayılan bir bulut sağlayıcı seçebilir. Aday gösterilen uygulamalar tüm teknik koşulları karşılamalı ve tüm kalite testlerini geçmelidir. OEM bulut medya sağlayıcı pilot programının süreci ve şartları hakkında daha fazla bilgi edinmek için sorgu formunu doldurun.
Bulut medya sağlayıcı oluşturmanız gerekip gerekmediğine karar verme
Bulut medya sağlayıcılar, kullanıcıların fotoğraflarını ve videolarını buluta yedekleme ve buluttan alma konusunda birincil kaynak olarak kullandığı uygulamalar veya hizmetler olarak tasarlanmıştır. Uygulamanızda yararlı içeriklerin bulunduğu bir kitaplık varsa ancak genellikle fotoğraf depolama çözümü olarak kullanılmıyorsa bunun yerine doküman sağlayıcı oluşturmayı düşünebilirsiniz.
Profil başına bir etkin bulut sağlayıcı
Her Android profili için aynı anda en fazla bir etkin bulut medya sağlayıcısı olabilir. Kullanıcılar, seçtikleri bulut medya sağlayıcı uygulamasını fotoğraf seçici ayarlarından istedikleri zaman kaldırabilir veya değiştirebilir.
Android fotoğraf seçici, varsayılan olarak otomatik bir bulut sağlayıcı seçmeye çalışır.
- Cihazda yalnızca bir uygun bulut sağlayıcı varsa bu uygulama otomatik olarak mevcut sağlayıcı olarak seçilir.
Cihazda birden fazla uygun bulut sağlayıcı varsa ve bunlardan biri OEM tarafından seçilen varsayılanla eşleşiyorsa OEM tarafından seçilen uygulama belirlenir.
Cihazda birden fazla uygun bulut sağlayıcı varsa ve bunlardan hiçbiri OEM'in seçtiği varsayılanla eşleşmiyorsa uygulama seçilmez.
Bulut medya sağlayıcınızı oluşturma
Aşağıdaki diyagramda, Android uygulaması, Android fotoğraf seçici, yerel cihazın MediaProvider ve CloudMediaProvider arasındaki fotoğraf seçme oturumundan önce ve oturum sırasında gerçekleşen etkinliklerin sırası gösterilmektedir.
- Sistem, kullanıcının tercih ettiği bulut sağlayıcıyı başlatır ve medya meta verilerini Android fotoğraf seçici arka ucuna düzenli olarak senkronize eder.
- Bir Android uygulaması fotoğraf seçiciyi başlattığında, birleştirilmiş yerel veya bulut öğe ızgarasını kullanıcıya göstermeden önce, sonuçların mümkün olduğunca güncel olmasını sağlamak için bulut sağlayıcıyla gecikmeye duyarlı artımlı bir senkronizasyon gerçekleştirir. Yanıt alındıktan sonra veya son tarihe ulaşıldığında, fotoğraf seçici ızgarasında artık erişilebilen tüm fotoğraflar gösteriliyor. Bu fotoğraflar, cihazınızda yerel olarak depolanan fotoğraflarla buluttan senkronize edilen fotoğrafların birleştirilmesiyle oluşturuluyor.
- Kullanıcı kaydırma yaparken fotoğraf seçici, kullanıcı arayüzünde göstermek için bulut medya sağlayıcısından medya küçük resimlerini getirir.
- Kullanıcı oturumu tamamladığında ve sonuçlar bir bulut medya öğesi içerdiğinde fotoğraf seçici, içerik için dosya tanımlayıcıları ister, URI oluşturur ve çağıran uygulamaya dosyaya erişim izni verir.
- Uygulama artık URI'yi açabilir ve medya içeriklerine salt okuma erişimi vardır. Hassas meta veriler varsayılan olarak çıkartılır. Fotoğraf seçici, Android uygulaması ile bulut medya sağlayıcısı arasındaki veri alışverişini koordine etmek için FUSE dosya sisteminden yararlanır.
Sık Karşılaşılan Sorunlar
Uygulamanızı değerlendirirken dikkate almanız gereken bazı önemli noktalar şunlardır:
Yinelenen dosyalardan kaçının
Android fotoğraf seçici, bulut medya durumunu inceleyemediğinden CloudMediaProvider, hem bulutta hem de yerel cihazda bulunan tüm dosyaların imleç satırında MEDIA_STORE_URI sağlamalıdır. Aksi takdirde kullanıcı, fotoğraf seçicide yinelenen dosyalar görür.
Önizleme ekranı için resim boyutlarını optimize etme
onOpenPreview tarafından döndürülen dosyanın tam çözünürlüklü bir resim olmaması ve istenen Size'ye uyması çok önemlidir. Çok büyük bir resim, kullanıcı arayüzünde yükleme sürelerine neden olur. Çok küçük bir resim ise cihazın ekran boyutuna bağlı olarak pikselli veya bulanık olabilir.
Doğru yönü işleme
onOpenPreview içinde döndürülen küçük resimler EXIF verilerini içermiyorsa önizleme ızgarasında küçük resimlerin yanlış döndürülmesini önlemek için doğru yönde döndürülmelidir.
Yetkisiz erişimi önleyin
Verileri ContentProvider'dan arayana döndürmeden önce MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION olup olmadığını kontrol edin. Bu işlem, yetkisiz uygulamaların bulut verilerine erişmesini engeller.
CloudMediaProvider sınıfı
android.content.ContentProvider sınıfından türetilen CloudMediaProvider
sınıfı, aşağıdaki örnekte gösterilenler gibi yöntemler içerir:
Kotlin
abstract class CloudMediaProvider : ContentProvider() {
@NonNull
abstract override fun onGetMediaCollectionInfo(@NonNull bundle: Bundle): Bundle
@NonNull
override fun onQueryAlbums(@NonNull bundle: Bundle): Cursor = TODO("Implement onQueryAlbums")
@NonNull
abstract override fun onQueryDeletedMedia(@NonNull bundle: Bundle): Cursor
@NonNull
abstract override fun onQueryMedia(@NonNull bundle: Bundle): Cursor
@NonNull
abstract override fun onOpenMedia(
@NonNull string: String,
@Nullable bundle: Bundle?,
@Nullable cancellationSignal: CancellationSignal?
): ParcelFileDescriptor
@NonNull
abstract override fun onOpenPreview(
@NonNull string: String,
@NonNull point: Point,
@Nullable bundle: Bundle?,
@Nullable cancellationSignal: CancellationSignal?
): AssetFileDescriptor
@Nullable
override fun onCreateCloudMediaSurfaceController(
@NonNull bundle: Bundle,
@NonNull callback: CloudMediaSurfaceStateChangedCallback
): CloudMediaSurfaceController? = null
}
Java
public abstract class CloudMediaProvider extends android.content.ContentProvider {
@NonNull
public abstract android.os.Bundle onGetMediaCollectionInfo(@NonNull android.os.Bundle);
@NonNull
public android.database.Cursor onQueryAlbums(@NonNull android.os.Bundle);
@NonNull
public abstract android.database.Cursor onQueryDeletedMedia(@NonNull android.os.Bundle);
@NonNull
public abstract android.database.Cursor onQueryMedia(@NonNull android.os.Bundle);
@NonNull
public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@NonNull
public abstract android.content.res.AssetFileDescriptor onOpenPreview(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@Nullable
public android.provider.CloudMediaProvider.CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull android.os.Bundle, @NonNull android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback);
}
CloudMediaProviderContract sınıfı
Android fotoğraf seçici, birincil CloudMediaProvider uygulama sınıfına ek olarak CloudMediaProviderContract sınıfını da içerir.
Bu sınıf, fotoğraf seçici ile bulut medya sağlayıcı arasındaki birlikte çalışabilirliği özetler. Bu kapsamda, senkronizasyon işlemleri için MediaCollectionInfo, beklenen Cursor sütunları ve Bundle ekstraları gibi konular ele alınır.
Kotlin
object CloudMediaProviderContract {
const val EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID"
const val EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED"
const val EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID"
const val EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE"
const val EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN"
const val EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL"
const val EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED"
const val EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION"
const val MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
const val PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER"
object MediaColumns {
const val DATE_TAKEN_MILLIS = "date_taken_millis"
const val DURATION_MILLIS = "duration_millis"
const val HEIGHT = "height"
const val ID = "id"
const val IS_FAVORITE = "is_favorite"
const val MEDIA_STORE_URI = "media_store_uri"
const val MIME_TYPE = "mime_type"
const val ORIENTATION = "orientation"
const val SIZE_BYTES = "size_bytes"
const val STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension"
const val STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3 // 0x3
const val STANDARD_MIME_TYPE_EXTENSION_GIF = 1 // 0x1
const val STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2 // 0x2
const val STANDARD_MIME_TYPE_EXTENSION_NONE = 0 // 0x0
const val SYNC_GENERATION = "sync_generation"
const val WIDTH = "width"
}
object AlbumColumns {
const val DATE_TAKEN_MILLIS = "date_taken_millis"
const val DISPLAY_NAME = "display_name"
const val ID = "id"
const val MEDIA_COUNT = "album_media_count"
const val MEDIA_COVER_ID = "album_media_cover_id"
}
object MediaCollectionInfo {
const val ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent"
const val ACCOUNT_NAME = "account_name"
const val LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation"
const val MEDIA_COLLECTION_ID = "media_collection_id"
}
}
Java
public final class CloudMediaProviderContract {
public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
public static final String EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
public static final String EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID";
public static final String EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE";
public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
public static final String EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL";
public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
}
// Columns available for every media item
public static final class CloudMediaProviderContract.MediaColumns {
public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
public static final String DURATION_MILLIS = "duration_millis";
public static final String HEIGHT = "height";
public static final String ID = "id";
public static final String IS_FAVORITE = "is_favorite";
public static final String MEDIA_STORE_URI = "media_store_uri";
public static final String MIME_TYPE = "mime_type";
public static final String ORIENTATION = "orientation";
public static final String SIZE_BYTES = "size_bytes";
public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3; // 0x3
public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1; // 0x1
public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2; // 0x2
public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0; // 0x0
public static final String SYNC_GENERATION = "sync_generation";
public static final String WIDTH = "width";
}
// Columns available for every album item
public static final class CloudMediaProviderContract.AlbumColumns {
public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
public static final String DISPLAY_NAME = "display_name";
public static final String ID = "id";
public static final String MEDIA_COUNT = "album_media_count";
public static final String MEDIA_COVER_ID = "album_media_cover_id";
}
// Media Collection metadata that is cached by the OS to compare sync states.
public static final class CloudMediaProviderContract.MediaCollectionInfo {
public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
public static final String ACCOUNT_NAME = "account_name";
public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
public static final String MEDIA_COLLECTION_ID = "media_collection_id";
}
onGetMediaCollectionInfo
onGetMediaCollectionInfo() yöntemi, işletim sistemi tarafından önbelleğe alınmış bulut medya öğelerinin geçerliliğini değerlendirmek ve bulut medya sağlayıcıyla gerekli senkronizasyonu belirlemek için kullanılır. İşletim sistemi tarafından sık sık çağrılma olasılığı nedeniyle onGetMediaCollectionInfo(), performansı etkileyen kritik bir bileşen olarak kabul edilir. Uzun süren işlemlerden veya performansı olumsuz etkileyebilecek yan etkilerden kaçınmak çok önemlidir. İşletim sistemi, bu yöntemden gelen önceki yanıtları önbelleğe alır ve uygun işlemleri belirlemek için bunları sonraki yanıtlarla karşılaştırır.
Kotlin
abstract fun onGetMediaCollectionInfo(extras: Bundle): Bundle
Java
@NonNull
public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);
Döndürülen MediaCollectionInfo paketi aşağıdaki sabitleri içerir:
onQueryMedia
onQueryMedia() yöntemi, fotoğraf seçicideki ana fotoğraf ızgarasını çeşitli görünümlerde doldurmak için kullanılır. Bu çağrılar gecikmeye duyarlı olabilir ve arka planda proaktif senkronizasyonun bir parçası olarak veya tam ya da artımlı senkronizasyon durumu gerektiğinde fotoğraf seçici oturumları sırasında çağrılabilir. Fotoğraf seçici kullanıcı arayüzü, sonuçları göstermek için yanıtı süresiz olarak beklemez ve kullanıcı arayüzü amacıyla bu isteklerin süresini uzatabilir. Döndürülen imleç, gelecekteki oturumlar için fotoğraf seçicinin veritabanında işlenmeye çalışmaya devam eder.
Bu yöntem, medya koleksiyonundaki tüm medya öğelerini temsil eden bir Cursor döndürür. Bu öğeler, isteğe bağlı olarak sağlanan ekstralara göre filtrelenir ve MediaColumns#DATE_TAKEN_MILLIS'nin ters kronolojik sırasına göre sıralanır (en yeni öğeler önce gelir).
Döndürülen CloudMediaProviderContract paketi aşağıdaki sabitleri içerir:
EXTRA_ALBUM_IDEXTRA_LOOPING_PLAYBACK_ENABLEDEXTRA_MEDIA_COLLECTION_IDEXTRA_PAGE_SIZEEXTRA_PAGE_TOKENEXTRA_PREVIEW_THUMBNAILEXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLEDEXTRA_SYNC_GENERATIONMANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSIONPROVIDER_INTERFACE
Bulut medya sağlayıcı, döndürülen Bundle öğesinin bir parçası olarak CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID değerini ayarlamalıdır. Bunun ayarlanmaması bir hatadır ve döndürülen Cursor öğesini geçersiz kılar. Bulut medya sağlayıcı, sağlanan ekstralardaki filtreleri işlediyse döndürülen ContentResolver#EXTRA_HONORED_ARGS'nın bir parçası olarak Cursor#setExtras'ya anahtarı eklemelidir.
onQueryDeletedMedia
onQueryDeletedMedia() yöntemi, bulut hesabında silinen öğelerin fotoğraf seçici kullanıcı arayüzünden doğru şekilde kaldırılmasını sağlamak için kullanılır. Bu aramalar, gecikmeye karşı hassas olabilecekleri için aşağıdakilerin bir parçası olarak başlatılabilir:
- Arka planda proaktif senkronizasyon
- Fotoğraf seçici oturumları (tam veya artımlı senkronizasyon durumu gerektiğinde)
Fotoğraf seçicinin kullanıcı arayüzünde duyarlı bir kullanıcı deneyimine öncelik verilir ve yanıt için süresiz olarak beklenmez. Sorunsuz etkileşimler için zaman aşımları yaşanabilir. Döndürülen tüm Cursor, gelecekteki oturumlar için fotoğraf seçicinin veritabanında işlenmeye çalışmaya devam eder.
Bu yöntem, onGetMediaCollectionInfo() tarafından döndürülen mevcut sağlayıcı sürümündeki tüm medya koleksiyonunda silinen tüm medya öğelerini temsil eden bir Cursor döndürür. Bu öğeler, ekstralara göre isteğe bağlı olarak filtrelenebilir.
Bulut medya sağlayıcı, döndürülen Cursor#setExtras öğesinin bir parçası olarak CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID öğesini ayarlamalıdır. Bu öğenin ayarlanmaması bir hatadır ve Cursor öğesini geçersiz kılar. Sağlayıcı, sağlanan ekstralarda herhangi bir filtreyi işlediyse anahtarı ContentResolver#EXTRA_HONORED_ARGS öğesine eklemelidir.
onQueryAlbums
onQueryAlbums() yöntemi, bulut sağlayıcıda bulunan Cloud albümlerinin listesini ve ilişkili meta verilerini getirmek için kullanılır. Daha fazla bilgi için CloudMediaProviderContract.AlbumColumns sayfasına bakın.
Bu yöntem, medya koleksiyonundaki tüm albüm öğelerini temsil eden bir Cursor döndürür. Bu öğeler, sağlanan ekstralarla isteğe bağlı olarak filtrelenir ve AlbumColumns#DATE_TAKEN_MILLIS öğesinin ters kronolojik sırasına göre sıralanır. En yeni öğeler önce gösterilir. Bulut medya sağlayıcı, döndürülen Cursor'nin bir parçası olarak CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID'yı ayarlamalıdır. Bunun ayarlanmaması bir hatadır ve döndürülen Cursor öğesini geçersiz kılar. Sağlayıcı, sağlanan ekstralardaki filtreleri işlediyse döndürülen Cursor'nin bir parçası olarak anahtarı ContentResolver#EXTRA_HONORED_ARGS'ya eklemelidir.
onOpenMedia
onOpenMedia() yöntemi, sağlanan mediaId ile tanımlanan tam boyutlu medyayı döndürmelidir. Bu yöntem, cihaza içerik indirilirken engelliyorsa terk edilmiş istekleri iptal etmek için sağlanan CancellationSignal öğesini düzenli olarak kontrol etmeniz gerekir.
onOpenPreview
onOpenPreview() yöntemi, sağlanan mediaId öğesi için sağlanan size öğesinin küçük resmini döndürmelidir. Küçük resim, orijinal CloudMediaProviderContract.MediaColumns#MIME_TYPE biçiminde olmalı ve onOpenMedia tarafından döndürülen öğeden çok daha düşük çözünürlüklü olması beklenir. İçerik cihaza indirilirken bu yöntem engellenirse terk edilen istekleri iptal etmek için sağlanan CancellationSignal öğesini düzenli olarak kontrol etmeniz gerekir.
onCreateCloudMediaSurfaceController
onCreateCloudMediaSurfaceController() yöntemi, medya öğelerinin önizlemesini oluşturmak için kullanılan bir CloudMediaSurfaceController veya önizleme oluşturma desteklenmiyorsa null döndürmelidir.
CloudMediaSurfaceController, Surface'nin belirli örneklerinde medya öğelerinin önizlemesinin oluşturulmasını yönetir. Bu sınıfın yöntemleri eşzamansız olacak şekilde tasarlanmıştır ve ağır işlemler yaparak engelleme yapmamalıdır. Tek bir CloudMediaSurfaceController örneği, birden fazla platformla ilişkili birden fazla medya öğesini oluşturmaktan sorumludur.
CloudMediaSurfaceController, aşağıdaki yaşam döngüsü geri çağırma listesini destekler:
onConfigChangeonDestroyonMediaPauseonMediaPlayonMediaSeekToonPlayerCreateonPlayerReleaseonSurfaceChangedonSurfaceCreatedonSurfaceDestroyed