Uygulama mimarisi kılavuzu

Uygulama mimarisi, yüksek kaliteli bir Android uygulamasının temelidir. İyi tanımlanmış bir mimari, telefonlar, tabletler, katlanabilir cihazlar, ChromeOS cihazlar, araba ekranları ve XR dahil olmak üzere Android cihazların sürekli genişleyen ekosistemine uyum sağlayabilen, ölçeklenebilir ve bakımı kolay bir uygulama oluşturmanıza olanak tanır.

Uygulama kompozisyonu

Tipik bir Android uygulaması, hizmetler, içerik sağlayıcılar ve yayın alıcılar gibi birden fazla uygulama bileşeninden oluşur. Bu bileşenleri uygulama manifestinizde beyan edersiniz.

Uygulamanın kullanıcı arayüzü de bir bileşendir. Geçmişte kullanıcı arayüzleri birden fazla etkinlik kullanılarak oluşturuluyordu. Ancak modern uygulamalar tek etkinlikli bir mimari kullanır. Tek bir Activity, ekranlar veya Jetpack Compose hedefleri için kapsayıcı görevi görür.

Birden fazla form faktörü

Uygulamalar yalnızca telefonlarda değil, tabletlerde, katlanabilir cihazlarda ve ChromeOS cihazlarda da çalışabilir. Uygulamanızın her zaman dikey veya yatay yönde sabit kalacağını varsaymayın. Cihazı döndürme veya katlanabilir bir cihazı katlama ve açma gibi yapılandırma değişiklikleri, uygulamanızın kullanıcı arayüzünü yeniden oluşturmasına neden olur. Bu durum, uygulama durumunu etkiler.

Kaynak kısıtlamaları

Mobil cihazlar (büyük ekranlı cihazlar dahil) kaynak açısından sınırlıdır. Bu nedenle, işletim sistemi kaynaklarını diğer işlemlere vermek için uygulamanızın işlemini herhangi bir zamanda durdurabilir.

Değişken başlatma koşulları

Kaynakların sınırlı olduğu bir ortamda, uygulamanızın bileşenleri tek tek ve sırasız olarak başlatılabilir. Ayrıca, işletim sistemi veya kullanıcı bunları istediği zaman yok edebilir. Bu nedenle, uygulama bileşenlerinizde uygulama verilerini veya durumunu depolamayın. Uygulama bileşenlerinizi bağımsız ve birbirinden ayrı hale getirin.

Sık karşılaşılan mimari ilkeler

Uygulama verilerini ve durumunu depolamak için uygulama bileşenlerini kullanamıyorsanız uygulamanızı nasıl tasarlamalısınız?

Android uygulamalarının boyutu büyüdükçe uygulamanın ölçeklenmesine olanak tanıyan bir mimari tanımlamak önemlidir. İyi tasarlanmış bir uygulama mimarisi, uygulamanın bölümleri arasındaki sınırları ve her bölümün sorumluluklarını tanımlar.

İlgi alanlarının ayrılması

Uygulama mimarinizi belirli birkaç ilkeye uyacak şekilde tasarlayın.

En önemli ilke ilgi alanlarının ayrılmasıdır: Uygulamanızı, sorumlulukları ve sınırları net bir şekilde tanımlanmış yöntemlere, sınıflara, dosyalara, paketlere, modüllere ve katmanlara ayırma.

Kodunuzun tamamını Activity içine yazmak yaygın bir hatadır.

Activity'nın birincil rolü, uygulamanızın kullanıcı arayüzüne ev sahipliği yapmaktır. Android işletim sistemi, bu etkinliklerin yaşam döngüsünü kontrol eder ve ekran döndürme gibi kullanıcı işlemlerine veya düşük bellek gibi sistem etkinliklerine yanıt olarak bunları sık sık yok edip yeniden oluşturur.

