Android & Kotlin ve Retrofit ile Veri Çekip Listeleme Nasıl Yapılır?
Kemerleri sıkı bağlayın, en temel uygulamalardan birini yapacağız 💨
Pek çok kez internetten veri çekme, rest api, servise istek atma gibi terimler duymuşsunuzdur. Uygulama geliştirirken internetin nimetlerinden faydalanmak istiyorsak bu terimlere aşina olmak şart. Hele bir mobil app yapıyorsak servislerle işimiz pek hayli fazla olacaktır.
Peki nedir bu servis dediğimiz şey? Öncelikle konuyu dağıtmadan biraz internetin mantığından bahsetmek lazım. İnternet dediğimiz şeyi pek çok bilgisayarın birbiriyle bağlantıda ve iletişim halinde olduğu bir ağ olarak düşünebiliriz. Bu bilgisayarlardan iletişimi başlatana müşteri(client), cevap veren hizmet edene de servis(service) diyoruz.
Bu iletişimde bir başka bilgisayardaki bilgilere erişme isteğinde bulunuruz, karşı bilgisayar kabul ederse istediğimiz bilgileri gönderir. Aynı şekilde bir başka bilgisayar da bizden istekte bulunabilir.
Şimdi sizlerle bizim istek atan olduğumuz ve servisten cevap beklediğimiz bir uygulamayı Android ortamında yapmaya çalışacağız. Bunu kolay ve etkili şekilde başarabilmek için bir kaç kütüphaneden yardım almamız lazım. İsterseniz bu projede kullandığım bütün kütüphaneleri önceden build.gradle dosyasına aşağıdaki gibi ekleyelim ve “sync now” diyelim:
Peki nasıl bir veri elde etmeyi amaçlıyoruz? O veriyi nasıl anlamlandıracağız? Dünya genelinde kabul gören ve verileri kendi gözlerimizle dahi rahatlıkla kavrayabileceğimiz bir dosyalama sistemi var, adı “JSON”. JSON’un yaptığı iş aslında bir objeyi string formatına dönüştürmek. Örnek bir JSON dosyası şu şekilde gözükür:
JSON’ın açılımı JavaScript Object Notation, fakat sadece JS değil diğer programlama dillerinde de kabul görmüştür. JSON’u kullanabilmek için bir objeye dönüştürmek gerekir. Bu işleme “deserialization” denir. Tersi olan durum yani bir objeyi JSON stringine dönüştürmeye ise “serialization” denir. Bu iki kavram önemli.
İnternetten veri çekeceğiz dedik peki hangi veri kaynağını kullanacağız? Ben bu örnekte Firebase üzerinde oluşturulmuş bir veritabanını kullandım. Hızlıca bir şeyler denemek isterseniz kullanabileceğiniz en güzel yöntem bu. Bu örnek için çok detayını bilmenize gerek yok. Ama nasıl kullanıldığını anlatan 5 dakikalık yazının linkini de şuraya bırakıyorum:
İnternette pek çok şirketin API’larına ulaşabilirsiniz. Instagram, Facebook, THY hatta İBB’nin açık veri havuzunu kullanabilirsiniz.
Bizim veri çekeceğimiz kaynağa ise şuradan erişebilir ve inceleyebilirsiniz. Kısaca servis cevap kodlarını kedilerin çeşitli fotoğraflarıyla özdeşleştiren bir veri yığını 😺
https://hwasampleapi.firebaseio.com/http.json
Pekiii Kotlin bu JSON’u nasıl anlamlandıracak? Bu işlemi yapmak için pek çok çözüm mevcut. Bunlardan en köklü ve uzun zamandır popüler olanı Google tarafından geliştirilen gson. Ancak içerisinde 12 yıllık commit bulunan gson biraz eskidi ve hantallaştı😕. Bunun yerine direkt Kotlin ekibi tarafından geliştirilen Kotlinx/serialization kütüphanesini kullanacağız.
Bu kütüphanenin amacı internetten çektiğimiz JSON verilerini kotlin classına çevirmek. Bunu yapmak için de şöyle bir class modeline ihtiyacımız var:
“@Serializable” ibaresi bu classa JSON için kullanılabilme özelliği kazandırıyor. “@SerialName” dediği ise JSON içerisinde gördüğümüz anahtar kelimelerle eşleşen kısım, birebir uyumlu olması lazım. Altlarındaki val’ların isimlerini istediğiniz gibi yazabilirsiniz ancak aynısı olması daha hoş olur.
Bu classları elinizle tek tek oluşturmak zahmetli geliyorsa bunun için de bir araç var. Android Studio’da plugins kısmına JSON To Kotlin Class yazın ve çıkan eklentiyi indirin.
Bunu yükledikten sonra boş bir dosyaya gelin ve yukarıdaki menüden Code->Generate->Generate Kotlin Data Class Code veya kısa yol olarak Alt+K yapın.
Çıkan şu pencereye bir adet basmakalıp JSON objesi yerleştirip classınızın ismini verin. Ayrıca Advanced->Annotation>kotlinx.serialization seçmeyi unutmayın. Ta taaa class’ımız hazır :)
Şimdi gelelim Retrofit ile biraz önce endpointini vermiş olduğum api ye nasıl istek atarız onu öğrenmeye. Normalde pek çok karmaşık mevzunun olduğu bu işlem Retrofit sayesinde küçük bir interface oluşturmak kadar basit bir işe indirgenebiliyor. Aşağidaki gibi bir interface oluşturalım.
“@GET” ile talep ettiğimiz verinin hangi endpointten geleceğini belirliyoruz. Bunun için dikkat etmemiz gereken linkin sabit olan kısmını değil değişken olan yeri, uzantıyı yazmak.
Get işlemi bu servisten veri çek anlamına geliyor. Altındaki ise uygulamamızda istek atmak için kullanacağımız fonksiyon. Kendisi bir liste halinde daha önce tanımladığımız HtppCatModel’leri döndürüyor. Suspend ibaresi ise bu işlemin ana rutin dışında destekçi rutinlerde gerçekleştiğini belirtiyor. Bu sayede bir yandan ekran çizimini aksatmıyoruz, bir yandan da servise istek atma kısmı da devam ediyor.
Şimdi yapmamız gereken Retrofit objesini inşa etmek. Bu zahmetli bir iş olduğundan, arkada pek çok iş yaptığından dolayı uygulamamızı yormamak adına sadece bir tane obje oluşturalım ve uygulamanın geri kalanında bunu çağıralım. Aşağıdaki gibi yazalım:
BASE_URL dediğimiz servis linkinin sabit olan kısmı. Tabi burada farklı bir işlem daha yapmamız gerekti o da JSON objesini retrofit2-kotlinx-serialization-converter kullanarak Retrofitin kullanımına uygun hale getirmek. Ve sonunda retrofit objemizi oluşturduk. Bununla istek atacağız, dönen veriyi bir kotlin objesinde tutacağız evet ama bunları nasıl ekranda göstereceğiz?
Elimizde pek çok Http Cat olacak ve bunları listelemek istiyoruz. Listeleme yöntemlerinden en sevileni RAM dostu arkadaşımız RecyclerView’ı kullanacağız. Hemen bir activity_main.xml adında bir layout dosyası açıp şunları yazalım:
Ve bir adet layout da bu RecyclerView’da listelenecek olan kartların tasarımı için açalım. İsmine list_item_card.xml verelim:
Bunlar bize yapmak istediğimiz iş için bir şablon olarak yeterli gelecektir. Şimdi esas yapmamız gereken apiden veriyi çekip RecyclerView içine adapte etmek. Bu iş için de bir adapter sınıfına ihtiyacımız var. Aşağıdaki gibi yazalım, ardından açıklamaya koyulacağım.
Bu DataAdapter sınıfı çektiğimiz verileri listemize adapte etmemizi sağlayacak. Evet elimizde veriler JSON olarak var ama bu JSON verilerinin arayüzde hangi elemana denk geleceğini ne yazık ki kendi kendine anlamıyor, anlatmak lazım.
ViewHolder diye bir alt sınıf açıp burada arayüz elemanlarımızı id leri ile çağırıp tanıtıyoruz. Bu sınıf bizim listede gördüğümüz her bir kart elemanını temsil edecek.
Adapter sınıfımız RecyclerView.Adapter’dan türediği için onCreateViewHolder, onBindViewHolder ve getItemCount fonksiyonlarını override etmemiz gerekiyor. Peki nedir bunlar biraz ondan bahsedelim:
onCreateViewHolder: RecyclerView’ımız yeni bir ViewHolder oluşturmaya ihtiyaç duyduğu anda çağırılır.
onBindViewHolder: Belli bir indisteki ViewHolder elemanının nasıl gözükeceğine karar verir.
getItemCount: Basitçe listemizdeki eleman sayısını döndürür.
Eveet, eğer gözünüzden kaçmadıysa burada ViewHolder’ımıza internetten çektiğimiz resimlerin linklerini yerleştirmek için başka bir kütüphane daha kullandım. Bu işlem için de pek çok farklı çözüm var, mesela Glide ve Picasso gibi. Ancak biz daha Kotlin ve coroutines dostu, az yer kaplayan Coil’i kullandık. Kullanımı da çok basit. Sadece load(imageURL) deyip internetteki bir görseli uygulamamıza ekleyebiliyoruz.
Uygulamamızı ayağa kaldırmak için son bir dokunuş kaldı. Hazırsanız MainActivity.kt’yi açalım ve şunları yazalım:
Daha önce yazıda bahsettiğimiz gibi ana ekran çizimlerinin kilitlenmemesi için veri çekme işlemini farklı bir yaşam döngüsü içerisinde gerçekleştirdik. Verimizi Retrofit ile çekip listemize ekledik. Listeyi de RecyclerView yardımıyla kullanmış olduk.
Buraya kadar takip edip herhangi bir sorunla karşılaşmadıysak uygulamamızın çıktısı aşağıdaki gibi olacaktır.
Aşağıdan projemin Github reposuna ulaşabilirsiniz.
Yolunda gitmeyen bir şeyler olursa bana ulaşabilirsiniz. Herkese bol veri çekmeli günler diliyorum 😋