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

[Devoxx Belgium '18] Things I wish I knew when I started building Android Libraries/SDK -Vol.2

[Devoxx Belgium '18] Things I wish I knew when I started building Android Libraries/SDK -Vol.2

Building an Android library in the current times is way different than what it used to be earlier. Things have changed considerably and keeping up to date with them is a now a necessity than just some acquirable knowledge.

This session in a second part to the last year’s talk at Droidcon Berlin 2017 with the same title. In this session we will dive deeper into best practices and ways of architecting android libraries. You will get to learn about the common pitfalls and how to overcome them by using the right approach such as leveraging architecture components and making your android libraries lifecycle aware. We will also be covering how one can leverage Kotlin language when developing android libraries as well as information around API design and exploring the path to becoming a better Android Library Developer.

By the end of this session, you will be all set to build android libraries that scale and have API which contributes to developer happiness.

Google Slides: https://docs.google.com/presentation/d/1svOPXO9dYeuim1uHHymciIobdFKMb-4TKNUrvWmovH8/edit?usp=sharing

Video: https://www.youtube.com/watch?v=jQyt3HSmx2I

Nishant Srivastava

November 14, 2018
Tweet

More Decks by Nishant Srivastava

Other Decks in Technology

Transcript

  1. View Slide

  2. OSS Android libraries developed

    View Slide

  3. Android ARchive(AAR)
    @nisrulz

    View Slide

  4. AAR = Java ARchive(JAR) + Resources
    @nisrulz

    View Slide

  5. AAR = Java ARchive(JAR) + Resources
    @nisrulz
    Sensey: https://github.com/nisrulz/sensey

    View Slide

  6. AAR = Java ARchive(JAR) + Resources
    @nisrulz

    View Slide

  7. Using an AAR as dependency
    @nisrulz

    View Slide

  8. Using an AAR as dependency
    @nisrulz
    repositories{
    flatDir{
    dirs 'libs'
    }
    }
    dependencies {
    implementation(name:'nameOfYourAARFileWithoutExtension', ext:'aar')
    }

    View Slide

  9. Using an AAR as dependency
    @nisrulz
    repositories{
    flatDir{
    dirs 'libs'
    }
    }
    dependencies {
    implementation(name:'nameOfYourAARFileWithoutExtension', ext:'aar')
    }
    // None of the dependencies of the library are downloaded

    View Slide

  10. Why doesn’t my AAR
    download transitive
    dependencies?
    @nisrulz

    View Slide

  11. Sensey’s build.gradle
    @nisrulz
    dependencies {
    ...
    // Other testing dependencies
    // Transitive dependency: Support Compat library
    implementation "com.android.support:support-compat:27.0.2"
    }

    View Slide

  12. Sensey’s AAR
    @nisrulz

    View Slide

  13. Sensey’s AAR
    @nisrulz
    No build.gradle file

    View Slide

  14. Maven Artifact
    @nisrulz

    View Slide

  15. @nisrulz
    Maven Artifact

    View Slide

  16. POM?
    @nisrulz

    View Slide

  17. POM
    @nisrulz
    + Stands for Project Object Model
    + Is a XML file
    + Contains
    + Information about the project
    + Configuration details used by Maven to build the project

    View Slide

  18. POM
    @nisrulz
    POM of Sensey Android Library


    4.0.0
    com.github.nisrulzsensey1.8.0
    aarsensey
    Android library which makes playing with sensor events & detecting gestures a breeze.
    https://github.com/nisrulz/sensey


    The Apache Software License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0.txt



    nisrulzNishant [email protected]


    https://github.com/nisrulz/sensey.git
    https://github.com/nisrulz/sensey.git
    https://github.com/nisrulz/sensey



    com.android.support
    support-compat27.0.2
    runtime


    ...



    View Slide

  19. POM
    @nisrulz
    POM of Sensey Android Library


    4.0.0
    com.github.nisrulzsensey1.8.0
    aarsensey
    Android library which makes playing with sensor events & detecting gestures a breeze.
    https://github.com/nisrulz/sensey


    The Apache Software License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0.txt



    nisrulzNishant [email protected]


    https://github.com/nisrulz/sensey.git
    https://github.com/nisrulz/sensey.git
    https://github.com/nisrulz/sensey



    com.android.support
    support-compat27.0.2
    runtime


    ...



    View Slide

  20. POM
    @nisrulz
    POM of Sensey Android Library


    ...


    com.android.support
    support-compat27.0.2
    runtime


    ...



    View Slide

  21. Bundled Proguard Configs
    @nisrulz

    View Slide

  22. Bundled Proguard Configs
    @nisrulz
    android {
    release {
    minifyEnabled true
    // Rules to be used during the AAR generation
    proguardFiles 'proguard-rules-for-building-library.pro'
    // Rules appended to the integrating app
    consumerProguardFiles 'proguard-rules-for-using-library.pro'
    }
    ...
    }

    View Slide

  23. Bundled Proguard Configs
    @nisrulz
    Fuel: https://github.com/kittinunf/Fuel

    View Slide

  24. Bundled Proguard Configs
    @nisrulz

    View Slide

  25. Bundled Proguard Configs
    @nisrulz
    # Fuel’s bundled Proguard file
    # Without specifically keeping this class,
    # callbacks on android don't function properly.
    -keep class com.github.kittinunf.fuel.android.util.AndroidEnvironment

    View Slide

  26. Bundled Proguard Configs
    @nisrulz
    # To check the merged configuration
    # Add the below to your current config
    -printconfiguration proguard-merged-config.txt

    View Slide

  27. Bundled Proguard Configs
    @nisrulz
    # DON'T DO THIS
    -dontobfuscate
    -optimizations !code/allocation/variable
    # Effectively no optimizations
    -keep public class * {
    public protected *;
    }

    View Slide

  28. Bundled Proguard Configs
    @nisrulz
    # DON'T DO THIS
    # Adding the below in library proguard rules disables
    # the optimizations in the Android app
    -dontoptimze

    View Slide

  29. Modularization
    @nisrulz

    View Slide

  30. Modularization
    @nisrulz

    View Slide

  31. Modularization
    @nisrulz
    EasyDeviceInfo: https://github.com/nisrulz/easydeviceinfo

    View Slide

  32. Modularization
    @nisrulz
    dependencies {
    def libVer = {latest_version}
    // Base + Ads Bundled Library
    implementation "com.github.nisrulz:easydeviceinfo:$libVer"
    // Base Library
    implementation "com.github.nisrulz:easydeviceinfo-base:$libVer"
    // Ads Library
    implementation "com.github.nisrulz:easydeviceinfo-ads:$libVer"
    }

    View Slide

  33. Modularization
    @nisrulz


    ...


    com.github.nisrulz
    easydeviceinfo-ads 2.4.1
    compile


    com.github.nisrulz
    easydeviceinfo-base 2.4.1
    compile

    ...


    View Slide

  34. Modularization
    @nisrulz


    ...


    com.github.nisrulz
    easydeviceinfo-ads 2.4.1
    compile


    com.github.nisrulz
    easydeviceinfo-base 2.4.1
    compile

    ...


    View Slide

  35. Modularization
    @nisrulz


    ...


    com.github.nisrulz
    easydeviceinfo-ads 2.4.1
    compile


    com.github.nisrulz
    easydeviceinfo-base 2.4.1
    compile

    ...


    View Slide

  36. Modularization
    @nisrulz


    ...


    com.github.nisrulz
    easydeviceinfo-common2.4.1
    compile


    com.google.android.gms
    play-services-basement11.8.0
    runtime

    ...


    View Slide

  37. Modularization
    @nisrulz


    ...


    com.github.nisrulz
    easydeviceinfo-common2.4.1
    compile


    com.google.android.gms
    play-services-basement11.8.0
    runtime

    ...


    View Slide

  38. Avoiding Resource Name
    Conflicts
    @nisrulz

    View Slide

  39. @nisrulz
    Conflict occurs between a library & app resource
    > Project will not compile
    What happens when..

    View Slide

  40. @nisrulz
    Conflict occurs between 2 libraries integrated in the app
    > Resources from library defined first in build.gradle gets
    included
    What happens when..

    View Slide

  41. @nisrulz
    Add a prefix to all your resources.
    Solution?

    View Slide

  42. @nisrulz
    Add a prefix to all your resources.
    Enforce this in Android Studio
    android {
    resourcePrefix 'YOUR_PREFIX_' // i.e 'sensey_'
    }
    Solution?

    View Slide

  43. @nisrulz
    Solution?
    Resource named ‘app_name’ does not start with the project’s
    resource prefix ‘sensey_’;
    Rename to `sensey_app_name`?

    View Slide

  44. minSdkVersion Restriction
    @nisrulz

    View Slide

  45. @nisrulz
    minSdkVersion of app >= minSdkVersion of library
    minSdk Restriction

    View Slide

  46. @nisrulz
    minSdk Restriction

    View Slide

  47. @nisrulz
    Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller
    than version 21 declared in library [:sensey]
    Suggestion:
    + Use a compatible library with a minSdk of at most 14
    + Increase this project's minSdk version to at least 21
    + Use tools:overrideLibrary="com.github.nisrulz.sensey" to force
    usage (may lead to runtime failures)
    minSdk Restriction

    View Slide

  48. @nisrulz
    Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller
    than version 21 declared in library [:sensey]
    Suggestion:
    + Use a compatible library with a minSdk of at most 14
    + Increase this project's minSdk version to at least 21
    + Use tools:overrideLibrary="com.github.nisrulz.sensey" to force
    usage (may lead to runtime failures)
    minSdk Restriction

    View Slide

  49. @nisrulz
    Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller
    than version 21 declared in library [:sensey]
    Suggestion:
    + Use a compatible library with a minSdk of at most 14
    + Increase this project's minSdk version to at least 21
    + Use tools:overrideLibrary="com.github.nisrulz.sensey" to force
    usage (may lead to runtime failures)
    minSdk Restriction

    View Slide

  50. @nisrulz
    xmlns:tools="http://schemas.android.com/tools"
    package="com.github.nisrulz.senseysample">


    ...


    minSdk Restriction

    View Slide

  51. @nisrulz
    xmlns:tools="http://schemas.android.com/tools"
    package="com.github.nisrulz.senseysample">


    ...


    minSdk Restriction

    View Slide

  52. Access Visibility
    vs
    Code Organization
    @nisrulz

    View Slide

  53. Visibility vs Organization
    @nisrulz
    + Code organized in individual packages; everything is public

    View Slide

  54. Visibility vs Organization
    @nisrulz
    + Code organized in individual packages; everything is public
    + Code organized inside one package; everything is package private
    and only public on demand

    View Slide

  55. Visibility vs Organization
    @nisrulz
    + Code organized in individual packages; everything is public
    + Code organized inside one package; everything is package private
    and only public on demand
    But if in Kotlin land,
    + Code organized in individual packages; everything is internal and
    only public on demand

    View Slide

  56. Visibility vs Organization
    @nisrulz
    // file name: exampleLibrary.kt
    // module name: example
    package com.example.library

    View Slide

  57. Visibility vs Organization
    @nisrulz
    // file name: exampleLibrary.kt
    // module name: example
    package com.example.library
    // visible inside exampleLibrary.kt
    private fun setup() { ... }

    View Slide

  58. Visibility vs Organization
    @nisrulz
    // file name: exampleLibrary.kt
    // module name: example
    package com.example.library
    // visible inside exampleLibrary.kt
    private fun setup() { ... }
    // property is visible everywhere
    public var name: String = "ExampleLib"
    // setter is visible only in exampleLibrary.kt
    private set{...}

    View Slide

  59. Visibility vs Organization
    @nisrulz
    // file name: exampleLibrary.kt
    // module name: example
    package com.example.library
    // visible inside exampleLibrary.kt
    private fun setup() { ... }
    // property is visible everywhere
    public var name: String = "ExampleLib"
    // setter is visible only in exampleLibrary.kt
    private set{...}
    // visible inside the module i.e example
    internal val debugTag = "Example-Debug"

    View Slide

  60. Lifecycle-Aware
    Android Library
    @nisrulz

    View Slide

  61. Lifecycle Components
    @nisrulz
    Classes designed to help deal with Android
    lifecycle
    + Lifecycle
    + LifecycleOwner
    + LifecycleObserver

    View Slide

  62. Lifecycle Components
    @nisrulz
    Classes designed to help deal with Android
    lifecycle

    View Slide

  63. Lifecycle Components
    @nisrulz
    Classes designed to help deal with Android
    lifecycle
    i.e Activity, Fragment, Service, Custom

    View Slide

  64. LifecycleObserver
    @nisrulz
    dependencies {
    def lifecycleVer = "1.1.1"
    // Runtime
    implementation "android.arch.lifecycle:runtime:$lifecycleVer"
    // Annotation Support
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycleVer"
    ...
    }

    View Slide

  65. LifecycleObserver
    @nisrulz
    object AwesomeLibrary : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun init() { ... }
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart() { ... }
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun cleanup() { ...}
    }

    View Slide

  66. LifecycleOwner
    @nisrulz
    class MainActivity : AppCompatActivity() { // LifecycleOwner
    override fun onResume() {
    // Add lifecycle observer
    lifecycle.addObserver(AwesomeLibrary.INSTANCE)
    }
    override fun onStop() {
    // Remove lifecycle observer
    lifecycle.removeObserver(AwesomeLibrary.INSTANCE)
    }
    }

    View Slide

  67. ProcessLifecycleOwner
    @nisrulz
    Class that tracks the lifecycle of
    whole application process

    View Slide

  68. ProcessLifecycleOwner
    @nisrulz
    dependencies {
    def lifecycleVer = "1.1.1"
    // For ProcessLifecycleOwner
    implementation "android.arch.lifecycle:extensions:$lifecycleVer"
    ...
    }

    View Slide

  69. ProcessLifecycleOwner
    @nisrulz
    extension artifact automatically adds
    element to your manifest

    View Slide

  70. ProcessLifecycleOwner
    @nisrulz


    ...
    android:name="android.arch.lifecycle.ProcessLifecycleOwnerInitializer"
    android:authorities="com.example.app.lifecycle-trojan"
    android:exported="false"
    android:multiprocess="true" />


    View Slide

  71. ProcessLifecycleOwner
    @nisrulz
    // Source code
    // Internal class to initialize Lifecycles.
    public class ProcessLifecycleOwnerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
    ...
    ProcessLifecycleOwner.init(getContext());
    return true;
    }
    ...
    }

    View Slide

  72. ProcessLifecycleOwner
    @nisrulz
    Initializes ProcessLifecycleOwner even if your app
    does not use it!
    Why?
    To invoke ProcessLifecycleOwner as soon as process
    starts

    View Slide

  73. Auto Initialize
    Android Library
    @nisrulz

    View Slide

  74. AutoInit Android Library
    @nisrulz
    Android Libraries need Android context to handle simple tasks such as
    + Hook into Android Runtime
    + Access app resources
    + Use System Services
    + Register BroadcastReceiver

    View Slide

  75. AutoInit Android Library
    @nisrulz
    public class MyApplication extends Application {
    @Override
    public void onCreate() {
    super.onCreate();
    // Init android library
    MyAwesomeLibrary.init(this);
    }
    }

    View Slide

  76. AutoInit Android Library
    @nisrulz
    public class MyApplication extends Application {
    @Override
    public void onCreate() {
    super.onCreate();
    // Init android library
    MyAwesomeLibrary.init(this);
    }
    }

    View Slide

  77. AutoInit Android Library
    @nisrulz
    public class MyApplication extends Application {
    @Override
    public void onCreate() {
    super.onCreate();
    // Init android library
    MyAwesomeLibrary.init(this);
    }
    }

    View Slide

  78. AutoInit Android Library
    @nisrulz
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:name=".MyApplication"
    ... >

    View Slide

  79. AutoInit Android Library
    @nisrulz
    ContentProvider “can be” used to simplify the process.
    How? ContentProvider is
    + Created and initialized (on the main thread) before
    all other components
    + Participate in manifest merging at build time.

    View Slide

  80. AutoInit Android Library
    @nisrulz
    public class AwesomeLibInitProvider extends ContentProvider {
    ...
    @Override
    public boolean onCreate() {
    // get the context (Application context)
    Context context = getContext();
    // initialize AwesomeLib here
    AwesomeLib.getInstance().init(context);
    return false;
    }
    ...
    }

    View Slide

  81. AutoInit Android Library
    @nisrulz
    public class AwesomeLibInitProvider extends ContentProvider {
    ...
    @Override
    public boolean onCreate() {
    // get the context (Application context)
    Context context = getContext();
    // initialize AwesomeLib here
    AwesomeLib.getInstance().init(context);
    return false;
    }
    ...
    }

    View Slide

  82. AutoInit Android Library
    @nisrulz
    package="github.nisrulz.sample.awesomelib">

    android:name=".AwesomeLibInitProvider"
    android:authorities="${applicationId}.awesomelibinitprovider"
    android:enabled="true"
    android:exported="false">


    View Slide

  83. AutoInit Android Library
    @nisrulz
    package="github.nisrulz.sample.awesomelib">

    android:name=".AwesomeLibInitProvider"
    android:authorities="${applicationId}.awesomelibinitprovider"
    android:enabled="true"
    android:exported="false">


    View Slide

  84. AutoInit Android Library
    @nisrulz
    package="github.nisrulz.sample.awesomelib">

    android:name=".AwesomeLibInitProvider"
    android:authorities="${applicationId}.awesomelibinitprovider"
    android:enabled="true"
    android:exported="false">


    View Slide

  85. AutoInit Android Library
    @nisrulz
    package="github.nisrulz.sample.awesomelib">

    android:name=".AwesomeLibInitProvider"
    android:authorities="${applicationId}.awesomelibinitprovider"
    android:enabled="true"
    android:exported="false">


    View Slide

  86. AutoInit Android Library
    @nisrulz
    ContentProvider “can be” used to simplify the process.
    Why it shouldn’t be
    + There can be only one Content Provider with a given
    “authority” string
    + Only run on main thread; does not support multi
    process

    View Slide

  87. Links/References
    @nisrulz
    Android Libraries I have built:
    https://github.com/nisrulz/nisrulz.github.io#open-source-co
    ntributions
    Auto initialize android library example:
    https://github.com/nisrulz/android-examples/tree/develop/Au
    toInitLibrary
    Lifecycle Aware android library example:
    https://github.com/nisrulz/android-examples/tree/develop/Li
    feCycleCompForLib

    View Slide

  88. twitter.com/nisrulz
    github.com/nisrulz
    www.nisrulz.com

    View Slide

  89. twitter.com/nisrulz
    github.com/nisrulz
    www.nisrulz.com

    View Slide