Bu geçici yapı, bunları uygulama verilerini veya durumunu tutmak için uygunsuz hale getirir. Verileri bir Activity içinde saklarsanız bileşen yeniden oluşturulduğunda bu veriler kaybolur. Verilerin kalıcı olmasını sağlamak ve kararlı bir kullanıcı deneyimi sunmak için durumu bu kullanıcı arayüzü bileşenlerine emanet etmeyin.

Uyarlanabilir düzenler

Cihaz yönündeki değişiklikler veya uygulama penceresinin boyutundaki değişiklikler gibi yapılandırma değişikliklerini sorunsuz bir şekilde işleyen uygulamalar oluşturun. Çeşitli form faktörlerinde optimum kullanıcı deneyimi sunmak için uyarlanabilir standart düzenleri uygulayın.

Veri modellerinden Drive kullanıcı arayüzü

Bir diğer önemli ilke de kullanıcı arayüzünüzü veri modellerinden, tercihen kalıcı modellerden yönlendirmektir. Veri modelleri, bir uygulamanın verilerini temsil eder. Bunlar, uygulamanızdaki kullanıcı arayüzü öğelerinden ve diğer bileşenlerden bağımsızdır. Bu nedenle, kullanıcı arayüzü ve uygulama bileşeni yaşam döngüsüne bağlı değildirler ancak işletim sistemi, uygulamanın sürecini bellekten kaldırdığında yine de yok edilirler.

Kalıcı modeller şu nedenlerle idealdir:

  • Android OS, kaynakları boşaltmak için uygulamanızı yok ederse kullanıcılar veri kaybetmez.

  • Uygulamanız, ağ bağlantısının kesintili olduğu veya kullanılamadığı durumlarda çalışmaya devam eder.

Uygulamanızı sağlam ve test edilebilir hale getirmek için uygulama mimarinizi veri modeli sınıflarına dayandırın.

Tek doğruluk kaynağı

Uygulamanızda yeni bir veri türü tanımlandığında bu türe tek bir doğru kaynak (SSOT) atayın. SSOT, bu verilerin sahibidir ve yalnızca SSOT bu verileri değiştirebilir veya dönüştürebilir. SSOT, bunu sağlamak için verileri değişmez bir tür kullanarak kullanıma sunar. Verileri değiştirmek için SSOT, diğer türlerin çağırabileceği işlevleri kullanıma sunar veya etkinlikleri alır.

Bu kalıbın birden fazla avantajı vardır:

  • Belirli bir veri türüyle ilgili tüm değişiklikleri tek bir yerde toplar.
  • Verileri, diğer türlerin kurcalayamayacağı şekilde korur.
  • Verilerde yapılan değişiklikler daha kolay izlenebilir. Böylece hatalar daha kolay tespit edilebilir.

Çevrimdışı öncelikli uygulamalarda, uygulama verilerinin doğruluk kaynağı genellikle bir veritabanıdır. Diğer bazı durumlarda ise doğruluk kaynağı ViewModel olabilir.

Tek yönlü veri akışı

Tek ve doğru kaynak ilkesi genellikle tek yönlü veri akışı (UDF) modeliyle birlikte kullanılır. UDF'de durum yalnızca tek bir yönde, genellikle üst bileşenden alt bileşene doğru akar. Veri akışını ters yönde değiştiren etkinlikler.

Android'de durum veya veriler genellikle hiyerarşinin daha yüksek kapsamlı türlerinden daha düşük kapsamlı türlerine akar. Etkinlikler genellikle daha düşük kapsamlı türlerden tetiklenerek ilgili veri türü için SSOT'a ulaşır. Örneğin, uygulama verileri genellikle veri kaynaklarından kullanıcı arayüzüne akar. Düğmeye basma gibi kullanıcı etkinlikleri, kullanıcı arayüzünden uygulama verilerinin değiştirildiği ve sabit bir türde kullanıma sunulduğu SSOT'ye aktarılır.

