Android Kotlin Projelerimizde Dependency Injection için Koin Kütüphanesinin Kullanımı

Mert Melih Aytemur
Appcent
Published in
4 min readApr 4, 2024

--

Bu yazıda, Android Kotlin projelerimizde dependency injection (bağımlılık enjeksiyonu) işlemlerini nasıl daha etkin ve düzenli bir şekilde yönetebileceğimizi ele alacağız.

Koin kütüphanesinin temel özelliklerini, bu kütüphaneyi projelerimize nasıl entegre edebileceğimizi ve Koin’in sunduğu avantajları detaylı bir şekilde inceleyeceğiz. Pratik örneklerle desteklenen içeriğimiz, Kotlin tabanlı Android uygulama geliştirme sürecinizi iyileştirmek için kapsamlı bir rehber sunacak.

Öncelikle Dependency Injection Nedir?

Dependency Injection (DI), nesne yönelimli programlamada kullanılan bir tasarım desenidir. Bu desen, bir sınıfın bağımlılıklarını (yani, işlevselliği için gerekli olan diğer sınıflar veya modüller) doğrudan oluşturmak yerine, dışarıdan sağlama yöntemidir. Dependency Injection, sınıflar arasındaki bağımlılıkları azaltmaya ve kodun test edilebilirliğini artırmaya yardımcı olur.

  • Bağımlılıkların Dışarıdan Sağlanması: Bir sınıfın ihtiyaç duyduğu nesneler, sınıf içinde doğrudan oluşturulmaz. Bunun yerine, bu nesneler sınıfa dışarıdan, örneğin bir yapılandırıcı metot (constructor) aracılığıyla enjekte edilir.
  • Modülerlik ve Yeniden Kullanılabilirlik: Sınıflar, belirli bir görevi yerine getirir ve bağımlılıkları dışarıdan sağlandığı için farklı bağlamlarda yeniden kullanılabilir hale gelir.
  • Test Edilebilirlik: Bağımlılıkların dışarıdan enjekte edilmesi, özellikle birim testleri sırasında, test edilmek istenen sınıfa alternatif (mock veya stub) nesnelerin sağlanmasını kolaylaştırır, böylece sınıfın işlevselliği izole bir şekilde test edilebilir.

Koin Kütüphanesi Android Projemize Nasıl Entegre Edilir?

Projenizi Koin ile kullanmaya başlamadan önce, build.gradle dosyanıza gerekli Koin bağımlılıklarını eklemeniz gerekmektedir. Bu, hem projenizin kök build.gradle dosyasında hem de modülünüzün build.gradle dosyasında yapılacak değişiklikleri içerir.

Kök build.gradle dosyasında, Koin sürümünüzü bir değişken olarak ekleyebilirsiniz:

ext {
koin_version = 'x.x.x' // Koin'in güncel sürümünü kullanın
}

Modülünüzün build.gradle dosyasında ise, Koin bağımlılıklarını ekleyin:

dependencies {
implementation "io.insert-koin:koin-android:$koin_version"
// Diğer gerekli Koin modülleri, örneğin:
// implementation "io.insert-koin:koin-androidx-viewmodel:$koin_version"
}

Koin’i Başlatalım:

Koin’i başlatmak için, Android uygulamanızın Application sınıfında Koin modüllerinizi yükleyerek startKoin fonksiyonunu çağırmanız gerekmektedir. Eğer bir Application sınıfınız yoksa, oluşturmanız ve Android manifest dosyanızda tanımlamanız gerekecektir.

startKoin fonksiyonu: Koin'in başlatılmasını sağlar. Bu işlem genellikle uygulamanın Application sınıfının onCreate metodunda yapılır. androidLogger() fonksiyonu, Koin'in loglamasını aktif hale getirir, androidContext(this@MyApplication) ile uygulamanın context'i Koin'e aktarılır, ve modules(appModule) ile Koin'e hangi modüllerin kullanılacağı belirtilir.

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger() // Loglamayı aktifleştirir (isteğe bağlı)
androidContext(this@MyApplication)
modules(appModule) // Koin modüllerinizi burada belirtin
}
}
}

Şimdi sizlerle örnek bir servis isteği senaryosu üzerinden bağımlılıklarımızı nasıl yönetebileceğimizi göstereceğim.

Uygulamamızda servisten güncel haberleri çekeceğimiz varsayalım. Öncelikle Servis arayüzümüzü oluşturalım.

Retrofit ile HTTP isteklerini tanımlayan bir arayüzdür.

