İçeriğe geç
Tüm yazılar

Android'de ML Kit ile barkod tarama (2026): Stocky bir kiler ürününü bir saniyeden kısa sürede nasıl ekliyor

ML Kit ve CameraX ile cihaz üzerinde barkod tarama için pratik bir 2026 rehberi: format ayarı, çevrimdışı ürün arama ve bir kiler uygulamasının arkasındaki kısmi kullanım matematiği.

MFKAPPS 5 dk okuma

Bir market ürününü uygulamaya elle yazmak, her satın aldığınızda ödediğiniz küçük bir vergidir. Ad, marka, boy, belki bir kategori — mutfağını stoklayan biri için bu, sahip olduğu her ürünle çarpılan otuz saniyelik bir sürtünme. Kullanmaya değer tek kiler uygulaması bu matematiği yenmek zorunda, bu da barkodun neredeyse tüm işi yapması gerektiği anlamına gelir.

Stocky’nin kurulduğu kısıtlama tam olarak bu: kamerayı bir barkoda doğrult, ürün bir saniye sonra kilerde olsun — ad, boy, kategori, beslenme verisi, hepsi dolu. İşte o bir saniyenin arkasındaki hat ve taramanın kendisinden daha uzun sürede doğru hale gelen kısımlar.

Tarama hattı: CameraX + ML Kit Barcode Scanning

ML Kit’in Barcode Scanning API’si, Text Recognition’dan ayrı bir model — daha küçük, daha hızlı ve tam olarak tek bir iş için kurulmuş: bir kamera karesindeki barkodun formatını ve kodlanmış değerini bulmak. Kurulum tek satırlık bir Gradle bağımlılığı artı bir CameraX analyzer:

// app/build.gradle.kts
dependencies {
    implementation("com.google.mlkit:barcode-scanning:17.3.0")
    implementation("androidx.camera:camera-camera2:1.4.0")
    implementation("androidx.camera:camera-lifecycle:1.4.0")
}
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.common.InputImage
import androidx.camera.core.ImageProxy

private val options = BarcodeScannerOptions.Builder()
    .setBarcodeFormats(
        Barcode.FORMAT_EAN_13,
        Barcode.FORMAT_EAN_8,
        Barcode.FORMAT_UPC_A,
        Barcode.FORMAT_UPC_E,
    )
    .build()

private val scanner = BarcodeScanning.getClient(options)

@androidx.camera.core.ExperimentalGetImage
fun analyzeFrame(image: ImageProxy, onFound: (String) -> Unit) {
    val media = image.image ?: return image.close()
    val input = InputImage.fromMediaImage(media, image.imageInfo.rotationDegrees)

    scanner.process(input)
        .addOnSuccessListener { barcodes ->
            barcodes.firstOrNull()?.rawValue?.let(onFound)
        }
        .addOnCompleteListener { image.close() }
}

setBarcodeFormats çağrısı göründüğünden daha önemli. Varsayılanda bırakıldığında, tarayıcı bir konserve domates kutusunda asla göremeyeceği QR kodları, PDF417, Aztec ve daha bir düzine format için de arama yapar — her kare bu aramanın bedelini öder. Perakende ürünleri neredeyse tamamen EAN-13, EAN-8, UPC-A ve UPC-E içinde yaşar. Bu dört formatla sınırlamak her karedeki boşa harcanan işi azaltır ve daha da önemlisi, arka plandaki rastgele desenlerden gelen yanlış-pozitif tespitleri keser.

13 haneli bir sayıdan gösterebileceğiniz bir ürüne

Bir barkod yalnızca rakamlardır. Gerçek ürün — ad, marka, paket boyutu, beslenme bilgisi — başka bir yerden gelmek zorunda. Stocky bunu, açık kaynaklı, topluluk tarafından beslenen ürün veritabanı Open Food Facts üzerinden çözer; buna genel veri setinin kaçırdığı, topluluk tarafından eklenen Türk ürünleri de eklenir.

Arama işleminin kendisi, taranan koda göre anahtarlanmış sıradan bir REST çağrısıdır:

suspend fun lookupProduct(barcode: String): ProductInfo? {
    localCache.find(barcode)?.let { return it }

    return runCatching {
        api.getProduct(barcode).toProductInfo()
    }.getOrNull()?.also { localCache.save(barcode, it) }
}