Bu model, veri tutarlılığını daha iyi korur, hata yapma olasılığı daha düşüktür, hata ayıklaması daha kolaydır ve SSOT modelinin tüm avantajlarını sunar.

UDF hakkında daha fazla bilgi için Jetpack Compose'da tek yönlü veri akışı başlıklı makaleyi inceleyin.

Yaygın mimari ilkeleri göz önünde bulundurarak her uygulamayı en az iki katmanla tasarlayın:

  • Kullanıcı arayüzü katmanı: Uygulama verilerini ekranda gösterir.
  • Veri katmanı: Uygulamanızın iş mantığını içerir ve uygulama verilerini kullanıma sunar.

Kullanıcı arayüzü ile veri katmanları arasındaki etkileşimleri basitleştirmek ve yeniden kullanmak için alan katmanı adlı ek bir katman ekleyebilirsiniz.

Tipik bir uygulama mimarisinde, kullanıcı arayüzü katmanı uygulama verilerini veri katmanından veya kullanıcı arayüzü katmanı ile veri katmanı arasında yer alan isteğe bağlı alan katmanından alır.
Şekil 1. Tipik bir uygulama mimarisinin şeması.

Modern uygulama mimarisi

Modern bir Android uygulama mimarisinde aşağıdaki teknikler (diğerlerinin yanı sıra) kullanılır:

  • Uyarlanabilir ve katmanlı mimari
  • Uygulamanın tüm katmanlarında tek yönlü veri akışı (UDF)
  • Kullanıcı arayüzünün karmaşıklığını yönetmek için durum tutucular içeren kullanıcı arayüzü katmanı
  • Eş yordamlar ve akışlar
  • Bağımlılık ekleme ile ilgili en iyi uygulamalar

Daha fazla bilgi için Android mimarisiyle ilgili öneriler başlıklı makaleyi inceleyin.

Kullanıcı arayüzü katmanı

Kullanıcı arayüzü katmanının (veya sunum katmanı) görevi, uygulama verilerini ekranda göstermektir. Veriler, kullanıcı etkileşimi (ör. düğmeye basma) veya harici giriş (ör. ağ yanıtı) nedeniyle her değiştiğinde kullanıcı arayüzü, değişiklikleri yansıtacak şekilde güncellenir.

Kullanıcı arayüzü katmanı iki tür yapıdan oluşur:

  • Ekrandaki verileri işleyen kullanıcı arayüzü öğeleri. Bu öğeleri, uyarlanabilir düzenleri desteklemek için Jetpack Compose işlevlerini kullanarak oluşturursunuz.
  • Verileri tutan, kullanıcı arayüzünde gösteren ve mantığı işleyen durum sahipleri (ör. ViewModel). Durum koruyucular, durum sağladıkları kullanıcı arayüzü öğesiyle aynı süre boyunca yaşamalıdır. Örneğin, bir ekranın ViewModel'i, ekran uygulamanın gezinme eski yığınından kaldırılana kadar bellekte tutulmalıdır. Daha fazla bilgi için Durum Ömürleri başlıklı makaleyi inceleyin.
Tipik bir mimaride, kullanıcı arayüzü katmanının kullanıcı arayüzü öğeleri durum tutuculara, durum tutucular ise veri katmanındaki veya isteğe bağlı alan katmanındaki sınıflara bağlıdır.
Şekil 2. Uygulama mimarisinde kullanıcı arayüzü katmanının rolü.

Uyarlanabilir kullanıcı arayüzlerinde, ViewModel nesneleri gibi durum tutucular, farklı pencere boyutu sınıflarına uyum sağlayan kullanıcı arayüzü durumunu gösterir. Bu kullanıcı arayüzü durumunu elde etmek için currentWindowAdaptiveInfo() kullanabilirsiniz. NavigationSuiteScaffold gibi bileşenler, bu bilgileri kullanarak mevcut ekran alanına göre farklı gezinme kalıpları (ör. NavigationBar, NavigationRail veya NavigationDrawer) arasında otomatik olarak geçiş yapabilir.