Retrofit: HTTP isteklerini tanımlamak için kullanılan bir kütüphane. @GET("my/endpoint") annotation'ı, belirli bir HTTP endpoint'e GET isteği yapılacağını belirtir ve bu metod asenkron olarak haberleri çeker.

interface NewsApi {
@GET("my/endpoint")
suspend fun fetchNews()
}

Sonrasında (Clean Architecture ve Mvvm kullandığımızı varsayarak) Ağ çağrıları için Repository arayüzümüzü oluşturalım.

fetchNewsCall: Haberleri çekmek için kullanılan metoddur. Repository deseni, veri erişim katmanını soyutlayarak, veri kaynağının değişmesi durumunda uygulamanın diğer bölümlerinin etkilenmemesini sağlar.

interface NewsRepository {
suspend fun fetchNewsCall()
}

Ve bu repository’i implement edeceğimiz RepositoryImpl sınıfımızı oluşturalım. NewsRepositoryImpl, NewsApi üzerinden ağ çağrılarını gerçekleştiren sınıftır.

Bu sınıf, NewsRepository arayüzünü implement eder ve NewsApi üzerinden ağ çağrılarını gerçekleştirir.

class NewsRepositoryImpl(
private val api: NewsApi
): MainRepository {

override suspend fun fetchNewsCall() {
api.callApi()
}
}

Şimdi ise View modelimizi oluşturalım ve ağ çağrısına hazır hale getirelim

class NewsViewModel(
private val repository: NewsRepository
): ViewModel() {

fun fetchNewsCall() {
viewModelScope.launch{
repository.fetchNewsCall()
}
}
}

Kullanıcı Arayüzüne geçmeden önce, koin app modülümüzü oluşturalım.

appModule: Retrofit ile HTTP istekleri için gerekli yapılandırmaları içerir. NewsApi ve Repository'nin nasıl oluşturulacağını tanımlar.

  • single: Bir sınıfın tek bir instance'ını (nesnesini) oluşturmak ve bunu uygulama boyunca kullanmak için kullanılır. Bu, yalnızca bir kez oluşturulan ve ihtiyaç duyulduğunda tekrar kullanılan singleton nesneler tanımlamak için kullanılır.
  • viewModel: Android ViewModel nesnelerini yönetmek için kullanılır. viewModel bloğu içinde tanımlanan bir ViewModel, Lifecycle bilincine sahip olacak şekilde oluşturulur ve uygulamanın UI ile ilgili verileri yönetmek ve saklamak için kullanılır.
// appModule, uygulama genelinde kullanılacak bağımlılıkları tanımlar.
val appModule = module {
single {
Retrofit.Builder()
.baseUrl("https://google.com")
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(NewsApi::class.java) // Retrofit ile NewsApi'nin bir örneği oluşturulur.
}
single<MainRepository> {
MainRepositoryImpl(get()) // NewsRepositoryImpl, NewsApi bağımlılığı ile oluşturulur.
}
viewModel {
MainViewModel(get()) // NewsViewModel, NewsRepository bağımlılığı ile oluşturulur.
}
}

Son olarak activity için bağımlılık modülümüzü oluşturalım

  • activityModule: Activity özelinde bağımlılıkları tanımlar. Örneğin, named("hello") ve named("bye") ile isimlendirilmiş bağımlılıklar tanımlanabilir.
// activityModule, MainActivity ile ilgili bağımlılıkları tanımlar.
val activityModule = module {
scope<MainActivity> {
scoped(qualifier = named("hello")) { "Hello" }
scoped(qualifier = named("bye")) { "Bye" }
}
}

ve activity üzerinde kullanımı

// MainActivity, uygulamanın ana aktivitesidir ve bağımlılıkların kullanıldığı yerdir.
class MainActivity : AppCompatActivity() {
private val viewModel by viewModel<NewsViewModel>() // Koin ile MainViewModel örneği alınır.

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Layout belirlenir.

val hello = inject<String>(named("bye")).value // "bye" adlı String bağımlılığını enjekte eder.
println(hello)

// ViewModel kullanılarak ağ çağrısı başlatılır.
viewModel.fetchNewsCall()
}
}

Koin ile Android’de bağımlılık enjeksiyonunun nasıl etkili bir şekilde uygulanabileceğini ele aldığımız bu rehber, uygulamanızın yapılandırmasını ve yönetimini basitleştirmeye yönelik adımları göstermektedir. Umarım, bu makale sizlere Koin’in gücünü keşfetme ve Android projelerinizde daha temiz, modüler ve test edilebilir bir kod tabanı oluşturma konusunda ilham vermiştir.

İyi Çalışmalar Dilerim.

--

--