2026'da Android Photo Picker API: galeriye erişim izni vermeden fotoğraf ekletmek
Android'in Photo Picker API'sine pratik bir rehber — tekli ve çoklu seçim, MIME filtreleme, Android 13 öncesi için destek katmanı ve neden READ_MEDIA_IMAGES'tan daha iyi.
Bir kullanıcıdan, tek bir gidere tek bir fiş fotoğrafı ekleyebilsin diye tüm fotoğraf kütüphanesini görme izni istemek, özelliğin ihtiyacından çok daha fazlasını istemek demektir. READ_MEDIA_IMAGES, uygulamanıza cihazdaki her fotoğrafa — tatil fotoğrafları, başkalarının mesajlarının ekran görüntüleri, içinde ne varsa — kalıcı erişim verir; oysa özellik yalnızca kullanıcının seçtiği tek bir görüntüye ihtiyaç duyar. Android’in Photo Picker’ı tam olarak bu uyumsuzluğu kapatmak için tasarlandı.
Bunu Granyn’e, bir kullanıcının kaydettiği bir gidere fiş fotoğrafı ekleyebilmesi için ekledim. İşte API’nin gerçekte nasıl çalıştığı ve izin isteğini tamamen ortadan kaldırması.
Eski yöntem ve neden bir sorumluluk taşıdığı
Photo Picker’dan önce, “kullanıcının bir fotoğraf seçmesine izin ver” iki yoldan biri anlamına geliyordu: READ_MEDIA_IMAGES (ya da Android 13 öncesinde READ_EXTERNAL_STORAGE) isteyip MediaStore’u kendiniz sorgulamak, ya da ACTION_GET_CONTENT’i başlatıp sistemin belge seçicisinin farklı OEM arayüzlerinde tutarlı davranmasını ummak. İzin yolu işe yarar, ama bir bedeli vardır:
- Alışkanlıkla reddedilmesi kolay bir özellik için çalışma zamanı izin istemi, ki bu sessizce kullanıcıların bir kısmı için özelliği öldürür.
- Play Console’un Hassas İzinler beyan akışı — geniş medya erişimi, inceleme sırasında daha fazla dikkat çeker.
- Uygulamanız yalnızca kullanıcının seçtiği tek fotoğrafa dokunsa bile, gizlilik politikanızda gerekçelendirmeniz gereken kalıcı erişim.
Bunların hiçbiri kullanıcıya bir şey kazandırmaz. Özelliğin ihtiyacı olan tek şey, tek bir görüntüye, tek seferlik okuma erişimidir.
Photo Picker gerçekte nasıl çalışır
Photo Picker, sistemin sahip olduğu bir arayüzdür — ayrı, güvenilir bir süreçte çalışır, ve kullanıcının dokunduğu görüntü uygulamanıza bir content:// URI’si olarak teslim edilir. Uygulamanız asla galeriye bir izin izni almaz; sadece o tek URI’yi, o fotoğrafla sınırlı olarak, ihtiyacınız olduğu süre boyunca alır.
Tekli seçim için kurulum hiçbir manifest izni gerektirmez:
class AddExpenseFragment : Fragment() {
private val pickImage = registerForActivityResult(
ActivityResultContracts.PickVisualMedia(),
) { uri: Uri? ->
if (uri != null) attachReceiptPhoto(uri)
}
private fun launchPicker() {
pickImage.launch(
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly),
)
}
}
ActivityResultContracts.PickVisualMedia.ImageOnly, seçiciyi görüntülerle sınırlar; VideoOnly ve SingleMimeType("image/png") diğer yaygın durumları kapsar. <uses-permission> girdisi yok, çalışma zamanı istemi yok, gerekçe metni yazmanız gereken bir diyalog yok.
Birden fazla fotoğraf seçmek
Bir fiş genelde tek fotoğraftır, ama aynı contract, sizin seçtiğiniz bir sayıyla sınırlı çoklu seçim varyantına da sahiptir:
private val pickImages = registerForActivityResult(
ActivityResultContracts.PickMultipleVisualMedia(maxItems = 5),
) { uris: List<Uri> ->
uris.forEach { attachReceiptPhoto(it) }
}
Sistem arayüzü sınırı kendisi uygular — kullanıcı limite ulaştığında daha fazla dokunmayı devre dışı bırakır; kodunuzun sonradan daha uzun bir listeyi reddetmesi gerekmez.
Geri aldığınız URI kalıcı değil
Burası insanların takıldığı ayrıntı: ActivityResultCallback’inize teslim edilen content:// URI’si, yalnızca o çağrının süresi boyunca okunabilir olduğu garanti edilir. URI dizesini veritabanınızda saklayıp günler sonra açmaya çalışırsanız, bir SecurityException fırlatabilir — izin, kalıcılaştırılmış bir Storage Access Framework URI’sinin yaptığı gibi seçici oturumundan daha uzun ömürlü değildir.
Çözüm, ihtiyacınız olan bayt verisini hemen, uygulamanızın sahip olduğu bir dosyaya kopyalamaktır:
private fun attachReceiptPhoto(sourceUri: Uri) {
val destFile = File(requireContext().filesDir, "receipts/${UUID.randomUUID()}.jpg")
destFile.parentFile?.mkdirs()
requireContext().contentResolver.openInputStream(sourceUri)?.use { input ->
destFile.outputStream().use { output -> input.copyTo(output) }
}
viewModel.setReceiptPath(destFile.absolutePath)
}
Kopyalamayı ana iş parçacığının dışında yapın — dosya G/Ç’sidir — ama seçicinin Activity sonucu geri çağrısı dönmeden önce yapın, sonraki bir ekranda değil. Bayt verisini kopyaladıktan sonra, geçici content:// izni artık önemli değildir; uygulamanız ihtiyacı olduğu sürece okuyabileceği özel bir dosyaya sahiptir.
Android 13 öncesi için destek katmanı
Photo Picker, sistem özelliği olarak Android 13’te geldi, ama Google Play services aracılığıyla API 21+‘a geriye dönük destekleniyor — aynı ActivityResultContracts.PickVisualMedia çağrısı, Play services yüklü eski cihazlarda otomatik olarak bu destek katmanına çözümlenir. Minimum özellik setiniz gerçekten yerel seçicinin davranışını gerektiriyorsa (uygulama başına bulut medya sağlayıcı entegrasyonu gibi) açıkça kontrol etmeye değer bir şey var:
val isPhotoPickerAvailable = ActivityResultContracts.PickVisualMedia
.isPhotoPickerAvailable(requireContext())
Pratikte, basit bir “fotoğraf ekle” akışı için bunu dallandırmanıza gerek yok — contract kendi kendine sorunsuzca geriler. Bu, yalnızca yerel sürümde anlamlı olan seçiciye özgü arayüz ipuçları göstermeye karar verirken önem kazanır.
Gerçekte önemli olan
Buradaki kazanım yeni bir jest ya da daha güzel bir seçici arayüzü değil — izin modelinin sonunda özelliğin yaptığıyla eşleşmesi. Tek bir fiş fotoğrafı ekleyen bir kullanıcının, tüm kamera rulosuna kalıcı erişim vermesi hiç gerekmemeliydi, ve Photo Picker var olana kadar Android’in bunu önlemenin temiz bir yolu yoktu. READ_MEDIA_IMAGES’ı PickVisualMedia ile değiştirmek bir manifest iznini, bir çalışma zamanı istemini ve gizlilik politikamın bir paragrafını kaldırdı — ve özellik, kullanıcı tarafında tam olarak aynı şekilde çalışıyor, sadece ihtiyacından fazlasını istemeden.
// İlgili okumalar
Günlükten dahası
Android'de biyometrik kimlik doğrulama, 2026: BiometricPrompt, Keystore ve yerel öncelikli bir uygulamayı kilitlemek
androidx.biometric için pratik bir rehber — BiometricPrompt, CryptoObject destekli anahtarlar, cihaz kimlik bilgisi yedeği ve bir parmak izi kontrolünün hiçbir şeyi korumamasına yol açan hatalar.
2026'da Android'de Health Connect: local-first'ten vazgeçmeden veri okuyup yazmak
Android'in Health Connect API'sine 2026 için pratik bir rehber — izinler, arka planda okuma ve local-first uygulamaların bunu neden gerçek değil, isteğe bağlı bir katman olarak görmesi gerektiği.
2026'da androidx.startup: bir yığın ContentProvider olmadan kütüphane başlatma sırası
Android'in App Startup kütüphanesine pratik bir bakış — initializer'ları tek bir ContentProvider'da toplamak, bağımlılıkları tanımlamak ve yerine geçemediği tembel-init durumları.