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

Android Jetpack - Overview

Rico
September 27, 2018

Android Jetpack - Overview

A short summary of the new Android components released with Android Jetpack and a general overview.

Rico

September 27, 2018
Tweet

More Decks by Rico

Other Decks in Technology

Transcript

  1. Was ist Android Jetpack? “ … the next generation of

    components, tools and architectural guidance to accelerate your Android app development.” (Google) “... makes it far easier for you to build robust, high quality apps with less code.” (Google)
  2. Was ist Android Jetpack? 10 Jahre Android - Dinge haben

    sich geändert Quelle: Google - android-developers.googleblog.com • Re-Branding bestehender Komponenten • Refactoring von Support-Libraries • 5 neue Komponenten
  3. Support Librabries und androidx Überblick • Package refactoring - android.support.*

    zu androidx.* • MinSDK unabhängige Benennung • Semantic Versioning • Bundled in APK “When working with any recent release of the support library, you should not assume that the the v# package notation indicates a minimum API support level.” (Google)
  4. Support Librabries und androidx Migration dependencies { ... implementation 'androidx.appcompat:appcompat:1.0.0-rc02'

    ... } dependencies { ... implementation 'com.android.support:appcompat-v7:28.0.0-rc02' ... } <android.support.constraint.ConstraintLayout ... </android.support.constraint.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout ... </androidx.constraintlayout.widget.ConstraintLayout> (Android Studio RC 3.2 +)
  5. Android KTX Kotlin extensions “make Android development with Kotlin more

    concise, pleasant, and idiomatic by leveraging Kotlin language features such as extension functions/properties, lambdas, named parameters, and parameter default values. ” (Google)
  6. Android KTX Kotlin extensions “make Android development with Kotlin more

    concise, pleasant, and idiomatic by leveraging Kotlin language features such as extension functions/properties, lambdas, named parameters, and parameter default values. ” (Google) Android KTX does not add any new features to the existing Android APIs. ” (Google)
  7. candyDrawer.forEach { candy -> Log.i(candy.name, "has ${candy.calories} calories and costs

    ${candy.price}") } Android KTX extension functions + lambdas class Candy(val name: String, var calories: Long, var price: Double) .... val candyDrawer = listOf<Candy>( Candy("Snickers", 400, 0.80), Candy("Cookie", 200, 0.50), Candy("Onion", 50, 1.00)) .... for(candy in candyDrawer) { Log.i(candy.name, "has ${candy.calories} calories and costs ${candy.price}") } candyDrawer.forEach { Log.i(it.name, "has ${it.calories} calories and costs ${it.price}") }
  8. public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {

    for (element in this) action(element) } Android KTX extension functions + lambdas extension properties val <T> List<T>.lastIndex: Int get() = size - 1
  9. fun Candy.addSugar(additionalCalories: Long = 100L, extraCost: Double = 0.0) {

    calories += additionalCalories price += extraCost } Android KTX parameter default values named parameters .... candyDrawer[0].addSugar(100, 0.10) //Reasonable salesman candyDrawer[1].addSugar(101) //Teeth hating salesman candyDrawer[2].addSugar(extraCost = 0.20) //Greedy salesman
  10. Android KTX updatePadding(...) Awesome TextView Awesome TextView Awesome TextView Awesome

    TextView view.updatePadding(30, 30) KTX: view.setPadding(30, 30, view.right, view.bottom) normal: view.setPadding(view.left, 30, view.right, 30) normal: view.updatePadding(top = 30, bottom = 30) KTX:
  11. Android KTX Destructuring Declarations val (x, y) = point KTX:

    val x = point.x val y = point.y normal: inline operator fun Point.component1() = this.x inline operator fun Point.component2() = this.y the magic: val point = Point(21, 42)
  12. Android KTX Destructuring Declarations val (a,r,g,b) = yellow KTX: val

    r = yellow.red val g = yellow.green val b = yellow.blue val a = yellow.alpha normal: inline operator fun @receiver:ColorInt Int.component1() = (this shr 24) and 0xff inline operator fun @receiver:ColorInt Int.component2() = (this shr 16) and 0xff inline operator fun @receiver:ColorInt Int.component3() = (this shr 8) and 0xff inline operator fun @receiver:ColorInt Int.component4() = this and 0xff the magic: // a, r, g, b val yellow = Color.argb(255, 255, 255, 0)
  13. Android KTX Many useful extensions transaction.addListener({cleanUp()}, onCancel = {cleanUp()}) KTX:

    transaction.addListener(object : Transition.TransitionListener { override fun onTransitionStart(transition: Transition?) {} //Boilerplate override fun onTransitionResume(transition: Transition?) {} //Boilerplate override fun onTransitionPause(transition: Transition?) {} //Boilerplate override fun onTransitionCancel(transition: Transition?) { cleanUp() } //Payload override fun onTransitionEnd(transition: Transition?) { cleanUp() } //Payload }) normal: val transaction = Explode() transaction.addListener(::cleanUp, onCancel = ::cleanUp) .... fun cleanUp(transition: Transition? = null) {/*stuff*/} transaction.doOnCancel(::cleanUp) transaction.doOnEnd { cleanUp() }
  14. Android KTX Not the goal of KTX button.click { stuff()

    } //not just Code-Golf not in KTX: button.setOnClickListener(object: View.OnClickListener{ override fun onClick(v: View?) { stuff() } }) normal: val button = view.findViewById<Button>(R.id.buttonPanel) button.setOnClickListener{ stuff() } still normal:
  15. Android KTX Many things to explore androidx.core:core-ktx androidx.fragment:fragment-ktx androidx.palette:palette-ktx androidx.sqlite:sqlite-ktx

    androidx.collection:collection-ktx androidx.lifecycle:lifecycle-viewmodel-ktx androidx.lifecycle:lifecycle-reactivestreams-ktx android.arch.navigation:navigation-common-ktx android.arch.navigation:navigation-fragment-ktx android.arch.navigation:navigation-runtime-ktx android.arch.navigation:navigation-testing-ktx android.arch.navigation:navigation-ui-ktx android.arch.work:work-runtime-ktx
  16. Architecture Überblick • Room Persistence Library • Architecture Components •

    LiveData, ViewModel ... • Architecture Proposal: Codelabs • Neue Komponenten • Paging - stable 1.0.1 • WorkManager • Navigation - 1.0.0-alpha05
  17. Architecture - Work Manager Background Work unter Android • Viele

    Wege führen ans Ziel • Anwendungsfall entscheidet • Zugesicherte Ausführung? • Zeitnahe Ausführung? • Versionsabhängige Einschränkungen Services AsyncTasks JobScheduler RxJava Firebase JobDispatcher Threads Anko ?
  18. Architecture - Work Manager - 1.0.0-alpha09 - Garantierte Ausführung -

    Verketten und Parallelisieren von Arbeitseinheiten - Deckt breites Versionsspektrum ab - Opportunistisch Quelle: Google I/O ‘18
  19. Architecture - Work Manager Definieren von Arbeitspaketen override fun doWork():

    Result { val filePaths = inputData.getStringArray(KEY_INPUT_FILES) filePaths?.forEach { path -> return if (DropboxUploader.uploadFile(path)) { Result.SUCCESS } else { Result.FAILURE } } }
  20. Architecture - Work Manager Ausführen von Arbeitspaketen - OneTimeWorkRequest val

    uploadRequest = OneTimeWorkRequest .Builder(UploadRequest::class.java) .build() • Einmalige Ausführung: OneTimeWorkRequest • Constraints - Charging, Network Status, Verfügbarer Storage, ... val workManager = WorkManager.getInstance() workManager .beginWith(uploadRequest) .enqueue()
  21. Architecture - Work Manager Verketten und Parallelisieren von Arbeitspaketen •

    Verkettung von Workern • Parallele Ausführung von Workern
  22. Architecture - Work Manager Verketten und Parallelisieren von Arbeitspaketen ....

    val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java) .setInputMerger(ArrayCreatingInputMerger::class.java) .build() val cleanupRequest = OneTimeWorkRequest.Builder(CleanupWorker::class.java).build() workManager .beginWith(imageFilterRequest) .then(zipRequest, summaryRequest) .then(uploadRequest) .then(cleanupRequest) .enqueue()
  23. Work Manager: Periodic Work • Funktioniert ähnlich OneTimeWorkRequest • Kein

    chaining val request = PeriodicWorkRequest.Builder( ZipAndUploadWorker::class.java, MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS ) .build() workManager.enqueueUniquePeriodicWork( ZipAndUploadWorker.SCHEDULED_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, request )
  24. Architecture - Work Manager Status von Arbeitspaketen und Abbruch •

    Worker können anhand von Tag oder ID abgebrochen werden • WorkManager ermöglicht Observation von Workern
  25. Architecture - Navigation Library Überblick • Navigation Library • Mögliche

    Navigationspfade werden in “Navigation Graph” definiert • XML oder grafischer Editor • Automatisch generierte FragmentTransactions • Backstack management • Material Design Extensions & Typesafe Fragment Args • Implementiert “Guiding Principles of Navigation”
  26. Architecture - Navigation Navigation Graph <navigation ... android:id="@+id/app_navigation" app:startDestination="@id/greenFragment"> ...

    <fragment android:id="@+id/blueFragment" android:name="com.example.rcio.nav.BlueFragment" android:label="BlueFragment" tools:layout="@layout/fragment_blue"> <action android:id="@+id/action_blue_to_purple" app:destination="@id/purpleFragment" /> </fragment> ... </navigation>
  27. Architecture - Navigation NavigationHost - Fragments <fragment android:id="@+id/nav_host_fragment" android:layout_marginTop="?attr/actionBarSize" android:layout_width="match_parent"

    android:layout_height="match_parent" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/app_navigation" app:defaultNavHost="true" />
  28. Architecture - Navigation Ausführen von Actions view.buttonPurple.setOnClickListener { purpleButton ->

    purpleButton.findNavController().navigate(R.id.action_blue_to_purple) } override fun onSupportNavigateUp(): Boolean { return findNavController(R.id.nav_host_fragment).navigateUp() }
  29. Architecture - Navigation Fragment Arguments bisher private const val ARG_PRODUCTID

    = "productId" private const val ARG_PRODUCTGROUP = "categoryName" companion object { @JvmStatic fun newInstance(productId: Long, categoryName: String) = UnusedFragment().apply { arguments = Bundle().apply { putLong(ARG_PRODUCTID, productId) putString(ARG_PRODUCTGROUP, categoryName) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { param1 = it.getString(ARG_PRODUCTID) param2 = it.getString(ARG_PRODUCTGROUP) } }
  30. Architecture - Navigation Type safe args <navigation ...> <fragment android:id="@+id/blueFragment"

    ... android:label="BlueFragment" tools:layout="@layout/fragment_blue" > <argument android:name="BlueFragmentText" app:argType="string" android:defaultValue="DefaultText"/> ... </fragment> </navigation>
  31. Architecture - Navigation Type safe args private var args: BlueFragmentArgs?

    = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) args = arguments?.let { BlueFragmentArgs.fromBundle(it) } } view.buttonBlue.setOnClickListener { view -> val greenToBlueAction = GreenFragmentDirections .actionGreenToBlue() .setBlueFragmentText("Hi!!11") view.findNavController() .navigate(greenToBlueAction) }
  32. Slices <provider android:authorities="com.android.example.slicecodelab" android:name=".MySliceProvider" android:grantUriPermissions="true" android:exported="true"> </provider> Global (because it’s

    easy ;) : val candyDrawer = listOf<Candy>( Candy("Snickers", 200, 0.8, R.drawable.snickers), Candy("Cookie", 400, 0.5, R.drawable.cookie), Candy("Onion", 50, 1.0, R.drawable.onion)) class Candy(val name: String, var calories: Long, var price: Double, val imageId: Int ) Manifest:
  33. Slices val listBuilder = ListBuilder(context!!, sliceUri) // Construct our parent

    builder listBuilder.setHeader{ it.apply { title = "Candy Drawer" subtitle = "cosee.biz" summary = "cosee.biz" primaryAction = openMainActivity } } Manifest:
  34. Slices class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider() =

    true override fun onBindSlice(sliceUri: Uri): Slice? { val path = sliceUri.path when { path!!.startsWith("/candy") -> return createCandySlice(sliceUri) } return null } .... MySliceProvider:
  35. Slices val listBuilder = ListBuilder(context!!, sliceUri) // Construct our parent

    builder MySliceProvider.createCandySlice(sliceUri: Uri): listBuilder.setHeader{ it.apply { title = "Candy Drawer" subtitle = "cosee.biz" summary = "cosee.biz" primaryAction = openMainActivity } }
  36. Slices listBuilder.addGridRow { it.apply { primaryAction = openMainActivity candyDrawer.forEach {

    candy -> addCell{ it.apply { it.addImage(IconCompat.createWithResource(context, candy.imageId), SMALL_IMAGE) addTitleText(candy.name) addText("${candy.price}€") contentIntent = MainActivity.buildPendingIntent(context, candy.name) } } } } } return listBuilder.build() MySliceProvider.createCandySlice(sliceUri: Uri):