Retrofit Rehberi: Avantajları ve Android’de Retrofit ile RESTful API Etkileşimi

Merhaba! Android tarafında API çağrısı yapmanın can sıkıcı yanı işin kendisi değil, etrafındaki tekrar: bağlantıyı aç, gövdeyi oku, JSON'u ayrıştır, hataları yakala, işi doğru iş parçacığına taşı. Retrofit bu tekrarı ortadan kaldırıp geriye tek bir şey bırakıyor. Uç noktayı tarif eden bir arayüz yazmak.
Kütüphaneyi Square geliştiriyor ve altında yine Square’in OkHttp’si çalışıyor. Retrofit aslında HTTP konuşmuyor; yazdığınız arayüzü anotasyonlara bakarak OkHttp çağrılarına çeviriyor, dönen gövdeyi de bir dönüştürücüye veriyor. Bunu bilmek ilerideki birkaç tuzağı anlamayı kolaylaştıracak.
Önce genel resmi, sonra temel sınıfları, en sonunda da sık yapılan hataları ele alacağım. Kaynağa göz atmak isterseniz depo şurada:
Retrofit GitHubKısaca: Nedir Bu Retrofit?
Retrofit’i tarif eden anahtar kelime "tip güvenli HTTP istemcisi". Tip güvenli olan kısım şu: uç noktayı bir Java ya da Kotlin arayüzünde tarif ediyorsunuz, dönüş tipini de siz söylüyorsunuz. Retrofit çalışma zamanında o arayüzün somut bir uygulamasını üretiyor. Elle istek kurma, gövde okuma ya da JSON ayrıştırma kodu yazmıyorsunuz.
Diğer platformlarda karşılığı ne peki? iOS tarafında Alamofire ya da doğrudan URLSession, Flutter’da Dio ve http paketi, React Native’de çoğunlukla Axios aynı işi görüyor. İsimler değişiyor, fikir aynı kalıyor: ağ katmanını uygulama kodundan ayırmak.
Android’in kendi tarihinde de öncülleri var. AsyncTask basit senaryolarda iş görüyordu ama yaşam döngüsü sızıntıları yüzünden Android 11 (API 30) ile kullanımdan kaldırıldı; yeni kodda yeri yok. Volley hala ayakta ve küçük, sık, önbelleklenebilir isteklerde rahat. Retrofit ise API yüzeyi genişledikçe öne geçiyor, çünkü her yeni uç nokta ekstra kod değil, arayüze eklenen tek satır oluyor.