Burada kodun ima ettiğinden daha fazla ağırlık taşıyan iki karar var:

  • Önce önbellek, önce ağ değil. Bir barkod bir kez çözüldükten sonra — herhangi biri tarafından, aynı cihazdaki önceki bir tarama da dahil — artık Room içindedir ve bir sonraki tarama anında ve çevrimdışı gerçekleşir. Bir kiler uygulaması için aynı bir düzine ürün (süt, yumurta, aynı marka ekmek) sürekli yeniden taranır; önbellek isabet oranı hızla yükselir.
  • Bulamamak çıkmaz sokak değildir. Markasız veya bölgeye özgü ürünler her zaman Open Food Facts’te olmayacaktır. Arama boş dönerse, Stocky çıplak bir hata yerine barkodla önceden doldurulmuş kısa bir manuel giriş formuna geçer. Kullanıcı iki alanı doldurarak işi bitirir ve — onay verirse — bu kayıt, aynı barkodu tarayacak bir sonraki kişi için topluluk veri setini güçlendirebilir.

”Bir saniyeden kısa”yı dürüst tutmak

Tarama modelinin kendisi orta seviye bir telefonda 100 milisaniyenin çok altında bir barkodu çözer. Algılanan gecikmenin neredeyse tamamı etrafındaki her şeye gider: kamera ısınması, analyzer’ın kare hızı ve yinelenen tespitlerden kaçınmak.

His açısından asıl önemli olan ayrıntı şu: debounce olmadan, sabit tutulan bir kamera her karede analyzer’ı tetikler — kullanıcının elinin sabitlenmesi için geçen sürede aynı barkodun yirmiden fazla tespiti. Her biri için bir arama, bir titreşim ve bir arayüz güncellemesi tetiklemek hız değil, takılma gibi hissettirir.

private var lastCode: String? = null
private var lastAt = 0L

fun onBarcodeFound(code: String, now: Long) {
    if (code == lastCode && now - lastAt < 1_500) return
    lastCode = code
    lastAt = now
    handleScan(code)
}

Kod başına 1,5 saniyelik bir debounce penceresi, kullanıcı kamerayı kasıtlı olarak uzaklaştırıp geri getirdikten sonra aynı ürünü bilerek yeniden taramasına izin verirken (mesela kilere ikinci bir yoğurt kabı eklemek), tüm bir tespit patlamasını tek bir taramaya indirmeye yeter.

Kısmi kullanım sorunu

Bir kiler ürünü ikili bir var-yok bayrağı değildir, ve barkod taramanın tek başına çözemediği kısım da burasıdır. Stocky’nin gerçek veri modeli, her paket için kalan bir miktarı takip eder: 1 litrelik bir süt kutusu “stokta” ya da “bitti” değildir, önce 1000 mldir, kahvaltıdan sonra 700 ml olur, sonra 0 olur ve sessizce alışveriş listesine taşınır. Aynı paketteki barkodu yeniden taramak, kullanıcının kısmi bir tüketimi kaydetmesini sağlar — “300 ml iç” — hâlâ buzdolabında yarı dolu olan bir şey için tam bir “bitirdim, yenisini ekle” döngüsüne zorlamak yerine.

Daha sonra bitiş tahminlerini dürüst kılan tam olarak bu tek tasarım kararıdır: her seferinde sıfıra dönen ve size hız hakkında hiçbir şey söylemeyen kaba bir stokta-var/stokta-yok anahtarı değil, ürün başına gerçek bir tüketim eğrisi üzerine kurulurlar.

Cihazda kalan, cihazda kalmayan

Kare hiçbir zaman telefondan çıkmaz — ML Kit’in barkod modeli, metin tanıma kardeşi gibi tamamen cihaz üzerinde çalışır. Ağdan geçen tek şey barkodun kendi rakamlarıdır; milyonlarca başka alışverişçinin zaten taradığı bir ürünü aramak için gönderilir. Hiçbir fotoğraf çekilmez veya saklanmaz ve isteğe kimin taradığına dair hiçbir şey eklenmez. Kiler içeriği, tüketim geçmişi ve harcamalar yerel kalır; yalnızca anonim ürün araması paylaşılır.

Bu, OCR’den daha küçük bir gizlilik yüzeyidir ve bir özellik için görüntü API’leri arasında seçim yaparken hatırlanmaya değer: ihtiyacınız olan değer sabit, yapılandırılmış bir tanımlayıcıysa — düzyazı bir sayfa değil, bir barkod — bir tarayıcı modeli, birinin belgesini okumak zorunda olan bir tanıyıcıdan hem kurması daha hızlı hem de bir gizlilik politikasında savunması daha kolaydır.

Stocky şu anda Google Play’de yayında — bir barkod tarayın, mutfak takibinin geri kalanı oradan gelsin. Uygulama sayfasında daha fazlası.