Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DevFest Pangyo 2019 ~ 최신 AndroidX 체크

DevFest Pangyo 2019 ~ 최신 AndroidX 체크

DevFest Pangyo 2019에서 발표한 자료입니다.

pluulove (노현석)

November 15, 2019
Tweet

More Decks by pluulove (노현석)

Other Decks in Programming

Transcript

  1. Question?! 1. AndroidXܳ ٜযࠌ׮? 2. Jetpackਸ ঌҊ੓׮? 3. Support Libraryо

    ӝরդ׮? 4. Android Architecture Components? 5. ੉ٜ੄ ର੉੼ਸ ঌҊ੓׮?
  2. AndroidX Android ౱੉ Jetpack ղীࢲ ۄ੉࠳۞ܻܳ ѐߊ, పझ౟, ಁః૚, ߡ੹

    ҙܻ, ୹दೞחؘ ࢎਊೞח য়೑ࣗझ ೐۽ં౟ੑפ׮. https://developer.android.com/jetpack/androidx
  3. Updated packaging • Android java ࣗझܳ ಁః૑߹۽ ܻಂష݂ ▪ Android.<feature>.ClassName

    • Maven ▪ androidx.<feature>:<feature>-<sub-feature> ▪ ೙ਃೠ ӝמ߹ ߓನ • -v7, -v4 ಁః૑ݺ ઁѢ
  4. Activity & Fragment & Navigation - Activity 1.0.0(Stable) , 1.1.0-rc02(RC)

    - Fragment 1.1.0(Stable) , 1.2.0-rc02(RC) - Navigation 2.1.0(Stable) , 2.2.0-rc02(RC)
  5. class OldFragment : Fragment() { // Crazy fun backStart() {

    requireActivity().onBackPressed() } } җѢ Fragmentীࢲ onBackPressed // Crazy fun backStart() { requireActivity().onBackPressed() } // Safe override fun onAttach(context: Context) { super.onAttach(context) backListener = context as? BackListener backListener?.onBackPressed() }
  6. class MainFragment : Fragment() { private val dispatcher by lazy

    { requireActivity().onBackPressedDispatcher } private val backPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { // TODO } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) dispatcher.addCallback(this, backPressedCallback) } } Fragmentীࢲ onBackPressed ୊ܻ Back Pressed Flag
  7. // ComponentActivity.java public class ComponentActivity { private final OnBackPressedDispatcher mOnBackPressedDispatcher

    = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { // fallbackOnBackPressed ComponentActivity.super.onBackPressed(); } }); @Override @MainThread public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } } // OnBackPressedDispatcher.java public final class OnBackPressedDispatcher { @MainThread public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } } } ComponentActivity / BackPressedDispatcher ൒ܴ
  8. // ComponentActivity.java public class ComponentActivity { private final OnBackPressedDispatcher mOnBackPressedDispatcher

    = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { // fallbackOnBackPressed ComponentActivity.super.onBackPressed(); } }); @Override @MainThread public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } } // OnBackPressedDispatcher.java public final class OnBackPressedDispatcher { @MainThread public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } } } ComponentActivity / BackPressedDispatcher ൒ܴ
  9. // ComponentActivity.java public class ComponentActivity { private final OnBackPressedDispatcher mOnBackPressedDispatcher

    = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { // fallbackOnBackPressed ComponentActivity.super.onBackPressed(); } }); @Override @MainThread public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } } ComponentActivity / BackPressedDispatcher ൒ܴ // OnBackPressedDispatcher.java public final class OnBackPressedDispatcher { @MainThread public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } } }
  10. // ComponentActivity.java public class ComponentActivity { private final OnBackPressedDispatcher mOnBackPressedDispatcher

    = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { // fallbackOnBackPressed ComponentActivity.super.onBackPressed(); } }); @Override @MainThread public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } } // OnBackPressedDispatcher.java public final class OnBackPressedDispatcher { @MainThread public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } } } ComponentActivity / BackPressedDispatcher ൒ܴ
  11. // ComponentActivity.java public class ComponentActivity { private final OnBackPressedDispatcher mOnBackPressedDispatcher

    = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { // fallbackOnBackPressed ComponentActivity.super.onBackPressed(); } }); @Override @MainThread public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } } // OnBackPressedDispatcher.java public final class OnBackPressedDispatcher { @MainThread public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } } } ComponentActivity / BackPressedDispatcher ൒ܴ
  12. ViewModel ୡӝച lifecycle-viewmodel private val viewModel by lazy { ViewModelProviders.of(this).get<MainViewModel>()

    } This constructor was deprecated in API level 1.1.0. This class should not be directly instantiated
  13. ViewModel ୡӝച lifecycle-viewmodel val vm: VM = ViewModelProvider(this, factory).get(VM::class.java) val

    vm: VM = ViewModelProvider(this).get(VM::class.java) public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); }
  14. Support Coroutines in ViewModel After lifecycle-viewmodel-ktx 2.1.0 val ViewModel.viewModelScope: CoroutineScope

    get() { val scope: CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope } return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)) } To be continue…
  15. #AndroidDevSummit @Composable fun FilteredList(contacts: List<Contact>, filterText: String = "") {

    val state = +state { filterText } VerticalScroller { Column { TextField(filterText, onValueChange = { state.value = it }) contacts.filter { it.contains(state.value) }.forEach { Row { Image(it.photo) Text(it.name) } } } } }
  16. #AndroidDevSummit • Simplify • Fix what’s broken • No more

    cheating • Material + Animations • Compatibility Jetpack Compose
  17. #AndroidDevSummit Android Studio Live preview, Apply Changes Compose Compiler Plugin

    Code generation extensions kotlinc Upstream Kotlin compiler (1.4) Compose UI Foundation Standard layouts, interactions Compose UI Core Input, Measure, Layout, Drawing Compose Runtime Tree management, Effects Compose UI Material Surface, Buttons, Tabs, Themes Compose Build time (development host) Runtime (on device)
  18. #AndroidDevSummit Compose UI Foundation Standard layouts, interactions Compose UI Core

    Input, Measure, Layout, Drawing Compose UI Material Surface, Buttons, Tabs, Themes BUTTON Gestures, accessibility, & other internals
  19. Jetpack Compose • I/O 19, Unbundled • Announced Android Studio

    4.0 Canary 1 in Android Dev Summit • Last Release 0.1.0-dev02
  20. Maybe Merge? ConstraintLayout in Compose WIP Case2 Link : https://android-

    review.googlesource.com/c/ platform/frameworks/support/ +/1147725
  21. #AndroidDevSummit Using FragmentScenario @Test fun testEventFragment() { val scenario =

    launchFragmentInContainer<MyFragment>() onView(withId(R.id.refresh)).perform(click()) scenario.onFrament { fragment -> // Check that the fragment handled the click correctly. } }
  22. #AndroidDevSummit Using FragmentScenario @Test fun testEventMoveToCreatedFragment() { val scenario =

    launchFragmentInContainer<MyFragment>() scenario.moveToState(State.CREATED) } @Test fun testEventFragment() { val scenario = launchFragmentInContainer<MyFragment>() scenario.recreate() }
  23. #AndroidDevSummit Using FragmentFactory private class MyFactory() : FragmentFactory() { override

    fun instantiate( classLoader: ClassLoader, className: String ) = when (className) { MyFragment::class.java.name -> MyFragment(...) else -> super.instantiate(classLoader, className) } } override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFactory() super.onCreate(savedInstanceState) }
  24. FragmentContainerView extends FrameLayout • Fragmentਊ Container ◦ AddView => IllegalStateException

    • <fragment> / <FrameLayout>ܳ ؀୓ • Z-index ޙઁ ೧Ѿ • 3о૑ ੘সਸ ૓೯ ◦ ࢜۽਍ Fragment ੋझఢझ ࢤࢿ ◦ Fragment.onInflate(Context, AttributeSet, Bundle) ഐ୹ ◦ FragmentTransactionਸ प೯ೞৈ FragmentManagerী ୶о
  25. ViewModel ୡӝച // activity-ktx val activityVM : ActivityViewModel by viewModels()

    // fragment-ktx val fragmentViewModel : MyViewModel by viewModels() val activityViewModel : MyViewModel by activityViewModels() // navigation-ktx val navGraphViewModel: MyViewModel by navGraphViewModels(R.id.main)
  26. ROOM • SQLiteী ؀ೠ ୶࢚ച ҅க ઁҕ • ஹ౵ੌ ఋ੐ী

    Query ୓௼ • Migration੉ ए਑ • పझ౟ೞӝ ए਑ #AndroidDevSummit • RxJava/Coroutines ૑ਗ • Raw ௪ܻ ૑ਗ • AndroidX ݃੉Ӓۨ੉࣌ • Full Text Search • Views
  27. #AndroidDevSummit Async queres Query Type Observable Read One-Shot Read /

    Write Lifecycle RxJava Coroutines LiveData Publisher, Observable, Flowable Flowable N/A Single, Maybe, Completale suspend fun New!
  28. Flow 101 - Kotlin coroutine primitive - Asynchronous - Cold

    stream • transform stream (e.g. map, filter) • consume data (e.g. collect, single, toList)
  29. Flow Example fun getDogsToPet(limit: Int = -1): Flow<Dog> = flow

    { var i = 0 while (limit < 0 || i > limit) { emit(getRandomDog()) i++ } }
  30. ୡӝ DB ؘ੉ఠ ࢸ஖ (җѢ) 1. Database ৌӝ 2. Schema

    Ѩૐ 3. Database File ਫ਼Ә 4. Thread زӝച 5. Content ࠂࢎ 6. Database ײӝ #AndroidDevSummit
  31. #AndroidDevSummit • Easy to get off main thread • Minimal

    boilerplate • Structured concurrency = managed jobs Coroutines ੢੼
  32. #AndroidDevSummit class MyActivity : Activity { override fun onCreate(state: Bundle?)

    { super.onCreate(state) lifecycleScope.launch { // Run } lifecycleScope.launchWhenResumed { // Run } } } Activities & Fragments lifecycleScope.launchWhenResumed { // Run }
  33. #AndroidDevSummit ViewModel + LiveData class MainActivityViewModel : ViewModel { private

    val _result = MutableLiveData<String>() val result = LiveData<String> = _result init { viewModelScope.launch { val computationResult = doComputation() _result.value = computationResult } } }
  34. #AndroidDevSummit liveData coroutine builder class MyViewModel { val result =

    liveData { emit(doComputation()) } } After lifecycle 2.2 liveData(Dispatchers.IO) { emit(LOADING.STRING) emitSource(dataSource.fetchWeather()) } 1 2 LiveData
  35. emit N Values val currentWeather: LiveData<String> = dataSource.fetchWeather() LiveData →

    LiveData Flow → LiveData val currentWeatherFlow: LiveData<String> = liveData { dataSource.fetchWeatherFlow().collect { emit(it) } }
  36. emit N Values LiveData → LiveData Flow → LiveData val

    currentWeather: LiveData<String> = dataSource.fetchWeather() val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow().asLiveData()
  37. emit 1 + emit N Values val currentWeather: LiveData<String> =

    liveData { emit(LOADING_STRING) emitSource(dataSource.fetchWeather()) } LiveData → LiveData Flow → LiveData val currentWeatherFlow: LiveData<String> = liveData { emit(LOADING_STRING) emitSource( dataSource.fetchWeatherFlow().asLiveData() ) }
  38. emit 1 + emit N Values val currentWeather: LiveData<String> =

    liveData { emit(LOADING_STRING) emitSource(dataSource.fetchWeather()) } LiveData → LiveData Flow → LiveData val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow() .onStart { emit(LOADING_STRING) } .asLiveData()
  39. Suspend transformation val currentWeather: LiveData<String> = dataSource.fetchWeather().switchMap { liveData {

    emit(heavyTransformation(it)) } } LiveData → LiveData val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow() .map { heavyTransformation(it) } .asLiveData() } Flow → LiveData
  40. #AndroidDevSummit +{API} New Capabilities • Tap to focus • Zoom

    control • Device rotation info • Flash availability • And a lot more!
  41. #AndroidDevSummit // Set up use case // Step 1. Config

    val imageCapture = ImageCaptureConfig.Builder() .setTargetResolution(Size(600, 800)) .build() // Step 2. Bind CameraX.bindToLifecycle(this, imageCapture) // Step 3. Act imageCapture.takePicture(...)
  42. What is benchmarking? - ௏٘ ࢿמ ஏ੿ - ஏ੿ػ Ѿҗ۽

    ѐࢶ ߈৔ - ੹୓ ೐۽Ӓ۔ प೯ Improving App Performance with Benchmarking (Google I/O ’19) https://www.youtube.com/watch?v=ZffMCJdA5Qc
  43. Source : https://www.youtube.com/watch?v=ZffMCJdA5Qc Benchmark @Test fun myFirstBenchmark() { val worker

    = TestListenableWorkerBuilder<MyWorker>(context).build() val COUNT = 5 // Small iteration count val start = System.nanoTime() // Measuring immediately for (i in 0..COUNT) { worker.doWork() } // Includes outliers! val elapsed = (System.nanoTime() - start) Log.d("Benchmark", "Time taken was $elapsed ns") }
  44. Source : https://www.youtube.com/watch?v=ZffMCJdA5Qc Benchmark @Test fun myFirstBenchmark() { val worker

    = TestListenableWorkerBuilder<MyWorker>(context).build() val COUNT = 5 // Small iteration count val start = System.nanoTime() // Measuring immediately for (i in 0..COUNT) { worker.doWork() } // Includes outliers! val elapsed = (System.nanoTime() - start) Log.d("Benchmark", "Time taken was $elapsed ns") }
  45. Source : https://www.youtube.com/watch?v=ZffMCJdA5Qc Benchmark @Test fun myFirstBenchmark() { val worker

    = TestListenableWorkerBuilder<MyWorker>(context).build() val COUNT = 5 // Small iteration count val start = System.nanoTime() // Measuring immediately for (i in 0..COUNT) { worker.doWork() } // Includes outliers! val elapsed = (System.nanoTime() - start) Log.d("Benchmark", "Time taken was $elapsed ns") }
  46. Source : https://www.youtube.com/watch?v=ZffMCJdA5Qc Benchmark @get:Rule val benchmarkRule = BenchmarkRule() @Test

    fun myFirstBenchmark() { val worker = TestListenableWorkerBuilder<MyWorker>(context).build() val COUNT = 5 // Small iteration count val start = System.nanoTime() // Measuring immediately for (i in 0..COUNT) { worker.doWork() } // Includes outliers! val elapsed = (System.nanoTime() - start) Log.d("Benchmark", "Time taken was $elapsed ns") }
  47. Source : https://www.youtube.com/watch?v=ZffMCJdA5Qc Benchmark @get:Rule val benchmarkRule = BenchmarkRule() @Test

    fun myFirstBenchmark() { val worker = TestListenableWorkerBuilder<MyWorker>(context).build() benchmarkRule.measureRepeated { worker.doWork() } }
  48. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  49. @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule = BenchmarkRule()

    @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt RecyclerView Benchmark
  50. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  51. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  52. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  53. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  54. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  55. RecyclerView Benchmark @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val benchmarkRule

    = BenchmarkRule() @UiThreadTest @Test fun simpleScroll() { val recyclerView = activityRule.activity.recyclerView benchmarkRule.measureRepeated { val heightOfLastItem = recyclerView.getLastChild().height recyclerView.scrollBy(0, heightOfLastItem) } } https://github.com/android/performance-samples/blob/master/BenchmarkSample/benchmark/ src/androidTest/java/com/example/benchmark/RecyclerViewBenchmark.kt
  56. Stable Activity AppCompat Lifecycle Navigation Room Work Autofill Benchmark Biometric

    Coordinator Layout Fragment Lifecycle ViewModel-SavedState Navigation RecyclerView ViewPager2 ConstraintLayout RC Beta Alpha Ads Browser Camera Car Compose Security