Daha fazla bilgi edinmek için UI layer (Kullanıcı arayüzü katmanı) ve Compose UI Architecture (Compose kullanıcı arayüzü mimarisi) başlıklı makaleleri inceleyin.

Uyarlanabilir uygulamalar ve gezinme hakkında daha fazla bilgi için Uyarlanabilir uygulamalar oluşturma ve Uyarlanabilir gezinme oluşturma başlıklı makaleleri inceleyin.

Veri katmanı

Bir uygulamanın veri katmanı iş mantığını içerir. İş mantığı, uygulamanıza değer katan unsurdur. Uygulamanızın verileri nasıl oluşturduğunu, depoladığını ve değiştirdiğini belirleyen kurallardan oluşur.

Veri katmanı, her biri sıfır ila çok sayıda veri kaynağı içerebilen depolardan oluşur. Uygulamanızda işlediğiniz her farklı veri türü için bir depo sınıfı oluşturun. Örneğin, filmlerle ilgili veriler için bir MoviesRepository sınıfı veya ödemelerle ilgili veriler için bir PaymentsRepository sınıfı oluşturabilirsiniz.

Tipik bir mimaride, veri katmanının depoları uygulamanın geri kalanına veri sağlar ve veri kaynaklarına bağlıdır.
Şekil 3. Veri katmanının uygulama mimarisindeki rolü.

Depo sınıfları aşağıdakilerden sorumludur:

  • Verileri uygulamanın geri kalanına gösterme
  • Verilerde yapılan değişiklikleri merkezileştirme
  • Birden fazla veri kaynağı arasındaki çakışmaları çözme
  • Veri kaynaklarını uygulamanın geri kalanından ayırma
  • İş mantığı içerme

Her veri kaynağı sınıfı, yalnızca bir veri kaynağıyla (dosya, ağ kaynağı veya yerel veritabanı olabilir) çalışmaktan sorumludur. Veri kaynağı sınıfları, veri işlemleri için uygulama ile sistem arasındaki köprüdür.

Daha fazla bilgi edinmek için veri katmanı sayfasını inceleyin.

Alan katmanı

Alan katmanı, kullanıcı arayüzü ve veri katmanları arasında isteğe bağlı bir katmandır.

Alan katmanı, karmaşık işletme mantığını veya birden fazla görünüm modeli tarafından yeniden kullanılan daha basit işletme mantığını kapsüllemekten sorumludur. Tüm uygulamalar bu şartları karşılamadığından alan katmanı isteğe bağlıdır. Yalnızca gerektiğinde kullanın. Örneğin, karmaşıklığı gidermek veya yeniden kullanılabilirliği tercih etmek için kullanabilirsiniz.

İsteğe bağlı alan katmanı, dahil edildiğinde kullanıcı arayüzü katmanına bağımlılıklar sağlar ve veri katmanına bağlıdır.
Şekil 4. Alan katmanının uygulama mimarisindeki rolü.

Alan katmanındaki sınıflara genellikle kullanım alanları veya etkileşimciler adı verilir. Her kullanım alanı tek bir işlevden sorumludur. Örneğin, birden fazla görünüm modeli ekranda doğru mesajı göstermek için saat dilimlerini kullanıyorsa uygulamanızda GetTimeZoneUseCase sınıfı olabilir.

Daha fazla bilgi edinmek için alan katmanı sayfasına bakın.

Bileşenler arasındaki bağımlılıkları yönetme

