Slide 1

Slide 1 text

2019 DevDay Modernize the Development of LINE Messenger for Android > Masakuni Oishi > LINE App Dev Team1 Senior Software Engineer

Slide 2

Slide 2 text

Communication app “LINE” Initial release in June 2011

Slide 3

Slide 3 text

Released in 2011 December 2010 - Android 2.3 Gingerbread February 2011 - Android 3.0 Honeycomb June 2011 - LINE 1.0 October 2011 - Android 4.0 Ice Cream Sandwich November 2011 - Galaxy Nexus

Slide 4

Slide 4 text

Fragment Support library AndroidX Architecture components Jetpack Runtime permissions Dynamic feature module MediaCodec Kotlin Multi-window App Bundle Play Services

Slide 5

Slide 5 text

Keep up with changes in the development environment

Slide 6

Slide 6 text

Agenda > Kotlin and Kotlin Coroutines > Multi-module project

Slide 7

Slide 7 text

Kotlin
 in LINE Android app

Slide 8

Slide 8 text

Kotlin in LINE Android App > April 2017 - Start using Kotlin for unit tests > Then, improves our Coding Conventions • Adding some rules to the official Coding Conventions • e.g. Prefer “.also { }” than “.apply { }” • e.g. Don’t omit types of properties. > June 2017 - Start using Kotlin in production • New code should be written in Kotlin • When adding new features to Java code, convert the code to Kotlin first

Slide 9

Slide 9 text

Kotlin in LINE Android App > The number of code in LINE Android app Java code 6200 files (53%) 1048k lines (64%) Kotlin code 5450 files (47%) 593k lines (36%) (As of October 2019. Not including test code.)

Slide 10

Slide 10 text

Benefits of Kotlin > 100% interoperable with Java > Android Studio (IntelliJ IDEA) has a Java-to-Kotlin converter > null safety > Immutable collections > property delegation (lazy), extensions, data classes, sealed classes, etc. > Coroutines ← !!

Slide 11

Slide 11 text

Kotlin Coroutines > Simplifies asynchronous tasks • Can be used in procedural-way rather than functional-way (e.g. RxJava) • No callback-hell > Similar to async/await in C#, JavaScript > Structured concurrency & Managed cancellation > Developed as experimental for a long while > Oct 2018 - Kotlin Coroutines 1.0.0 → We couldn’t adopt it when we started using Kotlin (April 2017)

Slide 12

Slide 12 text

Async Libraries Used Before > LINE Android app has a lot of async code, and the code was written in many kinds of libraries… • android.os.AsyncTask • java.util.concurrent.Executor + callbacks • RxJava 1.x • RxJava 2.x • Our own async library • etc…

Slide 13

Slide 13 text

Transition Plan to Kotlin Coroutines > We want to unify these async libraries. > But until recently, we couldn’t adopt Kotlin Coroutines. → First, we adopted RxJava2 entirely. → Then, transit it to Kotlin Coroutines later. Kotlin Coroutines has a utility library that allows Coroutines and RxJava2 to be used each other.

Slide 14

Slide 14 text

RxJava2 → Kotlin Coroutines > To facilitate the transition to Kotlin Coroutines, functions that perform asynchronous tasks using RxJava2 should return Single. > We call such functions as “async functions”. // async function fun fooAsync(param: Param): Single = …

Slide 15

Slide 15 text

Example of Async Function // blocking function @WorkerThread fun fetchJsonBlocking(url: Url): Json = … // async function fun fetchJsonAsync(url: Url): Single = Single.fromCallable { fetchJsonBlocking(url) }.subscribeOn(Schedulers.io())

Slide 16

Slide 16 text

Call an Async Function val disposable = fetchJsonAsync(url) .observeOn(AndroidSchedulers.Main()) .subscribe { json -> updateUi(json) }

Slide 17

Slide 17 text

Call Multiple Async Functions // another async function fun getFooDataFor(json: Json): Single = … val disposable = fetchJsonAsync(url) .flatMap { json -> getFooDataFor(json) }.observeOn(AndroidSchedulers.Main()) .subscribe { fooData -> showFooData(fooData) }

Slide 18

Slide 18 text

Async Functions Can Be Easily Migrated to Coroutines

Slide 19

Slide 19 text

Call an Async Function From Coroutine mainScope.launch { val json = fetchJsonAsync(url).await() updateUi(json) } > By simply adding “.await()”, you can call async functions from Coroutines.

Slide 20

Slide 20 text

Call Multiple Async Functions From Coroutine mainScope.launch { val json = fetchJsonAsync(url).await() val fooData = getFooDataFor(json).await() showFooData(fooData) } > By using Coroutines, you can write multiple calls of async functions procedurally.

Slide 21

Slide 21 text

