Retrofit ile Android Uygulamalarında Güçlü ve Esnek Ağ İstekleri
Android uygulamaları geliştirirken, uzak sunucularla iletişim kurmak ve API’lerden veri çekmek temel ihtiyaçlardan biridir. Bu süreç, ağ istekleri göndermeyi, alınan verileri işlemeyi ve kullanıcı arayüzünde göstermeyi içerir. Android ekosisteminde ağ işlemlerini kolaylaştıran birçok kütüphane bulunsa da, Retrofit bu alanda en popüler ve güçlü seçeneklerden biridir. Square tarafından geliştirilen Retrofit, RESTful API’lerle etkileşimi type-safe bir şekilde ve minimal kodla yapmanızı sağlar. Bu rehberde, Retroft’in temel kullanımını, proje kurulumundan veri çekmeye kadar adım adım inceleyeceğiz.
Retrofit Nedir ve Neden Kullanmalıyız?
Retrofit, Android ve Java uygulamaları için tasarlanmış, HTTP tabanlı API’leri type-safe bir şekilde kullanmamızı sağlayan bir REST istemci kütüphanesidir. Geliştiricilerin, API endpoint’lerini Java/Kotlin arayüzleri olarak tanımlamasına olanak tanır ve bu arayüzleri kullanarak otomatik olarak HTTP istekleri oluşturur.
Retrofit kullanmanın başlıca avantajları şunlardır:
* Type-Safety: İstek ve yanıt verileri için Java/Kotlin sınıfları tanımlarsınız, bu da derleme zamanı hatalarını azaltır ve kod okunabilirliğini artırır.
* Kolay Kullanım: Annotation’lar (@GET, @POST, @Body vb.) sayesinde API endpoint’lerini tanımlamak oldukça basittir.
* Esneklik: JSON (Gson, Moshi), XML (SimpleXML) ve diğer veri formatları için dönüştürücülerle (Converter) kolayca entegre olur.
* Asenkron İşlemler: İstekleri ana iş parçacığını (main thread) bloke etmeden asenkron olarak yapmayı destekler.
* Genişletilebilirlik: OkHttp ile entegre çalışır ve Interceptor’lar aracılığıyla istek/yanıt süreçlerine müdahale etme imkanı sunar (örneğin, loglama, kimlik doğrulama).
* Coroutines/RxJava Desteği: Modern Android geliştirme yaklaşımları olan Kotlin Coroutines ve RxJava ile sorunsuz entegrasyon için Call Adapter’lar sunar.
Bu rehberde, basit bir “Post” listesini JSONPlaceholder API’sinden çekip gösterecek bir Android uygulaması geliştireceğiz.
Proje Kurulumu ve Bağımlılıklar
Yeni bir Android Studio projesi oluşturarak başlayalım. Projeyi oluşturduktan sonra, Retrofit ve ilgili bağımlılıkları projenize eklemeniz gerekmektedir. app/build.gradle dosyasını açın ve dependencies bloğuna aşağıdaki satırları ekleyin:
dependencies {
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// JSON verilerini Java/Kotlin objelerine dönüştürmek için Gson Converter
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// (Opsiyonel) Ağ isteklerini loglamak için OkHttp Logging Interceptor
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
// Kotlin Coroutines desteği için (Eğer kullanacaksanız)
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
// Retrofit Call Adapter for Coroutines
// implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
// Bu rehberde basit Call kullanacağız, Coroutines entegrasyonu ayrı bir konu.
}
Bağımlılıkları ekledikten sonra, projenizi senkronize etmeyi unutmayın (Sync Project with Gradle Files).
Ayrıca, uygulamanızın internete erişebilmesi için AndroidManifest.xml dosyanıza aşağıdaki izni eklemeniz gerekmektedir:
Bu adımlarla projemiz Retrofit kullanmaya hazır hale geldi.
API Arayüzü ve Veri Modelleri
Retrofit ile çalışırken, API’den alacağınız verilerin yapısını temsil eden veri modelleri (POJO – Plain Old Java Object veya Kotlin data class) oluşturmanız ve API’nin endpoint’lerini tanımlayan bir arayüz yazmanız gerekir.
Veri Modelleri (Data Models)
JSONPlaceholder API’sinden /posts endpoint’ine bir istek yaptığımızda aşağıdaki gibi bir JSON dizisi döner:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
// ... diğer postlar
]
Bu yapıyı temsil eden bir Kotlin data class’ı oluşturalım. app/src/main/java/your_package_name/model/Post.kt dosyası oluşturun:
package com.example.myretrofitapp.model
import com.google.gson.annotations.SerializedName
data class Post(
@SerializedName("userId")
val userId: Int,
@SerializedName("id")
val id: Int,
@SerializedName("title")
val title: String,
@SerializedName("body")
val body: String
)
@SerializedName annotation’ı, JSON anahtarı ile Kotlin sınıfındaki değişken adı farklı olduğunda kullanılır. Bu örnekte aynı olsa da, iyi bir pratik olarak eklemek faydalıdır.
API Arayüzü (API Interface)
Şimdi API endpoint’imizi tanımlayacak bir Kotlin arayüzü oluşturalım. Bu arayüz, Retrofit’in HTTP metodlarını (GET, POST, PUT, DELETE) ve URL path’lerini anlamasını sağlar. app/src/main/java/your_package_name/api/ApiService.kt dosyası oluşturun:
package com.example.myretrofitapp.api
import com.example.myretrofitapp.model.Post
import retrofit2.Call
import retrofit2.http.GET
interface ApiService {
@GET("posts")
fun getPosts(): Call>
// Örnek olarak ID'ye göre tek bir post çekme:
// @GET("posts/{id}")
// fun getPostById(@Path("id") postId: Int): Call
// Örnek olarak query parametresi ile filtreleme:
// @GET("posts")
// fun getPostsByUser(@Query("userId") userId: Int): Call>
}
* @GET("posts"): Bu annotation, bu metodun bir HTTP GET isteği göndereceğini ve isteğin ana URL’ye ek olarak /posts path’ine yapılacağını belirtir.
* fun getPosts(): Call: Bu metodun, >
List tipinde bir yanıt beklediğini ve bu yanıtın Retrofit’in Call objesi içinde paketlendiğini gösterir. Call objesi, asenkron veya senkron olarak isteği yürütmek için kullanılır.
Retrofit İstemcisini Oluşturma ve İstek Yapma
API arayüzümüzü ve veri modellerimizi tanımladığımıza göre, artık bir Retrofit istemcisi oluşturabilir ve ağ istekleri gönderebiliriz.
Retrofit Instance Oluşturma
Retrofit istemcisini oluşturmak genellikle uygulamanın yaşam döngüsü boyunca bir kez yapılan bir işlemdir. Bu nedenle, bir Singleton pattern kullanmak veya bir DI kütüphanesi (Hilt, Koin) ile yönetmek iyi bir pratik olacaktır. Basitlik adına, bir object (Kotlin singleton) kullanarak Retrofit instance’ımızı oluşturalım. app/src/main/java/your_package_name/api/RetrofitClient.kt dosyası oluşturun:
package com.example.myretrofitapp.api
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object RetrofitClient {
private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
// Logging Interceptor oluşturma
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY // Tüm istek ve yanıt gövdelerini logla
}
// OkHttpClient oluşturma ve Interceptor'ı ekleme
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS) // Bağlantı zaman aşımı
.readTimeout(30, TimeUnit.SECONDS) // Okuma zaman aşımı
.writeTimeout(30, TimeUnit.SECONDS) // Yazma zaman aşımı
.build()
// Retrofit instance'ı
val instance: ApiService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // Gson ile JSON dönüştürme
.client(okHttpClient) // OkHttpClient'ı Retrofit'e ekleme
.build()
retrofit.create(ApiService::class.java)
}
}
* BASE_URL: Tüm API isteklerinin temel URL’si.
* HttpLoggingInterceptor: OkHttp’nin bir parçasıdır ve ağ isteklerinin ve yanıtlarının konsola (Logcat) yazdırılmasını sağlar. Hata ayıklama (debugging) için çok faydalıdır.
* OkHttpClient: Retrofit, ağ isteklerini yapmak için OkHttp’yi kullanır. OkHttpClient üzerinden Interceptor’lar ekleyebilir, zaman aşımlarını ayarlayabilir ve diğer ağ yapılandırmalarını yapabilirsiniz.
* Retrofit.Builder(): Bir Retrofit instance’ı oluşturmak için kullanılır.
* baseUrl(): Temel URL’yi ayarlar.
* addConverterFactory(): Retrofit’in JSON gibi formatlardaki API yanıtlarını veri modellerimize nasıl dönüştüreceğini belirtir. Burada GsonConverterFactory kullanıyoruz.
* client(): Özelleştirilmiş OkHttpClient‘ımızı Retrofit’e ekler.
* retrofit.create(ApiService::class.java): Oluşturduğumuz ApiService arayüzünün bir implementasyonunu Retrofit’ten alırız. Bu sayede getPosts() gibi metodları çağırabiliriz.
* by lazy: Bu, instance değişkeninin ilk erişildiğinde başlatılmasını sağlar, bu da kaynakların gereksiz yere tüketilmesini önler.
Ağ İsteği Yapma ve Verileri İşleme
Şimdi MainActivity veya başka bir Fragment içinde bu Retrofit istemcisini kullanarak API isteğimizi yapalım. Basit bir TextView‘de ilk postun başlığını göstereceğiz.
package com.example.myretrofitapp
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.myretrofitapp.api.RetrofitClient
import com.example.myretrofitapp.model.Post
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
private lateinit var textViewResult: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textViewResult = findViewById(R.id.textViewResult)
fetchPosts()
}
private fun fetchPosts() {
// RetrofitClient'tan ApiService instance'ımızı alıyoruz
RetrofitClient.instance.getPosts().enqueue(object : Callback> {
override fun onResponse(call: Call>, response: Response>) {
if (response.isSuccessful) {
val posts = response.body()
posts?.let {
// İlk postun başlığını TextView'e set et
if (it.isNotEmpty()) {
textViewResult.text = "İlk Post Başlığı:\n${it[0].title}"
// Tüm postları Logcat'e yazdır
it.forEach { post ->
Log.d("RetrofitExample", "Post ID: ${post.id}, Title: ${post.title}")
}
} else {
textViewResult.text = "Hiç post bulunamadı."
}
}
} else {
// API yanıtı başarılı değilse (örn. 404, 500)
textViewResult.text = "Hata: ${response.code()} - ${response.message()}"
Log.e("RetrofitExample", "API Hatası: ${response.code()} - ${response.message()}")
}
}
override fun onFailure(call: Call>, t: Throwable) {
// Ağ hatası veya istisna durumunda
textViewResult.text = "Ağ Hatası: ${t.message}"
Log.e("RetrofitExample", "Ağ Hatası:", t)
}
})
}
}
activity_main.xml dosyanızda sadece bir TextView yeterli olacaktır:
* enqueue(object : Callback: Bu metod, ağ isteğini asenkron olarak arka planda yürütür ve ana iş parçacığını bloke etmez. İşlem tamamlandığında veya hata oluştuğunda >)
Callback arayüzündeki metodlar çağrılır.
* onResponse(): Sunucudan bir yanıt alındığında çağrılır (başarılı HTTP kodu 2xx veya başarısız 4xx, 5xx olabilir).
* response.isSuccessful: HTTP yanıt kodunun 200-299 aralığında olup olmadığını kontrol eder.
* response.body(): Başarılı bir yanıtta, dönüştürülmüş veri modelini (bu durumda List) içerir.
* response.code() ve response.message(): Hata durumunda HTTP kodunu ve mesajını verir.
* onFailure(): Ağ bağlantısı sorunları (internet yok, sunucuya ulaşılamıyor) veya istek sırasında bir istisna oluştuğunda çağrılır. t.message ile hata detaylarına erişilebilir.
Bu kod parçası, API’den postları çeker ve ilk postun başlığını TextView‘de gösterirken, tüm postları da Logcat’e yazdırır.
Pratik İpuçları ve En İyi Uygulamalar
Retrofit ile çalışırken uygulamanızın daha sağlam, bakımı kolay ve performanslı olmasını sağlayacak bazı pratik ipuçları ve en iyi uygulamalar şunlardır:
Hata Yönetimi
* HTTP Hata Kodları: response.isSuccessful kontrolü önemlidir. Ancak sadece false olması yeterli değildir. Belirli hata kodlarına (örneğin 401 Unauthorized, 404 Not Found) özel durumlar için response.code() değerini kontrol edin.
* Hata Gövdeleri: API’ler genellikle hata durumlarında da JSON formatında bir hata mesajı döner. Bu hata mesajlarını yakalamak için response.errorBody()?.string() metodunu kullanabilir ve bunu kendi hata modelinize dönüştürebilirsiniz.
* Ağ Bağlantısı Kontrolü: İstek yapmadan önce cihazın internet bağlantısı olup olmadığını kontrol etmek, onFailure callback’ine düşmeden kullanıcıya daha iyi geri bildirim sunmanızı sağlar.
Retrofit Instance’ı Singleton Olarak Yönetme
RetrofitClient objesi ile zaten bir singleton pattern uygulamış olduk. Bu yaklaşım, her ağ isteği için yeni bir Retrofit veya OkHttpClient instance’ı oluşturmanın önüne geçer, böylece bellek ve performans açısından verimlilik sağlar.
Arka Plan İş Parçacıkları ve UI Güncellemesi
Retrofit’in enqueue() metodu asenkron çalıştığı için ağ isteği arka planda yapılır. Ancak onResponse ve onFailure callback’leri genellikle ana iş parçacığında (main thread) çağrılır (Android’de). Bu nedenle UI güncellemelerini doğrudan bu callback’ler içinde yapabilirsiniz.
Interceptors Kullanımı
HttpLoggingInterceptor dışında, Interceptor’lar Retrofit’in en güçlü özelliklerinden biridir. Şunlar için kullanılabilir:
* Kimlik Doğrulama (Authentication): Her isteğe otomatik olarak bir API anahtarı veya kimlik doğrulama token’ı eklemek.
* Özel Başlıklar (Headers): Tüm isteklere belirli HTTP başlıkları (örneğin User-Agent, Accept-Language) eklemek.
* Yeniden Denemeler (Retries): Başarısız olan istekleri otomatik olarak belirli bir sayıda yeniden denemek.
// Örnek bir kimlik doğrulama Interceptor'ı
class AuthInterceptor(private val authToken: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $authToken")
.build()
return chain.proceed(newRequest)
}
}
// OkHttpClient'a ekleme
// private val okHttpClient = OkHttpClient.Builder()
// .addInterceptor(loggingInterceptor)
// .addInterceptor(AuthInterceptor("your_auth_token"))
// .build()
Coroutines veya RxJava ile Entegrasyon
Modern Android geliştirme ortamında, asenkron işlemleri yönetmek için Kotlin Coroutines veya RxJava gibi reaktif programlama kütüphaneleri yaygın olarak kullanılır. Retrofit, bu kütüphanelerle entegre olmak için CallAdapterFactory‘ler sunar.
* Coroutines: suspend fonksiyonlar ve Flow ile Retrofit metotlarını tanımlayabilir, böylece daha okunaklı ve yönetilebilir asenkron kod yazabilirsiniz. Bunun için kotlinx-coroutines-core ve retrofit2-kotlin-coroutines-adapter bağımlılıklarını eklemeniz ve Retrofit builder’ına addCallAdapterFactory(CoroutineCallAdapterFactory()) eklemeniz gerekir.
* RxJava: Observable, Single veya Completable gibi RxJava tipleriyle API yanıtlarını işleyebilirsiniz. Bunun için retrofit2:adapter-rxjava2 bağımlılığını eklemeniz ve addCallAdapterFactory(RxJava2CallAdapterFactory.create()) kullanmanız gerekir.
Bu entegrasyonlar, özellikle karmaşık asenkron akışlar ve veri dönüşümleri söz konusu olduğunda kodunuzu büyük ölçüde basitleştirebilir.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Bu rehberde, Retrofit’in ne olduğunu, neden bu kadar popüler olduğunu, bir Android projesinde nasıl kurulacağını, API arayüzleri ve veri modelleriyle nasıl kullanılacağını ve ağ isteklerinin nasıl yapılacağını adım adım öğrendik. Ayrıca, Retrofit’i daha etkin kullanmak için pratik ipuçları ve en iyi uygulamaları da ele aldık. Retrofit, Android uygulamalarınızda ağ işlemlerini yönetmek için güçlü, esnek ve geliştirici dostu bir çözümdür.
Sıkça Sorulan Sorular (SSS)
Retrofit nedir?
Retrofit, Android ve Java uygulamaları için Square tarafından geliştirilmiş, RESTful API’lerle type-safe bir şekilde etkileşim kurmayı sağlayan bir HTTP istemci kütüphanesidir.
Neden Retrofit kullanmalıyım?
Retrofit, API entegrasyonunu basitleştirir, type-safety sunar, JSON gibi veri formatlarını kolayca dönüştürür, asenkron işlemleri destekler ve Interceptor’lar aracılığıyla istek/yanıt süreçlerine müdahale etme esnekliği sağlar. Ayrıca Coroutines ve RxJava gibi modern asenkron kütüphanelerle uyumludur.
Retrofit ile hangi HTTP metodlarını kullanabilirim?
Retrofit, tüm standart HTTP metodlarını destekler: @GET, @POST, @PUT, @DELETE, @PATCH, @HEAD, @OPTIONS.
Retrofit senkron ve asenkron istekleri nasıl yönetir?
Retrofit’in döndürdüğü Call objesi üzerinde enqueue() metodunu çağırarak asenkron (arka planda) istekler yapabilirsiniz. execute() metodunu çağırarak ise senkron (ana iş parçacığını bloke eden) istekler yapabilirsiniz, ancak Android’de bu genellikle tavsiye edilmez ve ana iş parçacığında yapılırsa NetworkOnMainThreadException hatasına yol açabilir.
Retrofit’te hata yönetimi nasıl yapılır?
Ağ hataları veya sunucu tarafındaki hatalar için enqueue() metodunun Callback arayüzündeki onFailure() ve onResponse() metodları kullanılır. onResponse() içinde response.isSuccessful kontrolü ile HTTP yanıt kodunun 2xx aralığında olup olmadığına bakılır. Başarısız yanıtlar için response.code() ve response.errorBody() incelenerek detaylı hata bilgisi alınabilir.
Retrofit JSON dışındaki formatları destekler mi?
Evet, Retrofit çeşitli Converter Factory’ler aracılığıyla JSON (Gson, Moshi), XML (SimpleXML), Protobuf ve diğer veri formatlarını destekler. İhtiyacınıza göre uygun Converter Factory’yi addConverterFactory() metodu ile Retrofit builder’ına eklemeniz yeterlidir.
Retrofit’i Coroutines veya RxJava ile nasıl entegre ederim?
Coroutines ile entegrasyon için retrofit2-kotlin-coroutines-adapter bağımlılığını ekleyip Retrofit.Builder‘a addCallAdapterFactory(CoroutineCallAdapterFactory()) ekleyerek API arayüzünüzdeki metodları suspend olarak tanımlayabilirsiniz. RxJava için ise retrofit2:adapter-rxjava2 bağımlılığını ekleyip addCallAdapterFactory(RxJava2CallAdapterFactory.create()) kullanarak API metodlarınızı Observable, Single vb. gibi RxJava tipleriyle döndürebilirsiniz.