Retrofit Kullanmanın Avantajları
1. Anlaşılır ve Kolay Kullanım
Bir uç noktayı tarif etmek için gereken tek şey, anotasyonlarla süslenmiş bir metot imzası. Gövdesi yok, çünkü gövdeyi Retrofit yazıyor.
public interface ApiService { @GET("users/{userId}") Call<User> getUser(@Path("userId") int userId);}2. Modüler Yapı
Bütün uç noktaları tek bir arayüze yığmak zorunda değilsiniz. İlgili olanları kendi arayüzlerinde gruplamak, proje büyüdükçe hem aramayı hem de kod gözden geçirmeyi kolaylaştırır. Kullanıcı ve gönderi işlemlerini ayıralım:
// UserService.javapublic interface UserService { @GET("users/{userId}") Call<User> getUser(@Path("userId") int userId);} // PostService.javapublic interface PostService { @POST("posts") Call<Post> createPost(@Body Post post);}Bu ayrım kozmetik değil. Gönderi uç noktalarına dokunan bir değişiklik, kullanıcı tarafını gözden geçirmeye hiç zorlamaz. Üstelik ikisini de aynı Retrofit nesnesi üretebilir, yani ek bir maliyeti yok.
3. Gelişmiş Hata İzleme ve Ayıklama
Burada bilinmesi gereken bir ayrım var: isSuccessful() yalnızca 2xx yanıtlar için doğru döner. 404 ya da 500 istisna fırlatmaz, gayet normal bir yanıt olarak gelir. Ağ kopması veya ayrıştırma hatası ise IOException olarak karşınıza çıkar. Yani iki ayrı dal:
try { // Retrofit isteği yapılır Response<User> response = apiService.getUser(userId).execute(); if (response.isSuccessful()) { // Başarılı bir yanıt alındı, işlemlere devam edilir User user = response.body(); } else { // Başarısız yanıt alındı, HTTP hata durumları işlenir int statusCode = response.code(); String errorMessage = response.message(); // HTTP response ilgili işlemler yapılabilir switch (statusCode) { case 404: // HTTP 404 Not Found durumuyla ilgili işlemler break; case 500: // HTTP 500 Internal Server Error durumuyla ilgili işlemler break; default: // Diğer hata durumlarıyla ilgili genel işlemler break; } }} catch (IOException e) { // Ağ hatası veya dönüş değeri alınamazsa işlenir e.printStackTrace();}4. RxJava ile Asenkron Yapıyı Destekler
Yukarıdaki execute() çağrısının bloklayıcı olduğunu not edin; ana iş parçacığında çağırırsanız NetworkOnMainThreadException alırsınız. Asenkron çalışmanın bir yolu enqueue(), bir diğeri de dönüş tipini değiştirmek. RxJava kullanan projelerde uç nokta doğrudan Single döndürebilir:
interface ApiService { @GET("users/{userId}") fun getUser(@Path("userId") userId: Int): Single<User>}Bugün yeni yazılan Kotlin projelerinde daha yaygın olan yol ise coroutine desteği. Retrofit 2.6 ile birlikte metodu suspend yapıp doğrudan User döndürebiliyorsunuz, ayrı bir adaptöre gerek kalmıyor. RxJava zaten kurulu bir projede kalmak makul; sıfırdan başlıyorsanız coroutine tarafı daha az hareketli parça demek.
5. Özelleştirilebilir İstek (Request) Yapısı
Sabit başlıkları uç nokta bazında eklemek için @Headers anotasyonu yeterli. Aşağıda tek bir isteğe Cache-Control başlığı ekliyoruz:
// Örnek 1:public interface ApiService { @Headers("Cache-Control: max-age=640000") @GET("users/{userId}") Call<User> getCachedUser(@Path("userId") int userId);} // Örnek 2:// @Headers anotasyonu içinde birden fazla başlık belirleyebiliriz.public interface ApiService { @Headers({ "Accept: application/json", // Yanıtın JSON formatında olacağını belirtiyoruz "Cache-Control: max-age=640000" // Özel önbellek kontrol başlığı ekliyoruz }) @GET("users/{userId}") Call<User> getCachedUser(@Path("userId") int userId);}Accept başlığı istemcinin hangi formatta yanıt beklediğini söyler. Buradaki application/json değeri, sunucudan JSON isteniyor demek.
Cache-Control ise önbellek davranışını belirler. max-age=640000, alınan yanıtın yaklaşık 7,4 gün boyunca önbellekten servis edilebileceği anlamına geliyor. Burada atlanan bir ayrıntı var: önbelleği fiilen tutan Retrofit değil, altındaki OkHttp. OkHttpClient üzerinde bir Cache tanımlamadıysanız bu başlık tek başına hiçbir şey yapmaz.
Tek bir uç noktaya değil de bütün isteklere başlık eklemek istiyorsanız doğru yer arayüz değil, OkHttp interceptor’ı. Her çağrıda tekrarlanan kimlik doğrulama token’ı gibi başlıklar oraya konur; arayüze dağıtıldığında biri mutlaka unutulur.
Retrofit’de Kullanılan Temel Sınıflar (Class)
1. Model Sınıfları (Model Class)
Model sınıfları, API yanıtındaki JSON’un kod tarafındaki karşılığıdır. Alan adları JSON ile birebir tutuyorsa fazladan bir şey yapmanız gerekmez. Tutmuyorsa Gson tarafında @SerializedName ile eşleştirirsiniz. Kullanıcı yanıtını temsil eden bir User sınıfı şöyle görünür:
public class User { private String username; private String email; // ... diğer alanlar ve yöntemler}Dönüştürme işini Retrofit değil, kaydettiğiniz converter yapar. Gson en yaygını ama Moshi ve kotlinx.serialization da aynı yere takılır.
2. Retrofit Instance
Retrofit nesnesi, taban adres ve dönüştürücü gibi ayarları taşıyan merkezi yapılandırmadır. İki noktaya dikkat. baseUrl mutlaka bölü işaretiyle bitmeli, aksi halde Retrofit 2 daha kurulum anında hata verir. Ve bu nesne pahalıdır: uygulama başına tek örnek oluşturup paylaşın, her ekranda yenisini kurmayın.
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.mustafabaser.net/") .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService apiService = retrofit.create(ApiService.class);Buradaki create() çağrısı, yazdığınız arayüzün çalışma zamanında üretilmiş bir uygulamasını döndürür. O noktadan sonra elinizde sıradan bir nesne vardır, metotlarını normal şekilde çağırırsınız.
3. Arayüz Sınıfları (Interface Class)
Arayüzler uç noktaların sözleşmesidir. HTTP metodunu anotasyon belirler, yol parametrelerini @Path, sorgu parametrelerini @Query, gövdeyi @Body taşır. Uygulama kodu ile ağ katmanı arasındaki tek temas noktası burasıdır:
public interface ApiService { @GET("users/{userId}") Call<User> getUser(@Path("userId") int userId);}Bu metot users/{userId} adresine GET isteği atar ve gövdeyi User olarak çözer. Süslü parantez içindeki isim ile @Path içindeki isim aynı olmak zorunda. Uymazlarsa derleyici susar, hata çalışma zamanında patlar.
Sonuç
Toparlayalım. Retrofit’in yaptığı iş sihir değil: yazdığınız arayüzü OkHttp çağrılarına çeviriyor, gövdeyi bir dönüştürücüye veriyor, sonucu size tip güvenli biçimde geri getiriyor.
Günlük kullanımda üç şeyi hatırlamak yeterli. Retrofit nesnesi tekildir, her ekranda yenisini kurmayın. isSuccessful() yalnızca 2xx demektir, hata gövdesi ayrı okunur. Önbellek, zaman aşımı ve yeniden deneme gibi ağ davranışları Retrofit’te değil, altındaki OkHttp istemcisinde ayarlanır. Bu üçü yerine oturduğunda geri kalan iş, arayüze uç nokta eklemekten ibaret.
Bu yazımı beğendiyseniz diğer yazılarıma da göz atmanızı öneririm. Bu yazım aynı zamanda Medium blog’umda yayımlandı, buradan göz atabilirsiniz. 🚀
Referanslar
Retrofit, A type-safe HTTP client for Android and Java, Square

Mustafa Kürşad Başer
Kıdemli Yazılım Mühendisi
Karmaşık sorunlara zarif çözümler üretmekten keyif alan, tutkulu bir yazılım mühendisi. Kodlamanın ötesinde, teknoloji, sanat ve insan bilincinin kesişim noktalarını keşfetmekle derinden ilgileniyor.