Uygulamanızdaki sınıfların düzgün çalışması için diğer sınıflara bağlı olması. Belirli bir sınıfın bağımlılıklarını toplamak için aşağıdaki tasarım kalıplarından birini kullanabilirsiniz:

  • Bağımlılık ekleme (DI): Bağımlılık ekleme, sınıfların bağımlılıklarını oluşturmadan tanımlamasına olanak tanır. Çalışma zamanında, bu bağımlılıkları sağlamaktan başka bir sınıf sorumludur.
  • Hizmet bulucu: Hizmet bulucu kalıbı, sınıfların bağımlılıklarını oluşturmak yerine elde edebileceği bir kayıt defteri sağlar.

Bu kalıplar, kodu kopyalamadan veya karmaşıklık eklemeden bağımlılıkları yönetmek için net kalıplar sağladığından kodunuzu ölçeklendirmenize olanak tanır. Bu kalıplar, test ve üretim uygulamaları arasında hızlıca geçiş yapmanızı da sağlar.

Genel en iyi uygulamalar

Programlama, yaratıcılık gerektiren bir alandır ve Android uygulamaları geliştirmek de bu kapsamdadır. Bir sorunu çözmenin birçok yolu vardır. Örneğin, verileri birden fazla etkinlik veya parça arasında iletebilir, uzaktan verileri alıp çevrimdışı modu için yerel olarak kalıcı hale getirebilir ya da önemsiz olmayan uygulamaların karşılaştığı diğer yaygın senaryoları işleyebilirsiniz.

Aşağıdaki öneriler zorunlu olmasa da çoğu durumda bu önerilere uyarak kod tabanınızı daha sağlam, test edilebilir ve bakımı kolay hale getirebilirsiniz.

Verileri uygulama bileşenlerinde depolamayın.

Uygulamanızın giriş noktalarını (ör. etkinlikler, hizmetler ve yayın alıcılar) veri kaynağı olarak belirlemekten kaçının. Giriş noktalarının yalnızca söz konusu giriş noktasıyla alakalı veri alt kümesini alacak şekilde diğer bileşenlerle koordineli çalışmasını sağlayın. Her uygulama bileşeni, kullanıcının cihazıyla etkileşimine ve sistemin kapasitesine bağlı olarak kısa ömürlüdür.

Android sınıflarına olan bağımlılıkları azaltın.

Uygulama bileşenlerinizi, Context veya Toast gibi Android çerçeve SDK'sı API'lerini kullanan tek sınıflar haline getirin. Uygulamanızdaki diğer sınıfları uygulama bileşenlerinden ayırmak, test edilebilirliğe yardımcı olur ve uygulamanızdaki bağlantıyı azaltır.

Uygulamanızdaki modüller arasında net sorumluluk sınırları tanımlayın.

Ağdan veri yükleyen kodu, kod tabanınızdaki birden fazla sınıf veya pakete yaymayın. Benzer şekilde, aynı sınıfta veri önbelleğe alma ve veri bağlama gibi birbiriyle alakasız birden fazla sorumluluk tanımlamayın. Önerilen uygulama mimarisini kullanın.

Her modülden mümkün olduğunca az şey gösterin.

Dahili uygulama ayrıntılarını ortaya çıkaran kısayollar oluşturmayın. Kısa vadede biraz zaman kazanabilirsiniz ancak kod tabanınız geliştikçe teknik borçlanma olasılığınız artar.

Uygulamanızın benzersiz özüne odaklanarak diğer uygulamalardan öne çıkmasını sağlayın.

Aynı ortak metin kodunu tekrar tekrar yazarak zaman kaybetmeyin. Bunun yerine, zamanınızı ve enerjinizi uygulamanızı benzersiz kılan özelliklere odaklanarak harcayın. Tekrarlanan standart kodları Jetpack kitaplıkları ve diğer önerilen kitaplıklar işlesin.

Standart düzenler ve uygulama tasarım kalıpları kullanın.

Jetpack Compose kitaplıkları, uyarlanabilir kullanıcı arayüzleri oluşturmak için güçlü API'ler sağlar. Uygulamanızda standart düzenleri kullanarak birden fazla form faktöründe ve ekran boyutunda kullanıcı deneyimini optimize edin. Kullanım alanlarınıza en uygun düzenleri seçmek için uygulama tasarım kalıpları galerisini inceleyin.

