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

Custom Lint rules - Improve your code quality with dedicated conventions

Custom Lint rules - Improve your code quality with dedicated conventions

Presented at Droidcon Berlin on June 3rd 2015.

Although Lint is a fundamental part of the Android Developer Tools, the documentation on how to write custom Lint rules is rare and deprecated. Therefore, the objective of this workshop is to highlight the creation of custom Lint rules. It will showcase the usage of the most recent Lint API and it will demonstrate how to write different types of rules (e.g. code-, resource-, project-structure-related). It will also present how to build custom rules with Gradle and even how to bundle them with your app project (which improves the integration into CI environments).

Furthermore, it will provide some insights of useful conventions in real-world scenarios we had to struggle with and where custom Lint rules saved a lot of time and money.

At the end, participants will be prepared and encouraged to write their own custom Lint rules.

André Diermann

June 03, 2015
Tweet

More Decks by André Diermann

Other Decks in Programming

Transcript

  1. OUR MOTIVATION FOR CUSTOM LINT RULES IN WORDS enterprise mobility

    solution (> 10.000 users in EU, Asia, USA) several developers working distributed in different countries complex business logic due to full offline mode custom application architecture huge code base
  2. OUR MOTIVATION FOR CUSTOM LINT RULES IN NUMBERS 32 build

    variants >2.000 classes in ~100 packages (with ~150.000 lines of code) >200 screens with >1.500 widgets (>30.000 lines of XML) >3.500 string references (>35.000 translations) ...
  3. LINT tool for command-line and IDE scans all kind of

    development artifacts reports potential bugs, bad coding habits, broken conventions, ... features more than 200 built-in checks (January 2015)
  4. CUSTOM RULES MOTIVATION work in large and distributed teams requires

    dedicated conventions huge code bases require automated checks need for 'Android specific' validations (vs. checkstyle, FindBugs, ...) ...
  5. LINT API BASICS Core principles of the Lint API I

    s s u e D e t e c t o r S c a n n e r I s s u e R e g i s t r y
  6. ISSUE An I s s u e is a type

    of problem you want to find and show to the user.
  7. ISSUE registered in an I s s u e R

    e g i s t r y reported by a D e t e c t o r final class created by static factory method has certain attributes, such as S e v e r i t y S c o p e ...
  8. EXAMPLE p u b l i c s t a

    t i c f i n a l I s s u e I S S U E = I s s u e . c r e a t e ( " H e l l o W o r l d " , / / I D " U n e x p e c t e d a p p l i c a t i o n t i t l e " , / / b r i e f d e s c r i p t i o n " T h e a p p l i c a t i o n t i t l e s h o u l d " / / e x p l a n a t i o n + " s t a t e ' H e l l o w o r l d ' " , C a t e g o r y . C O R R E C T N E S S , / / c a t e g o r y 5 , / / p r i o r i t y S e v e r i t y . I N F O R M A T I O N A L , / / s e v e r i t y n e w I m p l e m e n t a t i o n ( / / i m p l e m e n t a t i o n H e l l o W o r l d D e t e c t o r . c l a s s , / / d e t e c t o r S c o p e . M A N I F E S T _ S C O P E / / s c o p e ) ) ;
  9. DETECTOR A D e t e c t o r

    is responsible for scanning through code and finding I s s u e instances and reporting them.
  10. EXAMPLE p u b l i c c l a

    s s H e l l o W o r l d D e t e c t o r e x t e n d s D e t e c t o r i m p l e m e n t s X m l S c a n n e r { p u b l i c s t a t i c f i n a l I s s u e I S S U E = I s s u e . c r e a t e ( . . . ) ; @ O v e r r i d e p u b l i c C o l l e c t i o n < S t r i n g > g e t A p p l i c a b l e E l e m e n t s ( ) { . . . } @ O v e r r i d e p u b l i c C o l l e c t i o n < S t r i n g > g e t A p p l i c a b l e A t t r i b u t e s ( ) { . . . } @ O v e r r i d e p u b l i c v o i d v i s i t E l e m e n t ( @ N o n N u l l X m l C o n t e x t c o n t e x t , @ N o n N u l l E l e m e n t e l e m e n t ) { . . . } @ O v e r r i d e p u b l i c v o i d v i s i t A t t r i b u t e ( @ N o n N u l l X m l C o n t e x t c o n t e x t , @ N o n N u l l A t t r a t t r i b u t e ) { . . . } }
  11. SCANNER A S c a n n e r is

    a specialized interface for D e t e c t o r s .
  12. SCANNER TYPES J a v a S c a n

    n e r C l a s s S c a n n e r B i n a r y R e s o u r c e S c a n n e r R e s o u r c e F o l d e r S c a n n e r X m l S c a n n e r G r a d l e S c a n n e r O t h e r F i l e S c a n n e r
  13. EXAMPLE JavaScanner XmlScanner a p p l i c a

    b l e S u p e r C l a s s e s ( ) g e t A p p l i c a b l e E l e m e n c h e c k C l a s s ( . . . ) v i s i t E l e m e n t ( . . . ) g e t A p p l i c a b l e M e t h o d N a m e s ( ) g e t A p p l i c a b l e A t t r i b v i s i t M e t h o d ( . . . ) v i s i t A t t r i b u t e ( . . . ) ... ...
  14. ISSUEREGISTRY An I s s u e R e g

    i s t r y is a registry which provides a list of checks to be performed on an Android project.
  15. ISSUEREGISTRY subclass I s s u e R e g

    i s t r y override g e t I s s u e s ( ) reference in M A N I F E S T j a r { m a n i f e s t { a t t r i b u t e s ' L i n t - R e g i s t r y ' : ' y o u r . p a c k a g e . n a m e . C u s t o m I s s u e R e g i s t r y ' } }
  16. EXAMPLE p u b l i c c l a

    s s C u s t o m I s s u e R e g i s t r y e x t e n d s I s s u e R e g i s t r y { @ O v e r r i d e p u b l i c L i s t < I s s u e > g e t I s s u e s ( ) { r e t u r n A r r a y s . a s L i s t ( / / N o t e : M y C u s t o m C h e c k . I S S U E , / / A c h e c k a c t u a l l y i s a d e t e c t o r . M y A d v a n c e d C h e c k . A N _ I S S U E , / / O n e d e t e c t o r c a n r e p o r t M y A d v a n c e d C h e c k . A N O T H E R _ I S S U E / / m u l t i p l e t y p e s o f i s s u e s . ) ; } }
  17. GETTING STARTED github.com/a11n/CustomLintRulesWorkshop $ g i t c l o

    n e h t t p s : / / g i t h u b . c o m / a 1 1 n / C u s t o m L i n t R u l e s W o r k s h o p . g i t $ c d C u s t o m L i n t R u l e s W o r k s h o p $ g i t c h e c k o u t - f s e c t i o n - 1
  18. PRO TIPS have a look at the use S d

    k C o n s t a n t s wherever possible utilize L i n t U t i l s when applicable default set of checks
  19. SIMPLE DETECTORS scan isolated artifacts of one type (e.g. just

    code or just resources) perform scan and evaluation in one phase
  20. TESTING No official test support official test support since last

    week's Google I/O not working (see ) (still in beta) #175161 Lint JUnit rule
  21. TESTING LintDetectorTest p u b l i c c l

    a s s H a r d c o d e d V a l u e s D e t e c t o r T e s t e x t e n d s A b s t r a c t C h e c k T e s t { @ O v e r r i d e p r o t e c t e d D e t e c t o r g e t D e t e c t o r ( ) { r e t u r n n e w H a r d c o d e d V a l u e s D e t e c t o r ( ) ; } p u b l i c v o i d t e s t S t r i n g s ( ) t h r o w s E x c e p t i o n { a s s e r t E q u a l s ( " r e s / l a y o u t / a c c e s s i b i l i t y . x m l : 3 : W a r n i n g : [ I 1 8 N ] H a r d c o d e d s t r i n g \ " B " < B u t t o n a n d r o i d : t e x t = \ " B u t t o n \ " a n d r o i d : i d = \ " @ + i d / b u t t o n 1 \ " a n d r " ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ \ n " + " r e s / l a y o u t / a c c e s s i b i l i t y . x m l : 6 : W a r n i n g : [ I 1 8 N ] H a r d c o d e d s t r i n g \ " B " < B u t t o n a n d r o i d : t e x t = \ " B u t t o n \ " a n d r o i d : i d = \ " @ + i d / b u t t o n 2 \ " a n d r " ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ \ n " + " 0 e r r o r s , 2 w a r n i n g s \ n " , l i n t F i l e s ( " r e s / l a y o u t / a c c e s s i b i l i t y . x m l " ) ) ; } }
  22. TESTING Lint JUnit rule @ R u l e p

    u b l i c L i n t l i n t = n e w L i n t ( ) ; @ T e s t p u b l i c v o i d t e s t ( ) t h r o w s E x c e p t i o n { l i n t . s e t F i l e s ( " A n d r o i d M a n i f e s t . x m l " , " r e s / v a l u e s / s t r i n g . x m l " ) ; l i n t . s e t I s s u e s ( M y C u s t o m R u l e . I S S U E ) ; l i n t . a n a l y z e ( ) ; L i s t < W a r n i n g > w a r n i n g s = l i n t . g e t W a r n i n g s ( ) ; a s s e r t T h a t ( w a r n i n g s ) . h a s S i z e ( 2 ) ; }
  23. BASIC APPROACH utilizes basic Lint extension feature two steps setup

    1. assemble custom Lint rules into JAR 2. copy JAR to ~ / . a n d r o i d / l i n t /
  24. BASIC APPROACH # ! / b i n / s

    h # B u i l d . j a r . / g r a d l e w a s s e m b l e # I n s t a l l i f [ ! - d " ~ / . a n d r o i d / l i n t / " ] ; t h e n m k d i r ~ / . a n d r o i d / l i n t / f i c p b u i l d / l i b s / l i n t . j a r ~ / . a n d r o i d / l i n t /
  25. BASIC APPROACH Pros Cons just assemble and copy no straightforward

    distribution and configuration one resulting JAR no project-specific rules no changes in the project to analyze inconvenient for multi developer teams applied for all analyzed projects inconvenient for CI environments
  26. INTEGRATED APPROACH uses AAR bundle as wrapper two steps setup

    1. wrap custom Lint rules into an AAR 2. make application project depend on that AAR
  27. THREE OPTIONS FOR DEPENDENCIES Copy the AAR to the libs

    folder d e p e n d e n c i e s { c o m p i l e f i l e T r e e ( d i r : ' l i b s ' , i n c l u d e : ' * . j a r ' ) } Deploy the AAR to a repository d e p e n d e n c i e s { c o m p i l e ' y o u r . p a c k a g e . n a m e : c u s t o m - l i n t : 1 . 0 . 0 @ a a r ' }
  28. THREE OPTIONS FOR DEPENDENCIES Have a Java module for the

    Lint rules and an Android library module as wrapper A n d r o i d a p p l i c a t i o n p r o j e c t - - a p p / / d e f a u l t A n d r o i d a p p l i c a t i o n m o d u l e - - l i n t / / A n d r o i d l i b r a r y , a c t s a s w r a p p e r f o r t h e L i n t r u l e s - - l i n t r u l e s / / J a v a m o d u l e w i t h y o u r c u s t o m L i n t r u l e s p r o j e c t . a f t e r E v a l u a t e { d e f c o m p i l e L i n t = p r o j e c t . t a s k s . g e t B y P a t h ( ' : l i n t : c o m p i l e L i n t ' ) c o m p i l e L i n t . d e p e n d s O n ' : l i n t r u l e s : j a r ' c o m p i l e L i n t < < { c o p y { f r o m ' . . / l i n t r u l e s / b u i l d / l i b s ' i n t o ' b u i l d / i n t e r m e d i a t e s / l i n t ' } } }
  29. INTEGRATED APPROACH Pros Cons allows project specific rules changes in

    project required integrated within project not applied for all analyzed projects ideal for multi developer teams (no official documentation on how to wrap into AAR bundle available) perfect for CI environments
  30. PLACEHOLDER used for layout purposes help to get a meaningful

    overview present applied styles show wrapping
  31. ACTIVITY/FRAGMENT CLASS NAMES Natural names Conventional names ... C o

    n s u m e d M a t e r i a l s A c t i v i t y C u s t o m e r S i g n a t u r e F r a g m e n t O r d e r C o n f i r m a t i o n F r a g m e n t O r d e r D a t a M o d e l O r d e r R e p o r t A c t i v i t y O r d e r R e p o r t V i e w M o d e l V a n S t o c k M a t e r i a l F r a g m e n t ... ... A c t i v i t y C o n s u m e d M a t e A c t i v i t y O r d e r R e p o r t F r a g m e n t C u s t o m e r S i g n F r a g m e n t O r d e r C o n f i r m F r a g m e n t V a n S t o c k M a t e M o d e l O r d e r D a t a V i e w M o d e l O r d e r R e p o r t ...
  32. LAYOUT NAMES should express where they are going to be

    used Examples: a c t i v i t y _ o r d e r _ s t a t u s . x m l f r a g m e n t _ c u s t o m e r _ d e t a i l s . x m l l i s t _ i t e m _ p u r c h a s e _ o r d e r . x m l
  33. STRING REFERENCES From experience, string references should be tied to

    a widget in a 1:1 relation!! reduces semantical incorrectness allows outsourcing of translation process may be refined by literals to avoid redundancy could be declared in distinct and dedicated files
  34. EXAMPLE < ! - - f r a g m

    e n t _ c u s t o m e r _ s i g n a t u r e . x m l - - > < T e x t V i e w a n d r o i d : i d = " @ + i d / t v C u s t o m e r N a m e " a n d r o i d : t e x t = " @ s t r i n g / f r a g m e n t _ c u s t o m e r _ s i g n a t u r e _ t v C u s t o m e r N a m e _ t e x t " / > < E d i t T e x t a n d r o i d : i d = " @ + i d / e t C u s t o m e r N a m e " a n d r o i d : h i n t = " @ s t r i n g / f r a g m e n t _ c u s t o m e r _ s i g n a t u r e _ e t C u s t o m e r N a m e _ h i n t " / > < B u t t o n a n d r o i d : i d = " @ + i d / b t S u b m i t " a n d r o i d : t e x t = " @ s t r i n g / f r a g m e n t _ c u s t o m e r _ s i g n a t u r e _ b t S u b m i t _ t e x t " / > < ! - - s t r i n g . x m l - - > < s t r i n g n a m e = " f r a g m e n t _ c u s t o m e r _ s i g n a t u r e _ t v C u s t o m e r N a m e _ t e x t " > P l e a s e e n t e r t h e c u s t o m e r n a m e : < / s t r i n g > < s t r i n g n a m e = " f r a g m e n t _ c u s t o m e r _ s i g n a t u r e _ b t S u b m i t _ t e x t " > @ s t r i n g / l i t e r a l _ s u b m i t < / s t r i n g >
  35. ID PREFIXES infer semantics on widget types on inter-widget relations

    speed up code completion < R e l a t i v e L a y o u t a n d r o i d : i d = " @ + i d / l l C u s t o m e r D e t a i l s " . . . > < I m a g e V i e w a n d r o i d : i d = " @ + i d / i v C u s t o m e r N a m e " . . . / > < T e x t V i e w a n d r o i d : i d = " @ + i d / t v C u s t o m e r N a m e " . . . / > < E d i t T e x t a n d r o i d : i d = " @ + i d / e t C u s t o m e r N a m e " . . . / > . . . < B u t t o n a n d r o i d : i d = " @ + i d / b t S u b m i t " . . . / > < / R e l a t i v e L a y o u t >
  36. SUMMARY custom Lint rules are written by extending D e

    t e c t o r s and implementing S c a n n e r s complex rules have a scan and an evaluation phase Lint API provides convenient access to different types of development artifacts AAR format allows easy deployment and project-related rules
  37. WHAT'S NEXT release of reference guide for creating custom Lint

    rules improving Lint JUnit rule extending API advanced L i n t U t i l s advanced B a s e D e t e c t o r s encourage for more exchange (e.g. on StackOverflow)