Migrate Async Functions to Suspending Functions > Finish the transition to Coroutines by rewriting async functions with suspending functions. // suspending function suspend fun foo(param: Param): Result = … > Suspending functions can be called without “.await()”.

Slide 22

Slide 22 text

Example of Suspending Function // async function fun fetchJsonAsync(url: Url): Single = Single.fromCallable { fetchJsonBlocking(url) }.subscribeOn(Schedulers.io()) // suspending function suspend fun fetchJson(url: Url): Json = withContext(Dispatchers.IO) { fetchJsonBlocking(url) }

Slide 23

Slide 23 text

Cancellation of Coroutines > When using RxJava in Android apps, we usually cancel subscriptions of Rx when Activities / Fragments are stopped. > So, we implemented a utility class to cancel Coroutines on Activity/ Fragment’s onStop(). AutoResetLifecycleScope https://github.com/line/lich/tree/master/lifecycle

Slide 24

Slide 24 text

class FooFragment : Fragment() { private val coroutineScope: CoroutineScope = AutoResetLifecycleScope(this) fun fetchAndShowJson() { coroutineScope.launch { try { val json = fooService.fetchJson() updateUi(json) } catch (e: IOException) { Toast.makeText(…).show() } } }

Slide 25

Slide 25 text

What We Did To Introduce Coroutines > Wrote documents to introduce Kotlin Coroutines through RxJava2. > Implemented some utility classes for cancellation of Coroutines. > The utility classes and sample code using the MVVM pattern are published on GitHub: https://github.com/line/lich

Slide 26

Slide 26 text

Multi-module project
 for LINE Android app

Slide 27

Slide 27 text

Why Is Modularization Necessary? > Decouple code for each feature • Extensibility - Easy to understand the scope of impact on changes. > Speed up the build time • LINE Android app has more than 1.6M lines! > Reduce the initial APK size on installation • By using Dynamic Feature Modules + on-demand delivery

Slide 28

Slide 28 text

app

Slide 29

Slide 29 text

app Foo feature

Slide 30

Slide 30 text

Extract Facade of Features > To split a feature as a module, extract its Facade class. > A feature only exposes its Facade (and a few parameter classes). • The feature must be accessed through the Facade only. class FooFacade { fun launchFooActivity(messageId: String) { … } … }

Slide 31

Slide 31 text

app Foo feature

Slide 32

Slide 32 text

FooFacade app Foo feature

Slide 33

Slide 33 text

Split Facade into Interface and Implementation interface FooFacade { fun launchFooActivity(messageId: String) } class FooFacadeImpl : FooFacade { override fun launchFooActivity(messageId: String) = … } > Other features only need to know the Facade interface.

Slide 34

Slide 34 text

FooFacade app Foo feature

Slide 35

Slide 35 text

FooFacade FooFacadeImpl app Foo feature

Slide 36

Slide 36 text

FooFacade FooFacadeImpl app module Foo module

Slide 37

Slide 37 text

How To Get the Instance of the Facade? > The instance of the Facade is needed to call the feature. > How about using Dependency Injection (DI)? • Existing DI libraries such as Dagger2 are very complex to set up for multi-module projects. > So, we developed a library that easily solves the dependency problem in multi-module projects. • https://github.com/line/lich/tree/master/component val fooFacade: FooFacade = … ←??? fooFacade.launchFooActivity(messageId)

Slide 38

Slide 38 text

interface FooFacade { fun launchFooActivity(messageId: String) companion object : ComponentFactory() { override fun createComponent(context: Context): FooFacade = delegateToServiceLoader(context) } } @AutoService(FooFacade::class) class FooFacadeImpl : FooFacade { override fun launchFooActivity(messageId: String) = … } Resolve Dependencies of Facades

Slide 39

Slide 39 text

Call Feature Through its Facade class OtherModuleActivity : Activity() { // `by component()` provides the instance of FacadeImpl. private val fooFacade by component(FooFacade) fun startFooFeature(messageId: String) { fooFacade.launchFooActivity(messageId) } }

Slide 40

Slide 40 text

FooFacade FooFacadeImpl app module Foo module Resolve dependency

Slide 41

Slide 41 text

FooFacade FooFacadeImpl app module Foo module BarFacade BazFacade BarFacadeImpl BazFacadeImpl Bar module Baz module

Slide 42

Slide 42 text

How To Modularize Android Project > Extract the Facade interface of a feature. > Let other modules call the feature only through the Facade. > Split the implementation of the feature as a module. > Use “Lich Component” library to get the instance of a Facade. • https://github.com/line/lich/tree/master/component

Slide 43

Slide 43 text

About Lich > Lich is an OSS library collection for Android apps. • Developed by LINE Android client team. https://github.com/line/lich > Let’s check it out, and please star it!

Slide 44

Slide 44 text

Thank you