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

[Droidcon Berlin '17] Things I wish I knew when I started building Android sdk/libraries

[Droidcon Berlin '17] Things I wish I knew when I started building Android sdk/libraries

As more and more people start building Android Libraries, the whole process of building a better API for developers is getting bloated and everyone is coming up with their own ideas. However, if android library developers follow a certain standard with a rationale they can avoid most of the pitfalls. Building an android library is easy, but building one that keeps in mind developer happiness is rare but definitely not hard. The session would cover how one can build android libraries taking in consideration to exposing more informative API, making sure the best resources are utilized efficiently and that one does not force the app developer with redundant dependencies and complexities.

Animated Gif Version: https://goo.gl/WNeR4A

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

Link to blog post: https://android.jlelse.eu/things-i-wish-i-knew-when-i-started-building-android-sdk-libraries-dba1a524d619

Sketchnotes by:
Chiu-Ki Chan: https://twitter.com/chiuki/status/905046880723329024
Garima Jain: https://twitter.com/ragdroid/status/905053186318368770
Miriam Busch: https://twitter.com/miphoni/status/905174390312239105

Nishant Srivastava

September 05, 2017
Tweet

More Decks by Nishant Srivastava

Other Decks in Technology

Transcript

  1. Things I wish I knew when I
    started building android
    SDK/libraries
    Nishant Srivastava

    View Slide

  2. Nishant
    Srivastava
    @nisrulz

    View Slide

  3. @nisrulz
    #droidconDE
    Sensey
    https://github.com/nisrulz/sensey
    EasyDeviceInfo
    https://github.com/nisrulz/easydeviceinfo

    View Slide

  4. Wait...
    what is an Android Library?
    @nisrulz
    #droidconDE

    View Slide

  5. Android Library is...
    @nisrulz
    #droidconDE

    View Slide

  6. Android Library is...

    +

    +

    =
    @nisrulz
    #droidconDE

    View Slide

  7. Android Library is...
    Java code
    +

    +

    =
    @nisrulz
    #droidconDE

    View Slide

  8. Android Library is...
    Java code
    +
    Android resources
    +

    =
    @nisrulz
    #droidconDE

    View Slide

  9. Android Library is...
    Java code
    +
    Android resources
    +
    Android Manifest stub
    =
    @nisrulz
    #droidconDE

    View Slide

  10. Android Library is...
    Java code
    +
    Android resources
    +
    Android Manifest stub
    =
    Android ARchive
    (AAR)
    @nisrulz
    #droidconDE

    View Slide

  11. Why
    create an Android Library?
    @nisrulz
    #droidconDE

    View Slide

  12. Why create an Android Library?
    Short Answer
    @nisrulz
    #droidconDE

    View Slide

  13. Why create an Android Library?
    Short Answer
    You don’t have to…
    @nisrulz
    #droidconDE

    View Slide

  14. Why create an Android Library?
    Short Answer
    You don’t have to…
    ...unless a solution already exists.
    Then use that.
    @nisrulz
    #droidconDE

    View Slide

  15. Best Practices
    for building android libraries...
    ...that said, let us move on to
    @nisrulz
    #droidconDE

    View Slide

  16. Ease of use
    @nisrulz
    #droidconDE

    View Slide

  17. Ease of use
    Intuitive
    @nisrulz
    #droidconDE

    View Slide

  18. Ease of use
    Intuitive
    Consistent
    @nisrulz
    #droidconDE

    View Slide

  19. Ease of use
    Intuitive
    Consistent
    Easy to use, Hard to misuse
    @nisrulz
    #droidconDE

    View Slide

  20. Avoid multiple arguments
    // Do not DO this
    void init(String apikey, int refresh, long interval, String type,
    String username, String email, String password);
    @nisrulz
    #droidconDE

    View Slide

  21. Avoid multiple arguments
    // Do not DO this
    void init(String apikey, int refresh, long interval, String type,
    String username, String email, String password);
    // WHY?
    void init(“0123456789”,“prod”, 1000, 1, “nishant”, ”1234”,
    [email protected]”);
    // Passing arguments in right order is easy to mess up here :(
    @nisrulz
    #droidconDE

    View Slide

  22. Avoid multiple arguments
    // Do this
    void init(ApiSecret apisecret);
    @nisrulz
    #droidconDE

    View Slide

  23. Avoid multiple arguments
    // Do this
    void init(ApiSecret apisecret);
    // where ApiSecret is
    public class ApiSecret{
    String apikey; int refresh;
    long interval; String type;
    String name; String email; String pass;
    // constructor
    // validation checks(such as type safety)
    // setter and getters
    }
    @nisrulz
    #droidconDE

    View Slide

  24. Avoid multiple arguments
    // Or use Builder Pattern
    AwesomeLib awesomelib = new
    AwesomeLib.AwesomeLibBuilder()
    .apisecret(mApisecret).refresh(mRefresh)
    .interval(mInterval).type(mType)
    .username(mUsername).email(mEmail).password(mPassword)
    .build();
    }
    @nisrulz
    #droidconDE

    View Slide

  25. Minimize Permissions
    @nisrulz
    #droidconDE

    View Slide

  26. Minimize Permissions
    Use intents
    @nisrulz
    #droidconDE

    View Slide

  27. Minimize Permissions
    Use intents
    Reduce req. permissions
    @nisrulz
    #droidconDE

    View Slide

  28. Minimize Permissions
    Use intents
    Reduce req. permissions
    Check permission, use fallbacks
    @nisrulz
    #droidconDE

    View Slide

  29. Minimize Permissions
    Use intents
    Reduce req. permissions
    Check permission, use fallbacks
    public boolean hasPermission(Context context, String permission) {
    int result = context.checkCallingOrSelfPermission(permission);
    return result == PackageManager.PERMISSION_GRANTED;
    }
    @nisrulz
    #droidconDE

    View Slide

  30. Minimize Feature Requisite
    // Do not do this

    @nisrulz
    #droidconDE

    View Slide

  31. Minimize Feature Requisite
    // Do not do this

    // Runtime feature detection
    String feature = PackageManager.FEATURE_BLUETOOTH;
    public boolean isFeatureAvailable(Context context, String feature) {
    return context.getPackageManager().hasSystemFeature(feature);
    }
    // Enable/Disable the functionality depending on availability of feature
    @nisrulz
    #droidconDE

    View Slide

  32. Support different versions
    // RULE OF THUMB : Support the full spectrum of android versions
    @nisrulz
    #droidconDE

    View Slide

  33. Support different versions
    // RULE OF THUMB : Support the full spectrum of android versions
    android {
    ...
    defaultConfig {
    ..
    minSdkVersion 9
    targetSdkVersion 26
    ..
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  34. Support different versions
    // How?? : Detect version and Enable/Disable features or use
    // a fallback
    @nisrulz
    #droidconDE

    View Slide

  35. Support different versions
    // How?? : Detect version and Enable/Disable features or use
    // a fallback
    public boolean isOreoAndAbove(){
    return Build.VERSION.SDK_INT>= Build.VERSION_CODES.O;
    }
    @nisrulz
    #droidconDE

    View Slide

  36. Don’t log in production
    @nisrulz
    #droidconDE

    View Slide

  37. Don’t log in production
    ...but log exceptions/errors
    @nisrulz
    #droidconDE

    View Slide

  38. Don’t log in production
    ...but log exceptions/errors
    Provide flagged logging support
    AwesomeLibrary.init(apisecret,BuildConfig.DEBUG);
    @nisrulz
    #droidconDE

    View Slide

  39. Don’t log in production
    // Provide flexibility to log when debug flag is true
    void init(ApiSecret apisecret,boolean debuggable){
    try{
    ...
    } catch(Exception ex){
    if (debuggable){
    // This is printed only when debuggable is true
    ex.printStackTrace();
    }
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  40. Don’t log in production
    // Provide flexibility to log when debug flag is true
    void init(ApiSecret apisecret,boolean debuggable){
    try{
    ...
    } catch(Exception ex){
    if (debuggable){
    // This is printed only when debuggable is true
    ex.printStackTrace();
    }
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  41. Degrade gracefully on error
    @nisrulz
    #droidconDE

    View Slide

  42. Degrade gracefully on error
    Do not crash the app
    @nisrulz
    #droidconDE

    View Slide

  43. Degrade gracefully on error
    Do not crash the app
    Disable feature/functionality
    @nisrulz
    #droidconDE

    View Slide

  44. Catch specific exception
    // Do not do this
    try {
    // ...
    } catch(Exception e) {
    // ...
    }
    @nisrulz
    #droidconDE

    View Slide

  45. Catch specific exception
    // Do this
    try {
    // ...
    } catch(IOException e) {
    // ...
    }
    @nisrulz
    #droidconDE

    View Slide

  46. Handle poor network conditions
    @nisrulz
    #droidconDE

    View Slide

  47. Handle poor network conditions
    Acknowledge unreliable network exists
    @nisrulz
    #droidconDE

    View Slide

  48. Handle poor network conditions
    Acknowledge unreliable network exists
    Batch network calls
    @nisrulz
    #droidconDE

    View Slide

  49. Handle poor network conditions
    Acknowledge unreliable network exists
    Batch network calls
    Prefetch data ahead of time
    @nisrulz
    #droidconDE

    View Slide

  50. Handle poor network conditions
    Acknowledge unreliable network exists
    Batch network calls
    Prefetch data ahead of time
    Use better data structures such as flatbuffers
    @nisrulz
    #droidconDE

    View Slide

  51. Implementation/api VS compile
    Before Gradle 4.0
    compile
    Gradle 4.0+ onwards
    implementation
    api
    @nisrulz
    #droidconDE

    View Slide

  52. Require min dependencies
    Keep a check on method count
    http://www.methodscount.com/plugins
    @nisrulz
    #droidconDE

    View Slide

  53. Require min dependencies
    Let the app developer make the decision about inclusion
    @nisrulz
    #droidconDE

    View Slide

  54. Require min dependencies
    Let the app developer make the decision about inclusion
    AwesomeLib module:
    dependencies {
    // Gradle v2.12 and below
    provided 'com.squareup.retrofit2:retrofit:2.1.0'
    // Gradle v2.12+
    compileOnly 'com.squareup.retrofit2:retrofit:2.1.0'
    }
    @nisrulz
    #droidconDE

    View Slide

  55. Require min dependencies
    Let the app developer make the decision about inclusion
    AwesomeLib module:
    dependencies {
    // Gradle v2.12 and below
    provided 'com.squareup.retrofit2:retrofit:2.1.0'
    // Gradle v2.12+
    compileOnly 'com.squareup.retrofit2:retrofit:2.1.0'
    }
    @nisrulz
    #droidconDE

    View Slide

  56. Require min dependencies
    Let the app developer make the decision about inclusion
    App module:
    dependencies {
    // ...
    implementation 'com.squareup.retrofit2:retrofit:2.1.0'
    }
    @nisrulz
    #droidconDE

    View Slide

  57. Require min dependencies
    Enable/Disable feature based on availability
    private boolean hasRetrofitOnClasspath() {
    try {
    Class.forName("retrofit2.Retrofit");
    return true;
    } catch (ClassNotFoundException ex) {
    ex.printStackTrace();
    }
    return false;
    }
    @nisrulz
    #droidconDE

    View Slide

  58. Require min dependencies
    Enable/Disable feature based on availability
    private boolean hasRetrofitOnClasspath() {
    try {
    Class.forName("retrofit2.Retrofit");
    return true;
    } catch (ClassNotFoundException ex) {
    ex.printStackTrace();
    }
    return false;
    }
    @nisrulz
    #droidconDE

    View Slide

  59. Lifecycle Arch Components
    @nisrulz
    #droidconDE

    View Slide

  60. Lifecycle Arch Components
    public class AwesomeLib implements LifecycleObserver{
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void executeFnOnCreate(){
    // do something on create
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void cleanup(){
    // do some cleanup tasks
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  61. Lifecycle Arch Components
    public class AwesomeLib implements LifecycleObserver{
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void executeFnOnCreate(){
    // do something on create
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void cleanup(){
    // do some cleanup tasks
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  62. Lifecycle Arch Components
    public class MainActivity extends LifecycleActivity {
    AwesomeLib awesomeLib;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    // ..
    awesomeLib = new AwesomeLib();
    getLifecycle().addObserver(awesomeLib);
    }
    @Override
    protected void onDestroy() {
    // ...
    getLifecycle().removeObserver(awesomeLib);
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  63. Lifecycle Arch Components
    public class MainActivity extends LifecycleActivity {
    AwesomeLib awesomeLib;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    // ..
    awesomeLib = new AwesomeLib();
    getLifecycle().addObserver(awesomeLib);
    }
    @Override
    protected void onDestroy() {
    // ...
    getLifecycle().removeObserver(awesomeLib);
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  64. Do not hog the startup
    @nisrulz
    #droidconDE

    View Slide

  65. Do not hog the startup
    @nisrulz
    #droidconDE

    View Slide

  66. Do not hog the startup
    You do not need to
    initialize everything,
    @nisrulz
    #droidconDE

    View Slide

  67. Do not hog the startup
    You do not need to
    initialize everything,
    Try to do lazy
    initialization
    @nisrulz
    #droidconDE

    View Slide

  68. Remove functionality gracefully
    /**
    * @deprecated As of release 2.0, replaced by {@link #getPreferredSize()}
    */
    @Deprecated
    public Dimension preferredSize() {
    return getPreferredSize();
    }
    @nisrulz
    #droidconDE

    View Slide

  69. Document everything
    Provide Readme.md
    @nisrulz
    #droidconDE

    View Slide

  70. Document everything
    Provide Readme.md
    Include javadocs in code
    @nisrulz
    #droidconDE

    View Slide

  71. Document everything
    Provide Readme.md
    Include javadocs in code
    Bundle a sample app
    @nisrulz
    #droidconDE

    View Slide

  72. Document everything
    Provide Readme.md
    Include javadocs in code
    Bundle a sample app
    Maintain a changelog
    http://keepachangelog.com
    @nisrulz
    #droidconDE

    View Slide

  73. Document everything
    @nisrulz
    #droidconDE

    View Slide

  74. License is Important
    https://choosealicense.com/
    @nisrulz
    #droidconDE

    View Slide

  75. License is Important
    /*
    * Copyright (C) 2016 Nishant Srivastava
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    @nisrulz
    #droidconDE

    View Slide

  76. Versioning Strategy
    Define a versioning scheme i.e
    MAJOR.MINOR.PATCH
    http://semver.org/
    @nisrulz
    #droidconDE

    View Slide

  77. Versioning Strategy
    To make 2 versions coexist, release under a new
    group ID
    @nisrulz
    #droidconDE

    View Slide

  78. Versioning Strategy
    To make 2 versions coexist, release under a new
    group ID i.e
    1.x = io.reactivex:rxjava:x.y.z
    2.x = io.reactivex.rxjava2:rxjava:2.x.y
    @nisrulz
    #droidconDE

    View Slide

  79. Proguard, wait… what?
    @nisrulz
    #droidconDE

    View Slide

  80. Proguard, wait… what?
    Proguard rules can be shipped in an android library
    @nisrulz
    #droidconDE

    View Slide

  81. Proguard, wait… what?
    Proguard rules can be shipped in an android library
    android {
    ...
    defaultConfig {
    ...
    versionName "1.0.0"
    ...
    consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    }
    @nisrulz
    #droidconDE

    View Slide

  82. Proguard, wait… what?
    @nisrulz
    #droidconDE

    View Slide

  83. Proguard, wait… what?
    @nisrulz
    #droidconDE

    View Slide

  84. Proguard, wait… what?
    @nisrulz
    #droidconDE

    View Slide

  85. References
    BlogPost:https://android.jlelse.eu/things-i-wish-i-knew-when-i-star
    ted-building-android-sdk-libraries-dba1a524d619
    My open sourced android
    libraries:https://github.com/nisrulz/nisrulz.github.io#open-source-
    contributions
    Sensey:https://github.com/nisrulz/sensey
    EasyDeviceInfo:https://github.com/nisrulz/easydeviceinfo

    View Slide

  86. Credits
    Emoji Icons designed by Freepik from Flaticon

    View Slide

  87. Thank You
    Q & A
    twitter.com/ nisrulz
    github.com/ nisrulz
    .com

    View Slide

  88. Things I wish I knew when I
    started building android
    SDK/libraries
    twitter.com/ nisrulz
    github.com/ nisrulz
    .com

    View Slide