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

Building a Better Codebase with Lint - Droidcon Americas

Building a Better Codebase with Lint - Droidcon Americas

53b72671be580e70c9795c7eaf35ac12?s=128

Subhrajyoti Sen

November 17, 2020
Tweet

Transcript

  1. KeepTruckin Subhrajyoti Sen Droidcon Americas 2020 Building a Better Codebase

    with Lint
  2. What is Lint? < / >

  3. What is Lint? • Static analysis tool

  4. What is Lint? • Static analysis tool • Open Source

  5. What is Lint? • Static analysis tool • Open Source

    • Works on .java, .kt, .gradle and a lot more
  6. What is Lint? • Static analysis tool • Open Source

    • Works on .java, .kt, .gradle and a lot more • Can be used outside Android as well
  7. What is Lint? • Static analysis tool • Open Source

    • Works on .java, .kt, .gradle and a lot more • Can be used outside Android as well • Can be extended to write custom rules
  8. What is Lint? • Static analysis tool • Open Source

    • Works on .java, .kt, .gradle and a lot more • Can be used outside Android as well • Can be extended to write custom rules • Can be run from both Android Studio and CLI
  9. What does Lint look like? < / >

  10. Internationalization

  11. Security

  12. Performance

  13. Accessibility

  14. Why consider Lint? < / >

  15. Why consider Lint? • Opinionated codebase

  16. Why consider Lint? • Opinionated codebase • Simpler code reviews

  17. Why consider Lint? • Opinionated codebase • Simpler code reviews

    • Adherence to style guide
  18. Setup < / >

  19. Create a new module

  20. Dependencies dependencies { compileOnly "org.jetbrains.kotlin:kotlin stdlib:$kotlin_version" compileOnly 'com.android.tools.lint:lint api:27.1.1' compileOnly

    'com.android.tools.lint:lint checks:27.1.1' }
  21. Issue Registry class Registry : IssueRegistry() { override val api:

    Int = CURRENT_API @get:NotNull override val issues: List<Issue> get() = listOf() }
  22. Register Issue Registry jar { manifest { attributes 'Lint-Registry-V2': 'me.subhrajyoti.lint.Registry'

    } }
  23. Register Issue Registry dependencies { lintChecks project(':lint') }

  24. The Basic Elements Scope The kind of files the lint

    rule applies to • JAVA_FILE_SCOPE • GRADLE_SCOPE • MANIFEST_SCOPE • PROGUARD_SCOPE • RESOURCE_FILE_SCOPE
  25. The Basic Elements Severity How severe is the issue •

    INFORMATIONAL • WARNING • ERROR • FATAL
  26. The Basic Elements Category The category the issue falls in,

    from a pre-defined list of categories • SECURITY • PERFORMANCE • L18N • A11Y
  27. Use case - TextView

  28. Custom Rule Structure val ISSUE: Issue = Issue.create( "MissingStyleAttribute", "style

    attribute is missing", "We should use style to style a TextView " + "in order to provide consistent design", Category.CORRECTNESS, 5, Severity.ERROR, Implementation( MissingStyleAttributeDetector::class.java, Scope.RESOURCE_FILE_SCOPE ) )
  29. Custom Rule Structure class MissingStyleAttributeDetector : ResourceXmlDetector() { }

  30. Custom Rule Structure class MissingStyleAttributeDetector : ResourceXmlDetector() { @Nullable override

    fun getApplicableElements(): Collection<String>? { return listOf("TextView") } }
  31. Custom Rule Structure class MissingStyleAttributeDetector : ResourceXmlDetector() { @Nullable override

    fun getApplicableElements(): Collection<String>? { return listOf(SdkConstants.TEXT_VIEW) } }
  32. Custom Rule Structure class MissingStyleAttributeDetector : ResourceXmlDetector() { //.. override

    fun appliesTo(folderType: ResourceFolderType): Boolean { return folderType == ResourceFolderType.LAYOUT } }
  33. Custom Rule Structure class MissingStyleAttributeDetector : ResourceXmlDetector() { //.. override

    fun visitElement(context: XmlContext, element: Element) { if (!element.hasAttribute(SdkConstants.ATTR_STYLE)) { context.report( ISSUE, element, context.getLocation(element), "Add style to TextView in order to provide consistent design" ) } } }
  34. Remember this? class MissingStyleAttributeDetector : ResourceXmlDetector() { @Nullable override fun

    getApplicableElements(): Collection<String>? { return listOf(SdkConstants.TEXT_VIEW) } }
  35. A small bug

  36. Integration

  37. Integration • Incremental fixes

  38. Integration • Incremental fixes • Using a baseline

  39. Baseline android { lintOptions { baseline file("lint baseline.xml") } }

  40. Baseline - Pass 1

  41. Baseline - Pass 2

  42. lint-baseline.xml <issues format="5" by="lint 3.6.3" client="gradle" variant="debug" version="3.6.3"> <issue id="MissingStyleAttribute"

    message="Add style to TextView in order to provide consistent design" errorLine1=" &lt;TextView" errorLine2=" ^"> <location file="src/main/res/layout/activity_main.xml" line="10" column="5"/> </issue> </issues>
  43. Performance

  44. Performance • Single pass • Missing attribute • Fast •

    Enable in IDE
  45. Performance • Single pass • Missing attribute • Fast •

    Enable in IDE • Multiple pass • Unused resources • Slow • Disable in IDE
  46. Kotlin Considerations?

  47. Kotlin Considerations? • No explicit return statements

  48. Kotlin Considerations? • No explicit return statements • No explicit

    type declarations
  49. Kotlin Considerations? • No explicit return statements • No explicit

    type declarations • Write one test case for Kotlin usage
  50. References • https://groups.google.com/g/lint-dev • https://github.com/SubhrajyotiSen/LintTalk • https://github.com/vanniktech/lint-rules • https://www.youtube.com/watch?v=p8yX5-lPS6o

  51. @iamsubhrajyoti