React Native JSI Nedir? Yeni mimari neler getiriyor?
Önceki yazımda React Native Bridge’den bahsetmiştim. Şimdi ise React Native Bridge’te ortaya çıkan problemlere ve bu problemlerin gelecekte yeni mimari ile nasıl çözüleceğine değinelim.
React Native Bridge’in dezavantajları
React Native Bridge’in JS ve Native arasında haberleşmeyi sağladığından bahsetmiştim. Bu yapıdaki dezavantajlara başlıklar halinde teker teker değinelim:
Asenkron haberleşmeden dolayı mesajların gönderiminin garanti edilememesi
RN Bridge, native ve JS tarafı arasında tam asenkron yapıda çalışmaktadır. İki taraf da birbirinden habersiz olduğu için gönderilen mesajların sorumluluğu ortadaki tek bir bridge’de tutulmaktadır. Eğer bridge’de bir problem oluşursa mesajın gönderimi gerçekleşmeyebilir.
Bunun için şu analojiyi kullanabiliriz: Diyelim ki bir kişiye davetiye göndereceksiniz ve iletişim için sadece yerel posta firmasını kullanıyorsunuz. Postacıya davetiyeyi verdiğinizde, karşıdaki kişinin o postayı alıp almadığını ancak o kişi size posta gönderdiğinde fark edebilirsiniz. Ayrıca o gün posta firması yoğunluktan dolayı gönderimi çok geç yapabilir veya posta gönderim esnasında kaybolabilir. Bridge’de de böyle bir mesaj gönderiminin garanti edilememesi problemi bulunmaktadır.
Tüm native modüllerin kullanılmadığı halde uygulama açıldığında yüklenmesi
Uygulamanızda kamera, bluetooth, GPS gibi native modüller var ve sadece bazı sayfalar bu modülleri kullanıyor olduğunu varsayalım. Native ve JS tarafı birbirinden habersiz şekilde çalıştığı için bu modüllerin uygulama açıldığında yüklenerek kullanıma hazır olması gerekiyor. Bu durum da uygulamanın açılış süresini uzatan bir etken oluyor. Bir uygulamada ne kadar çok native modül varsa o kadar yavaş açılıyor. Bu konuda LazyReactPackage gibi çözümler var ama gradle desteği yok ve tam anlamıyla sağlıklı bir çözüm sunmuyor.
Ayrıca native modüller birçok farklı paket içerisinde yer alıyorlar. Uygulamanın açılması sırasında modül listesinin alınması için, proje içerisinde birkaç farklı yerde, natie modül listesi üzerinde for döngüleri ile gezinme işlemi yapmak gerekiyor. Runtime’da bu işlemin gerçekleşmesi de işlemci gücünün gereksiz bir şekilde harcanmasına neden oluyor.
Codepush güncellemelerinde modülün bulunmaması problemi oluşabiliyor
Native ve JS tarafı birbiri ile JSON mesajları üzerinden anlaştıkları ve direkt olarak birbirinden habersiz oldukları için, JavaScript’in call ettiği native module’ün gerçekten de uygulama içerisinde olup olmadığı muamma olabiliyor. Özellikle CodePush gibi over-the-air güncellemelerinde bu sorun ortaya çıkabiliyor.
Bridge’in, React Native uygulamasının yaşam döngüsüne sıkı bir şekilde bağlı olması
Bridge, RN uygulamasının yaşam döngüsüne (lifecycle) sıkı bir şekilde bağlı olduğu için uygulamanın kapanması ve açılmasında bridge de etkileniyor. Ayrıca native modüller singleton oldukları ve bridge’e bağlı oldukları için, bridge’in birçok defa kapanıp açıldığı eski (brownfield) RN uygulamalarında sorun oluşabiliyor.
Reflection kullanılması
Native Module’de çağrılacak fonksiyonlar ve parametreler, çalışma zamanında reflection ile bulunarak oluşturuluyor. Bu işlem derleme esnasında daha efektif bir şekilde yapılabilir.
Android’te JSC’nin uygulama apk’sına dahil edilmesi gerekiyor
JSC (JavaScript Core)’un Safari tarayıcılarında kullanıldığından bahsetmiştik. iOS’te Safari tarayıcısı ile birlikte JSC tümleşik olarak geliyor. Fakat Android tarafında bu durum geçerli değil. Bu nedenle yayınlanan uygulamaya JavaScript Core da eklenmiş oluyor. Bu da yaklaşık 3–4 MB’lık bir artış demek.
Ayrıca JSC sürekli iyileştirmeler ve güncellemeler alıyor. Android için JSC 2016'dan beri her yeni versiyon çıktığında eskiyi uydurmak için topluluk desteğine ihtiyaç oluyor.
Arayüz güncellemelerinde tam performans yakalanamaması
Arayüz güncellemelerinde de bridge kullanıldığı için, bridge’in asenkron olmasından dolayı Shadow tree üzerinde doğrudan bir kontrol sağlanmıyor ve bu nadiren arayüz problemlerine neden olabiliyor. Örneğin çok yavaş bir telefonda, bir FlatList üzerinde çok hızlı bir şekilde scroll yaptığınızda birkaç ms de olsa UI gecikmesinden dolayı boş/beyaz alanlar ile karşılaşabilirsiniz.
Aslında tüm bu sorunların temelinde bridge var. React Native, bridge’den bir şekilde kurtulabilirse bu problemlerin hepsi giderilmiş olacak. Bunun için çözüm olarak Facebook ekibi, React Native mimarisini değiştirerek yenilemeye çalışıyor. Bu mimari temelinde 3 ayaktan oluşuyor:
1. React’in geliştirilmesi ve yeni özellikler
Kasım 2018'deki React konferansında tanıttıkları hooks, suspense ve concurrent rendering yöntemleri React’in gücüne güç katarak yazılımcı deneyiminde büyük rol oynuyor. Hook’lar ile class’sız component gelişimi sağlanıyor. Suspense ile veriler gelmeden önce bileşenin bekletilmesi ve veriler geldikten sonra render edilmesi gerçekleştiriliyor. Concurrent mode’da ise render sürecinin asenkron bir şekilde işletilmesi sağlanıyor. Ayrıca RN ekibi, Codegen gibi bir tool kullanmak için TypeScript ve Flow gibi kütüphanelerle statik tip kontrollerini arttırdı. Dilerseniz Codegen’in ne olduğuna ayrı bir başlık halinde değinelim.
Codegen Nedir?
Native ve JS arasında parametrelerin tip güvenliğini (type safety) sağlamak için oluşturulan bir CLI aracıdır. Codegen sayesinde, JavaScript ve Native taraf arasındaki tip ve veri uyumsuzlukları otomatik bir şekilde giderilecek. Yazılan TypeScript/Flow kodları baz alarak, Fabric ve TurboModules için gereken interface kodları derleme zamanında otomatik olarak oluşturulacak. Böylece bridge yapısında olduğu gibi runtime’da tip kontrolünün yapılmasının ortadan kalkması ile uygulamalarda performans artışı sağlanmış olacak.
Fabric ve Turbomodules için sunulacak interface; TypeScript veya Flow gibi kütüphaneler yardımıyla veri tipli JavaScript dosyaları oluşturulacak ve daha sonra bu interface’in tanımı Java/ObjC için genişletilebilecek. Örneğin TurboModules’ü ele alacak olursak, Java/ObjC sınıfını sarmalayan ve bir JSI nesnesi kullanarak bu metotlara erişen C++ kodu üretilebilecek.
2. JSI nedir? JSC’ye nasıl bir katkısı var?
JSI (JavaScript Interface), adından da anlaşılacağı gibi JavaScript ile C++ arasında bir arayüz sağlar. JSC’nin gücüne güç katmak için Codegen ile oluşturulan JavaScript kodlarının, JSC tarafından direkt olarak hazır bir şekilde kullanılmasını sağlar. JSI’ın bir arayüz (interface) olması nedeniyle, JSC yerine başka bir JavaScript motoru ile kolayca yer değiştirebilir. Bu tak çıkar özelliği sayesinde JSC yerine Microsoft’un ChakraCore’u veya Google’ın V8 motoru ya da Facebook’un ünlü JavaScript motoru olan Hermes kullanılabilir.
Diğer bir güzel tarafı ise, JSI kullanılarak C++ nesnelerinin ve metotlarının referansları JavaScript tarafında tutulabilir. Böylece JavaScript ve Native taraflar birbirinden haberdar olacaktır. Bu sayede JSON mesajlarının serialize edilerek asenkron iletilmesi veya bridge üzerinde darboğaz oluşması gibi problemler yeni mimaride oluşmayacaktır.
Peki neden C++ kodu çalıştırılıyor? Yoksa tüm modüller sıfırdan C++ ile tekrar mı yazılacak?
C++ kodu çalıştırmasının sebebi, Android ve iOS tarafı arasında kod paylaşımının sağlanmasına izin vermesidir. Çünkü halihazırdaki ObjC ve Java kodunun kolayca C++’a çevrilebilmektedir. ObjC, C dilinin bir üst kümesi olduğu için zaten C++’ı destekler. Java tarafında ise, JNI yardımıyla C++ ve Java kodu arasında bir iletişim katmanı oluşturulabilir. Böylece native modüllerin de sıfırdan yazılmasına gerek kalmaz.
3. Fabric ve TurboModules nedir?
Halihazırdaki bridge, asenkron JSON mesajları ile hem arayüzün nasıl olacağını tasvir eden Shadow Tree’yi yönetiyor, hem de native modüller aracılığıyla native tarafta işlem yapıyor. Bu nedenle darboğaz oluşabiliyor ve istenilen bir UX sunulamayabiliyor.
Facebook ekibi bu bridge yapısını iki parçaya bölme kararı aldı. Native kısım ile TurboModules ilgilenecek, UI katmanında ise Fabric renderer olarak görev alacak.
TurboModules Nedir?
TurboModules (NativeModules), herhangi bir anda istenilen native bileşenin çalıştırılmasını sağlayan birimlerdir. JS ve native taraf arasında doğrudan bir şekilde referansları barındırarak (shared ownership), native modülün senkronize bir şekilde çalıştırılabilmesini sağlar. Bridge’de tüm native bileşenlerin tek bir anda yüklenmesi ve JSON mesajlarının beklenmesi gibi bir durum, bu mimaride yaşanmaz. Uygulama daha hızlı açılır hale gelir ve hızlı bir şeklide native bileşenlere ulaşabilir.
Fabric Nedir?
Fabric’i, TurboModules’ün UI (renderer) versiyonu olarak düşünebiliriz. Burada da shared ownership mekanizması mevcuttur ve her iki tarafın referansları tutulur. Bu sayede Shadow Tree üzerinde C++ ile doğrudan erişim sağlanarak arayüzün hızlı bir şekilde çalıştırılması sağlanır (Bridge’deki mevcut yapıda, React -> Native -> Shadow Tree -> Native UI şeklinde adımlar halinde gerçekleşiyor). Böylece çok yavaş telefonlarda hızlı scroll etme sonucu oluşan beyazlıklar giderilmiş olacaktır.
4. React Native’in Facebook’tan ayrılarak kendi reposuna taşınması (Lean Core)
Mevcut halde React Native facebook altında yer aldığı için facebook ekibinin yaptığı değişiklikler direkt olarak herkesi etkiliyor. İçerisinde birçok ayrılabilecek türden repo var. RN reposunun ayrı bir yere taşınması ile RN topluluğu Facebook imzası taşıyan kodlara istediği gibi değişiklikleri yapabilecek ve Facebook’a bağımlılıktan dolayı oluşan react native codebase’i küçültülmüş olacak.
Peki yeni mimariye ne zaman geçilecek?
Facebook tarafında bu konuda hummalı bir çalışma yapılıyor fakat tarihler tam olarak net değil. RN açık kaynaklı olduğu için bu süreci açılan discussion’larda yakından takip edebilirsiniz. Yakın bir tarih vermek gerekirse:
- JSI: Mevcut durumda aktif olarak kullanılıyor. Kodun çoğu yorum satırından oluştuğu için inceleyebilirsiniz. İlgili discussion’a da gidebilirsiniz.
- TurboModules: 2020'nin başında biteceği tahmin ediliyor. İlgili discussion’a bakabilirsiniz.
- Fabric: 2020'nin ortalarında biteceği tahmin ediliyor. İlgili discussion’a bakabilirsiniz.
- Bridge’e olan bağımlılığın tamamen kaldırılması: 2020'nin sonlarında biteceği tahmin ediliyor.
- Codegen: Codegen üzerinde halihazırda bir çalışma var fakat README dosyası yok ne şekilde çalıştırılacağını anlatan bir doküman da yok. İlgili discussion’a bakabilirsiniz.
- Lean Core: Çalışmalar devam ediyor. İlgili discussion’a bakabilirsiniz.
Sonuç Olarak
React Native’in yeni mimarisinin pozitif anlamda birçok etki yaratacağı aşikar. Bakalım yeni mimari geliştirici tarafında nasıl yenilikler ve kolaylıklar sunacak zamanla göreceğiz.
Yazım hakkında bilgi ve görüşlerinizi yorum kısmından belirtebilirsiniz. Ayrıca alkış kısmından bana motive edici alkışlarınızı gönderebilirsiniz. Sonraki yazımda görüşmek üzere…
Kaynaklar:
- https://medium.com/@christian.falch/https-medium-com-christian-falch-react-native-jsi-challenge-1201a69c8fbf
- http://blog.nparashuram.com/2019/01/react-natives-new-architecture-glossary.html
- https://formidable.com/blog/2019/react-codegen-part-1/
- https://formidable.com/blog/2019/jsi-jsc-part-2/
- https://formidable.com/blog/2019/fabric-turbomodules-part-3/
- https://formidable.com/blog/2019/lean-core-part-4/
- https://github.com/react-native-community/discussions-and-proposals/issues/4
- https://github.com/facebook/react-native/issues/23313
- https://github.com/react-native-community/discussions-and-proposals/issues/91
- https://github.com/react-native-community/discussions-and-proposals/issues/40