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

Your Android Open Source Library

Sangsoo Nam
September 04, 2017

Your Android Open Source Library

Open source libraries make the development much easier. You don't have to spend your time for making similar things. If you cannot find what you want, now it's time to make it and release to everyone.

In this talk, I explain how to make an open source library including hosting, testing, continuous integration, and publishing for both types: jar(java) and aar(android)

Sangsoo Nam

September 04, 2017
Tweet

More Decks by Sangsoo Nam

Other Decks in Technology

Transcript

  1. Open Source Show your work Teach others Lower bugs It’s

    fun! Great advertising Upstream improvement
  2. 6

  3. { "items": [ ... { "uri": "spotify:track:7MHN1aCFtLXjownGhvEQlF", "attributes": { "format_attributes":

    [ { "key": "status", "value": "UP" }, ... ] } }, { "uri": "spotify:track:7nKBxz47S9SD79N086fuhn", "attributes": { "format_attributes": [ { "key": "status", "value": "DOWN" }, ... ] } }, { "uri": "spotify:track:3rOSwuTsUlJp0Pu0MkN8r8", "attributes": { "format_attributes": [ { "key": "status", "value": "EQUAL" }, ... ] ≈ ≈ ≈
  4. if ("UP".equals(status)) { showIcon(iconUp); } else if("DOWN".equals(status)) { showIcon(iconDown); }

    else if("EQUAL".equals(status)) { showIcon(iconEqual); } • Every time string comparison with “equals” method • Typo error (e.g Upper or lower case)
  5. if (Status.UP == status) { showIcon(iconUp); } else if(Status.DOWN ==

    status) { showIcon(iconDown); } else if(Status.EQUAL == status) { showIcon(iconEqual); } • No need to compare strings
  6. EnumParser public enum Status { UP, DOWN, EQUAL; } @Test

    public void testEnumParser() { assertThat(EnumParser.forClass(Status.class).parse("UP")).isSameAs(Status.UP); assertThat(EnumParser.forClass(Status.class).parse("up")).isSameAs(Status.UP); assertThat(EnumParser.forClass(Status.class).parse("uP")).isSameAs(Status.UP); assertThat(EnumParser.forClass(Status.class).parse("DOWN")).isSameAs(Status.DOWN); assertThat(EnumParser.forClass(Status.class).parse("EQUAL")).isSameAs(Status.EQUAL); }
  7. EnumParser @Test public void testEnumParser() { assertThat(EnumParser.forClass(Status.class).parse("ALIEN")).isNull(); } @Test(expected =

    EnumConstantNotPresentException.class) public void testEnumParserStrict() { EnumParser.forClass(Status.class).parseStrict("ALIEN") } @Test public void testEnumParserVariation() { EnumParser<Mode> enumParser = EnumParser.forClass(Mode.class).withVariation( (mode) -> mode.name().replaceAll("_", "-") ); assertThat(enumParser.parse("SHUFFLE_PLAY")).isSameAs(Mode.SHUFFLE_PLAY); assertThat(enumParser.parse("SHUFFLE-PLAY")).isSameAs(Mode.SHUFFLE_PLAY); }
  8. EnumParser @Test public void testEnumParser() { assertThat(EnumParser.forClass(Status.class).parse("ALIEN")).isNull(); } @Test(expected =

    EnumConstantNotPresentException.class) public void testEnumParserStrict() { EnumParser.forClass(Status.class).parseStrict("ALIEN") } @Test public void testEnumParserVariation() { EnumParser<Mode> enumParser = EnumParser.forClass(Mode.class).withVariation( (mode) -> mode.name().replaceAll("_", "-") ); assertThat(enumParser.parse("SHUFFLE_PLAY")).isSameAs(Mode.SHUFFLE_PLAY); assertThat(enumParser.parse("SHUFFLE-PLAY")).isSameAs(Mode.SHUFFLE_PLAY); }
  9. Think Avoid comparing strings every time 
 for JSON data

    Build EnumParser: 
 Case insensitive parser 
 from string to Enum Ship
  10. MIT Apache 2.0 GPLv3 Permissions • Commercial use • Distribution

    • Modification • Private use • Commercial use • Distribution • Modification • Private use • Patent use • Commercial use • Distribution • Modification • Private use • Patent use Conditions • License and copyright notice • License and copyright notice • State changes • License and copyright notice • State changes • Disclose source • Same license Limitations • Liability • Warranty • Liability • Warranty • Trademark use • Liability • Warranty Weaker Stronger https:/ /choosealicense.com/licenses/
  11. Document What is this project? How to use it? Is

    it open source? Which license? How can I contribute?
  12. CI Badges Make sure every thing works after any change.

    Show your project is stable and well tested.
  13. build.gradle plugins { id 'jacoco' id 'com.github.kt3k.coveralls' version '2.8.1' }

    … jacocoTestReport { reports { xml.enabled = true // coveralls plugin depends on xml format report html.enabled = true } }
  14. Library Repository compile ’xxx:xxx:0.1.0’ JFrog Bintray 
 jcenter Icons made

    by Freepik from www.flaticon.com is licensed by CC 3.0 BY
  15. Library Repository compile ’xxx:xxx:0.1.0’ Sonatype
 Maven Central JFrog Bintray 


    jcenter Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
  16. Library Repository compile ’xxx:xxx:0.1.0’ Sonatype
 Maven Central JFrog Bintray 


    jcenter Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
  17. jcenter Maven repository hosted by JFrog Bintray. Deliver library though

    CDN. Largest Java Repository. Support easy way to upload
 Maven Central
  18. Maven A project management and comprehension tool providing a way

    to help with managing: • Builds • Dependencies • Releases • Distributions
 group_id:artifact_id:version POM(Project Object Model) file describes the project. 'com.google.dagger:dagger:2.11'
  19. build.gralde apply plugin: 'maven-publish' ... ext { PUBLISH_GROUP_ID = 'io.github.sangsoonam'

    PUBLISH_VERSION = '0.1.0' } 
 publishing { publications { MyPublication(MavenPublication) { from components.java groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION } } }
  20. Command ~/enumparser> ./gradlew publishToMavenLocal :enumparser:generatePomFileForMyPublicationPublication :enumparser:compileJava UP-TO-DATE :enumparser:processResources UP-TO-DATE :enumparser:classes

    UP-TO-DATE :enumparser:jar :enumparser:publishMyPublicationPublicationToMavenLocal :enumparser:publishToMavenLocal BUILD SUCCESSFUL Total time: 5.307 secs ~/enumparser> ls -al ~/.m2/repository/io/github/sangsoonam/enumparser/0.1.0 total 16 drwxr-xr-x 4 sangsoo staff 136 Aug 8 21:36 . drwxr-xr-x 5 sangsoo staff 170 Aug 8 21:36 .. -rw-r--r-- 1 sangsoo staff 2174 Aug 8 21:37 enumparser-0.1.0.jar -rw-r--r-- 1 sangsoo staff 613 Aug 8 21:37 enumparser-0.1.0.pom
  21. pom.xml ~/enumparser> cat ~/.m2/repository/io/github/sangsoonam/enumparser/0.1.0/enumparser-0.1.0.pom <?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/

    maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>io.github.sangsoonam</groupId> <artifactId>enumparser</artifactId> <version>0.1.0</version> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>11.0.1</version> <scope>runtime</scope> </dependency> </dependencies> </project>
  22. build.gralde plugins { id "com.jfrog.bintray" version "1.7" } ... bintray

    { user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_API_KEY') publications = ['MyPublication'] publish = true pkg { repo = PUBLISH_ARTIFACT_ID name = PUBLISH_ARTIFACT_ID version { name = 'v' + PUBLISH_VERSION released = new Date() } } }
  23. Command ~/enumparser> ./gradlew bintrayUpload :enumparser:generatePomFileForMyPublicationPublication :enumparser:compileJava UP-TO-DATE :enumparser:processResources UP-TO-DATE :enumparser:classes

    UP-TO-DATE :enumparser:jar UP-TO-DATE :enumparser:javadoc UP-TO-DATE :enumparser:javadocJar UP-TO-DATE :enumparser:sourceJar UP-TO-DATE :enumparser:publishMyPublicationPublicationToMavenLocal :enumparser:bintrayUpload BUILD SUCCESSFUL Total time: 6.62 secs
  24. !

  25. Maven Central Maven repository hosted by Sonatype. Well known. Quite

    hard to start but 
 have good guidelines for releasing.
  26. Artifact Repository Maven Release .jar + pom.xml JFrog Bintray 


    jcenter Your Bintray Upload Sync Sonatype
 Maven Central Sync
  27. Maven Central Requirements Have source and javadoc artifacts. POM(Project Object

    Model) should have enough information for the project. • Project Name • Project Description • Project URL • License • SCM URL • Developer information Artifacts should be signed for validation.
  28. pom.xml ~/enumparser> cat ~/.m2/repository/io/github/sangsoonam/enumparser/0.1.0/enumparser-0.1.0.pom <?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/

    maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>io.github.sangsoonam</groupId> <artifactId>enumparser</artifactId> <version>0.1.0</version> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>11.0.1</version> <scope>runtime</scope> </dependency> </dependencies> </project>
  29. build.gradle task javadocJar (type: Jar, dependsOn: javadoc) { classifier "sources"

    from javadoc.destinationDir } task sourceJar(type: Jar) { classifier "javadoc" from sourceSets.main.allJava } publishing { publications { MyPublication(MavenPublication) { from components.java groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact sourceJar artifact javadocJar } } }
  30. build.gradle task javadocJar (type: Jar, dependsOn: javadoc) { classifier "sources"

    from javadoc.destinationDir } task sourceJar(type: Jar) { classifier "javadoc" from sourceSets.main.allJava } publishing { publications { MyPublication(MavenPublication) { from components.java groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact sourceJar artifact javadocJar } } } ≈
  31. build.gradle publishing { publications { MyPublication(MavenPublication) { from components.java groupId

    = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact sourceJar artifact javadocJar pom.withXml { asNode().children().last() + pomConfig } } } }
  32. build.gradle publishing { publications { MyPublication(MavenPublication) { from components.java groupId

    = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact sourceJar artifact javadocJar pom.withXml { asNode().children().last() + pomConfig } } } } ≈
  33. build.gradle publishing { publications { MyPublication(MavenPublication) { from components.java groupId

    = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact sourceJar artifact javadocJar pom.withXml { asNode().children().last() + pomConfig } } } } ≈ def pomConfig = { description(PUBLISH_DESCRIPTION){} name PUBLISH_ARTIFACT_ID url GITHUB_URL licenses { license { name 'MIT License' url ‘http://www.opensource.org/...' } } scm { url GITHUB_URL } developers { developer { name DEVELOPER_NAME } } }
  34. pom.xml ~/enumparser> cat ~/.m2/repository/io/github/sangsoonam/enumparser/0.1.0/enumparser-0.1.0.pom <?xml version="1.0" encoding="UTF-8"?> ... <description>Utility for

    parsing a string to Enum constant.</description> <name>enumparser</name> <url>https://github.com/SangsooNam/enumparser</url> <licenses> <license> <name>MIT License</name> <url>http://www.opensource.org/licenses/mit-license.php</url> </license> </licenses> <scm> <url>https://github.com/SangsooNam/enumparser</url> </scm> <developers> <developer> <name>Sangsoo Nam</name> </developer> </developers> </project>
  35. build.gralde plugins { id "com.jfrog.bintray" version "1.7" } bintray {

    user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_API_KEY') publications = ['MyPublication'] publish = true pkg { repo = PUBLISH_ARTIFACT_ID name = PUBLISH_ARTIFACT_ID version { name = 'v' + PUBLISH_VERSION released = new Date() mavenCentralSync { user = System.getenv('OSS_USER') password = System.getenv('OSS_PASSWORD') } } } }
  36. build.gralde plugins { id "com.jfrog.bintray" version "1.7" } bintray {

    user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_API_KEY') publications = ['MyPublication'] publish = true pkg { repo = PUBLISH_ARTIFACT_ID name = PUBLISH_ARTIFACT_ID version { name = 'v' + PUBLISH_VERSION released = new Date() mavenCentralSync { user = System.getenv('OSS_USER') password = System.getenv('OSS_PASSWORD') } } } } ≈
  37. Command ~/enumparser> ./gradlew bintrayUpload :enumparser:generatePomFileForMyPublicationPublication :enumparser:compileJava UP-TO-DATE :enumparser:processResources UP-TO-DATE :enumparser:classes

    UP-TO-DATE :enumparser:jar UP-TO-DATE :enumparser:javadoc UP-TO-DATE :enumparser:javadocJar UP-TO-DATE :enumparser:sourceJar UP-TO-DATE :enumparser:publishMyPublicationPublicationToMavenLocal :enumparser:bintrayUpload BUILD SUCCESSFUL Total time: 6.62 secs
  38. CI Badges Make sure every thing works after any change.

    Show your project is stable and well tested.
  39. .travis.yml language: android jdk: oraclejdk8 android: components: - tools -

    platform-tools - build-tools-25.0.1 - android-25 - extra-android-m2repository
  40. build.gradle buildscript { dependencies { classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1' } } plugins

    { id 'jacoco-android' id 'com.github.kt3k.coveralls' version '2.5.0-x' } android { ... testOptions { unitTests.all { jacoco { // To include Robolectric tests includeNoLocationClasses = true } } } } coveralls { jacocoReportPath = "${buildDir}/reports/jacoco/jacocoTestDebugUnitTestReport/jacocoTestDebugUnitTestReport.xml" }
  41. Artifact Repository compile ’xxx:xxx:0.1.0’ Sonatype
 Maven Central JFrog Bintray 


    jcenter Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
  42. Artifact Repository compile ’xxx:xxx:0.1.0’ Sonatype
 Maven Central JFrog Bintray 


    jcenter Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
  43. build.gralde apply plugin: 'maven-publish' ext { PUBLISH_GROUP_ID = 'io.github.sangsoonam' PUBLISH_VERSION

    = '0.1.1' } publishing { publications { MyPublication(MavenPublication) { groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar" } } } ≈
  44. Command ~/duotoneimageview> ./gradlew publishToMavenLocal :duotoneimageview:generatePomFileForMyPublicationPublication :duotoneimageview:javadoc :duotoneimageview:javadocJar :duotoneimageview:sourceJar :duotoneimageview:publishMyPublicationPublicationToMavenLocal :duotoneimageview:publishToMavenLocal

    BUILD SUCCESSFUL Total time: 7.791 secs ~/duotoneimageview> ls -al ~/.m2/repository/io/github/sangsoonam/duotoneimageview/0.1.0 total 96 drwxr-xr-x 6 sangsoo staff 204 Aug 9 09:51 . drwxr-xr-x 4 sangsoo staff 136 Aug 9 09:51 .. -rw-r--r-- 1 sangsoo staff 16749 Aug 9 08:43 duotoneimageview-0.1.0.aar -rw-r--r-- 1 sangsoo staff 925 Aug 9 09:51 duotoneimageview-0.1.0.pom
  45. build.gralde plugins { id "com.jfrog.bintray" version "1.7" } ... bintray

    { user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_API_KEY') publications = ['MyPublication'] publish = true pkg { repo = PUBLISH_ARTIFACT_ID name = PUBLISH_ARTIFACT_ID version { name = PUBLISH_VERSION released = new Date() } } }
  46. Command ~/duotoneimageview> ./gradlew bintrayUpload :duotoneimageview:generatePomFileForMyPublicationPublication :duotoneimageview:javadoc UP-TO-DATE :duotoneimageview:javadocJar UP-TO-DATE :duotoneimageview:sourceJar

    UP-TO-DATE :duotoneimageview:publishMyPublicationPublicationToMavenLocal :duotoneimageview:bintrayUpload BUILD SUCCESSFUL Total time: 9.842 secs
  47. Maven Central Requirements Have source and javadoc artifacts. POM(Project Object

    Model) should have enough information for the project. • Project Name • Project Description • Project URL • License • SCM URL • Developer information Artifacts should be signed for validation.
  48. build.gradle task sourceJar(type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs

    } task javadoc(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } publishing { publications { MyPublication(MavenPublication) { groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar" artifact sourceJar artifact javadocJar } } }
  49. build.gradle task sourceJar(type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs

    } task javadoc(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } publishing { publications { MyPublication(MavenPublication) { groupId = PUBLISH_GROUP_ID version = PUBLISH_VERSION artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar" artifact sourceJar artifact javadocJar } } } ≈
  50. build.gradle publishing { publications { MyPublication(MavenPublication) { groupId = PUBLISH_GROUP_ID

    version = PUBLISH_VERSION ... artifact sourceJar artifact javadocJar pom.withXml { asNode().children().last() + pomConfig } } } } ≈ def pomConfig = { description(PUBLISH_DESCRIPTION){} name PUBLISH_ARTIFACT_ID url GITHUB_URL licenses { license { name 'MIT License' url ‘http://www.opensource.org/...' } } scm { url GITHUB_URL } developers { developer { name DEVELOPER_NAME } } }
  51. build.gralde plugins { id "com.jfrog.bintray" version "1.7" } bintray {

    user = System.getenv('BINTRAY_USER') key = System.getenv('BINTRAY_API_KEY') publications = ['MyPublication'] publish = true pkg { repo = PUBLISH_ARTIFACT_ID name = PUBLISH_ARTIFACT_ID version { name = PUBLISH_VERSION released = new Date() mavenCentralSync { user = System.getenv('OSS_USER') password = System.getenv('OSS_PASSWORD') } } } } ≈
  52. Command ~/duotoneimageview> ./gradlew bintrayUpload :duotoneimageview:generatePomFileForMyPublicationPublication :duotoneimageview:javadoc UP-TO-DATE :duotoneimageview:javadocJar UP-TO-DATE :duotoneimageview:sourceJar

    UP-TO-DATE :duotoneimageview:publishMyPublicationPublicationToMavenLocal :duotoneimageview:bintrayUpload BUILD SUCCESSFUL Total time: 9.842 secs
  53. Summary • Your code is valuable for other developers. Open

    your code. • Don’t forget the license • Document, document and document. • Continuous Integration: building and unit testing. • Release your library: jcenter or Maven central • Tweak if you have requests or pull request. Lib App Lib Lib