Yapılandırma değişikliklerinde kullanıcı arayüzü durumunu koruma

Uyarlanabilir düzenler için tasarım yaparken ekranı yeniden boyutlandırma, katlama ve yönlendirme değişiklikleri gibi yapılandırma değişikliklerinde kullanıcı arayüzü durumunu koruyun. Mimariniz, kullanıcının mevcut durumunun korunduğunu doğrulayarak sorunsuz bir deneyim sağlamalıdır.

Yeniden kullanılabilir ve birleştirilebilir kullanıcı arayüzü bileşenleri tasarlayın.

Uyarlanabilir tasarımı desteklemek için yeniden kullanılabilir ve birleştirilebilir kullanıcı arayüzü bileşenleri oluşturun. Bu sayede, önemli bir yeniden düzenleme yapmadan çeşitli ekran boyutlarına ve duruşlarına uyacak şekilde bileşenleri birleştirip yeniden düzenleyebilirsiniz.

Uygulamanızın her bölümünü ayrı ayrı nasıl test edebileceğinizi düşünün.

Ağdan veri getirmek için iyi tanımlanmış bir API, bu verileri yerel bir veritabanında kalıcı hale getiren modülün test edilmesini kolaylaştırır. Bunun yerine bu iki işlevin mantığını tek bir yerde karıştırırsanız veya ağ kodunuzu tüm kod tabanınıza dağıtırsanız test etmek imkansız olmasa da çok daha zor hale gelir.

Türler, eşzamanlılık politikalarından sorumludur.

Bir tür uzun süreli engelleme işi yapıyorsa bu hesaplamayı doğru iş parçacığına taşımaktan sorumlu olmalıdır. Tür, yaptığı hesaplamanın türünü ve hesaplamanın hangi iş parçacığında çalıştırılacağını bilir. Türler, ana ileti dizisini engellemeden ana ileti dizisinden çağrılabilen türler olmalıdır.

Mümkün olduğunca çok sayıda alakalı ve yeni veriyi kalıcı hale getirin.

Bu sayede kullanıcılar, cihazları çevrimdışı moddayken bile uygulamanızın işlevlerinden yararlanabilir. Kullanıcılarınızın tamamının sürekli ve yüksek hızlı bağlantıdan yararlanmadığını, yararlansalar bile kalabalık yerlerde kötü sinyal alabileceklerini unutmayın.

Mimari yapının avantajları

Uygulamanızda iyi bir mimari kullanmak projeye ve mühendislik ekiplerine birçok avantaj sağlar:

  • Uygulamanın genel olarak sürdürülebilirliğini, kalitesini ve sağlamlığını artırır.
  • Uygulamanın ölçeklenmesine olanak tanır. Daha fazla kişi ve daha fazla ekip, aynı kod tabanına minimum kod çakışmasıyla katkıda bulunabilir.
  • İlk katılım sürecine yardımcı olur. Mimari, projenize tutarlılık kazandırdığı için ekibin yeni üyeleri hızlı bir şekilde gerekli yetkinlik seviyesine ulaşabilir ve daha kısa sürede daha verimli olabilir.
  • Test etmek daha kolaydır. İyi bir mimari, genellikle test edilmesi daha kolay olan daha basit türleri teşvik eder.
  • İyi tanımlanmış süreçlerle hataları metodik bir şekilde incelemenizi sağlar.

İyi bir mimari için önceden zaman yatırımı yapılması gerekse de bu, kullanıcıları doğrudan etkiler. Daha üretken bir mühendislik ekibi sayesinde daha kararlı bir uygulamadan ve daha fazla özellikten yararlanırlar.

Örnekler

Aşağıdaki örneklerde iyi bir uygulama mimarisi gösterilmektedir: