Bir bulut medya sağlayıcısı, 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 sağlanan fotoğraf veya videoları seçebilir. Ayrıca bir bulut medya sağlayıcısı, Android fotoğraf seçicide göz atılabilecek albümler hakkında bilgi de verebilir.
Başlamadan önce
Bulut medya sağlayıcınızı oluşturmaya başlamadan önce aşağıdaki noktaları göz önünde bulundurun.
Uygunluk
Android, OEM tarafından atanan uygulamaların bulut medya sağlayıcısı olmasına izin vermek için bir pilot program yürütmektedir. Şu anda yalnızca OEM'ler tarafından aday gösterilen uygulamalar bu programa katılarak Android için bulut medya sağlayıcısı olmaya uygundur. Her OEM en fazla 3 uygulamayı aday gösterebilir. Bu uygulamalar onaylandıktan sonra, yüklendikleri GMS Android destekli cihazlarda bulut medya sağlayıcısı olarak erişilebilir hale gelir.
Android, uygun tüm bulut sağlayıcılarının sunucu tarafında bir listesini tutar. Her OEM, yapılandırılabilir bir yer paylaşımı kullanarak varsayılan bir bulut sağlayıcısı seçebilir. Belirlenen uygulamalar tüm teknik gereksinimleri karşılamalı ve tüm kalite testlerini geçmelidir. OEM bulut medya sağlayıcısı pilot programının süreci ve şartları hakkında daha fazla bilgi edinmek için sorgu formunu doldurun.
Bir bulut medya sağlayıcısı oluşturmanız gerekip gerekmediğine karar verin
Bulut medya sağlayıcıları, buluttaki fotoğraf ve videoları yedeklemek ve almak için kullanıcıların birincil kaynağı olarak işlev gören uygulamalar veya hizmetlerdir. Uygulamanızın yararlı içeriklerden oluşan bir kitaplığı varsa ancak genellikle fotoğraf depolama çözümü olarak kullanılmıyorsa bunun yerine bir doküman sağlayıcı oluşturmayı düşünmelisiniz.
Profil başına bir etkin bulut sağlayıcısı
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ısı uygulamasını diledikleri zaman fotoğraf seçici ayarlarından kaldırabilir veya değiştirebilir.
Android fotoğraf seçici, varsayılan olarak bir bulut sağlayıcısını otomatik olarak seçmeye çalışır.
- Cihazda yalnızca tek bir uygun bulut sağlayıcı varsa bu uygulama otomatik olarak geçerli sağlayıcı olarak seçilir.
Cihazda birden fazla uygun bulut sağlayıcısı varsa ve bu sağlayıcılardan biri, OEM'nin seçtiği varsayılan ayarla eşleşiyorsa OEM tarafından seçilen uygulama seçilir.
Cihazda birden fazla uygun bulut sağlayıcısı varsa ve bunlardan hiçbiri OEM'nin seçtiği varsayılan ayarla eşleşmiyorsa herhangi bir uygulama seçilmez.
Bulut medya sağlayıcınızı oluşturun
Aşağıdaki şemada, Android uygulaması, Android fotoğraf seçici, yerel cihazın MediaProvider
ve CloudMediaProvider
arasında fotoğraf seçimi oturumu öncesinde ve 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 düzenli aralıklarla Android fotoğraf seçici arka ucuyla senkronize eder.
- Bir Android uygulaması fotoğraf seçiciyi başlattığında kullanıcıya birleştirilmiş bir yerel öğe veya bulut öğe ızgarası göstermeden önce fotoğraf seçici, 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ı senkronizasyon gerçekleştirir. Yanıt aldıktan veya son tarihe ulaşıldığında fotoğraf seçici ızgarasında artık erişilebilir tüm fotoğraflar görüntülenerek cihazınızda yerel olarak depolanan fotoğraflar, buluttan senkronize edilen fotoğraflarla birleştirilir.
- Kullanıcı ekranı kaydırırken fotoğraf seçici, kullanıcı arayüzünde görüntülenmek üzere bulut medya sağlayıcısından medya küçük resimleri getirir.
- Kullanıcı oturumu tamamladığında ve sonuçlarda bir bulut medya öğesi bulunduğunda fotoğraf seçici, içerik için dosya tanımlayıcıları ister, URI oluşturur ve çağrı yapan uygulamaya dosyaya erişim izni verir.
- Uygulama artık URI'yı açabiliyor ve medya içeriklerine salt okuma erişimi sağlayabiliyor. 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 sistemini kullanır.
Sık Karşılaşılan Sorunlar
Uygulamanızı değerlendirirken aklınızda bulundurmanız gereken bazı önemli noktalar şunlardır:
Yinelenen dosyalardan kaçının
Android fotoğraf seçici, bulut medya durumunu hiçbir şekilde inceleyemediği için CloudMediaProvider
ürününün hem bulutta hem de yerel cihazda bulunan herhangi bir dosyanın imleç satırındaki MEDIA_STORE_URI
değerini sağlaması gerekir. Aksi takdirde kullanıcı, fotoğraf seçicide yinelenen dosyalar görür.
Resim boyutlarını önizleme görünümü için optimize edin
onOpenPreview
adresinden döndürülen dosyanın tam çözünürlüklü resim olmaması ve istenen Size
şartlarına uygun olması çok önemlidir. Çok büyük boyutlu bir resim, kullanıcı arayüzünde yükleme sürelerine neden olur ve çok küçük bir resim, cihazın ekran boyutuna bağlı olarak pikselleştirilebilir veya bulanık olabilir.
Doğru yönü tutma
onOpenPreview
içinde döndürülen küçük resimler kendi EXIF verilerini içermiyorsa, küçük resimlerin önizleme ızgarasında yanlış bir şekilde döndürülmesini önlemek için doğru yönde döndürülmelidirler.
Yetkisiz erişimi engelleyin
ContentProvider'dan arayana veri döndürmeden önce MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION
olup olmadığını kontrol edin. Bu, yetkisiz uygulamaların bulut verilerine erişmesini önler.
CloudMediaProvider sınıfı
android.content.ContentProvider
değişkeninden türetilen CloudMediaProvider
sınıfı, aşağıdaki örnekte gösterilenlere benzer 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 bir CloudMediaProviderContract
sınıfı içerir.
Bu sınıfta fotoğraf seçici ile bulut medya sağlayıcısı arasındaki birlikte çalışabilirlik özetlenmektedir. Bu esnada senkronizasyon işlemleri için MediaCollectionInfo
, öngörülen Cursor
sütunları ve Bundle
ekstra özellikler ele alınmaktadı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, önbelleğe alınan bulut medya öğelerinin geçerliliğini değerlendirmek ve bulut medya sağlayıcısı ile gerekli senkronizasyonu belirlemek için işletim sistemi tarafından kullanılır. İşletim sisteminin sıklıkla çağrı yapma potansiyeli nedeniyle onGetMediaCollectionInfo()
, performans açısından kritik öneme sahiptir. Uzun süren işlemlerden veya performansı olumsuz yönde 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 sabit değerleri içerir:
Sorgu Medyası
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 ya da tam ya da artımlı senkronizasyon durumu gerekli olduğunda fotoğraf seçici oturumları sırasında çağrılabilir. Fotoğraf seçici kullanıcı arayüzü, sonuçların görüntülenmesi için süresiz olarak yanıt beklemez ve kullanıcı arayüzü amacıyla bu istekleri zaman aşımına uğratabilir. Döndürülen imleç, gelecekteki oturumlar için fotoğraf seçicinin veritabanına işlenmeye devam edecektir.
Bu yöntem, medya koleksiyonundaki tüm medya öğelerini temsil eden ve isteğe bağlı olarak sağlanan ekstralara göre filtrelenen ve MediaColumns#DATE_TAKEN_MILLIS
öğesinin ters kronolojik düzeninde (en son öğe öncelikli) sıralanan bir Cursor
döndürür.
Döndürülen CloudMediaProviderContract
paketi aşağıdaki sabit değerleri içerir:
EXTRA_ALBUM_ID
EXTRA_LOOPING_PLAYBACK_ENABLED
EXTRA_MEDIA_COLLECTION_ID
EXTRA_PAGE_SIZE
EXTRA_PAGE_TOKEN
EXTRA_PREVIEW_THUMBNAIL
EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED
EXTRA_SYNC_GENERATION
MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION
PROVIDER_INTERFACE
Bulut medya sağlayıcısı, 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
geçersiz kılınır. Bulut medya sağlayıcısı, sağlanan ekstralarda herhangi bir filtre işlediyse döndürülen Cursor#setExtras
kapsamında anahtarı ContentResolver#EXTRA_HONORED_ARGS
öğesine eklemelidir.
onQuerySilinmişMedya
Bulut hesabındaki silinmiş öğelerin, fotoğraf seçici kullanıcı arayüzünden doğru şekilde kaldırılmasını sağlamak için onQueryDeletedMedia()
yöntemi kullanılır. Potansiyel gecikme hassasiyetlerinden dolayı bu çağrılar aşağıdakilerin kapsamında başlatılabilir:
- Arka planda proaktif senkronizasyon
- Fotoğraf seçici oturumları (tam veya artımlı senkronizasyon durumu gerekli olduğunda)
Fotoğraf seçicinin kullanıcı arayüzü, duyarlı kullanıcı deneyimine öncelik verir ve yanıt almak için süresiz olarak beklemez. Etkileşimlerin sorunsuz şekilde yürütülebilmesi için
zaman aşımları yaşanabilir. Döndürülen tüm Cursor
verileri, gelecekteki oturumlar için fotoğraf seçicinin veritabanında işlenmeye çalışılır.
Bu yöntem, onGetMediaCollectionInfo()
tarafından döndürülen ve geçerli sağlayıcı sürümündeki medya koleksiyonunun tamamında yer alan silinmiş tüm medya öğelerini temsil eden bir Cursor
döndürür. Bu öğeler isteğe bağlı olarak ekstralara göre filtrelenebilir.
Bulut medya sağlayıcısı, CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID
öğesini döndürülen Cursor#setExtras
politikasının bir parçası olarak ayarlamalıdır. Bu ayar yapılmaması bir hatadır ve Cursor
geçersiz kılınır. Sağlayıcı, sağlanan ekstralarda herhangi bir filtre gerçekleştirdiyse anahtarı ContentResolver#EXTRA_HONORED_ARGS
öğesine eklemelidir.
onQueryAlbümler
onQueryAlbums()
yöntemi, bulut sağlayıcıda bulunan Cloud albümlerinin listesini ve bunlarla ilişkili meta verileri getirmek için kullanılır. Ayrıntılı bilgi için CloudMediaProviderContract.AlbumColumns
sayfasını inceleyin.
Bu yöntem , medya koleksiyonundaki tüm albüm öğelerini temsil eden ve isteğe bağlı olarak sağlanan ekstralara göre filtrelenen ve en son AlbumColumns#DATE_TAKEN_MILLIS
olan öğeler arasında ters kronolojik sıralamada sıralanmış bir Cursor
döndürür. Bulut medya sağlayıcısı, döndürülen Cursor
öğesinin parçası olarak CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID
özelliğini ayarlamalıdır. Bunun ayarlanmaması bir hatadır ve döndürülen Cursor
geçersiz kılınır. Sağlayıcı, sağlanan ekstralarda herhangi bir filtreyi ele aldıysa anahtarı döndürülen Cursor
öğesinin parçası olarak ContentResolver#EXTRA_HONORED_ARGS
öğesine eklemelidir.
onOpenMedia
onOpenMedia()
yöntemi, sağlanan mediaId
tarafından tanımlanan tam boyutlu medyayı döndürmelidir. Bu yöntem cihaza içerik indirirken engelleme yaparsa iptal edilen 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
boyutunda olmalı ve onOpenMedia
tarafından döndürülen öğeden çok daha düşük çözünürlüklü olması beklenmektedir. Cihaza içerik indirirken bu yöntem engellenirse vazgeçilen istekleri iptal etmek için sağlanan CancellationSignal
değerini 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
, belirtilen Surface
örneklerinde medya öğelerinin önizlemesini oluşturmayı yönetir. Bu sınıfın yöntemlerinin eşzamansız olmaları amaçlanmıştır ve yoğun bir işlem gerçekleştirerek engelleme yapmamalıdır. Tek bir CloudMediaSurfaceController
örneği, birden çok yüzeyle ilişkilendirilmiş birden çok medya öğesini oluşturmaktan sorumludur.
CloudMediaSurfaceController
aşağıdaki yaşam döngüsü geri çağırma listesini destekler:
onConfigChange
onDestroy
onMediaPause
onMediaPlay
onMediaSeekTo
onPlayerCreate
onPlayerRelease
onSurfaceChanged
onSurfaceCreated
onSurfaceDestroyed