Have you ever been commented on just because your code format is different from the others? You have to edit changes code settings in Android Studio ? import other people’s settings into AS and do this again and again on different projects ? Really annoying!
In this article, I will introduce to you 2 tools that I often use in android code are ktlint
and detekt
to check code style , format code and code smell . The configuration is done directly on the code base and the results are exactly the same on different machines so it is very convenient. Posts mainly share code , config steps if there are any questions, please comment or contact , I will support.
Prepare
Things to keep in mind when getting started.
- The Android gradle project uses Gradle’s Kotlin DSL . If you use
Groovy
, do your own research and convert , I think it’s not too complicated. - If your project has many
module
(remember to distinguish them frompackage
) then you need to implement them on all modules .
Install ktlint
Ktlint
is Kotlin linter
opensource to check and format Kotlin code style, convention . Purpose I used ktlint in the project is to help you help to config android studio code style settings dragged on just as before, and so simple, I almost used to format the standard pinterest
launched, people can custom and it’s not too hard. Importantly code style rules should be done immediately at the init or start projects (probably Maintain source or new source).
No more rambling, let’s get started:
1. Go to github of ktlint and select the latest version
Declare in your dependency file (in my project is Configs.kt
), if you do not have this file , skip this step for now.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">object</span> Versions <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">val</span> ktlint <span class="token operator">=</span> <span class="token string">"0.36.0"</span> <span class="token comment">// other libs versions ...</span> <span class="token punctuation">}</span> <span class="token keyword">object</span> Deps <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">val</span> ktlint <span class="token operator">=</span> <span class="token string">"com.pinterest:ktlint: <span class="token interpolation"><span class="token delimiter variable">${</span> Versions <span class="token punctuation">.</span> ktlint <span class="token delimiter variable">}</span></span> "</span> <span class="token comment">// other dependencies ...</span> <span class="token punctuation">}</span> |
2. Create a source ktlint
management file , temporarily called ktlint.gradle.kts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | repositories <span class="token punctuation">{</span> <span class="token function">jcenter</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> ktlint <span class="token keyword">by</span> configurations <span class="token punctuation">.</span> creating <span class="token keyword">val</span> ktlintCheck <span class="token keyword">by</span> tasks <span class="token punctuation">.</span> <span class="token function">creating</span> <span class="token punctuation">(</span> JavaExec <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> group <span class="token operator">=</span> <span class="token string">"verification"</span> description <span class="token operator">=</span> <span class="token string">"Check Kotlin code style."</span> classpath <span class="token operator">=</span> configurations <span class="token punctuation">.</span> <span class="token function">getByName</span> <span class="token punctuation">(</span> <span class="token string">"ktlint"</span> <span class="token punctuation">)</span> main <span class="token operator">=</span> <span class="token string">"com.pinterest.ktlint.Main"</span> args <span class="token operator">=</span> <span class="token function">listOf</span> <span class="token punctuation">(</span> <span class="token string">"src/**/*.kt"</span> <span class="token punctuation">,</span> <span class="token string">"--reporter=html,output= <span class="token interpolation variable">$buildDir</span> /reports/ktlint/ktlint.html"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> ktlintFormat <span class="token keyword">by</span> tasks <span class="token punctuation">.</span> <span class="token function">creating</span> <span class="token punctuation">(</span> JavaExec <span class="token operator">::</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> group <span class="token operator">=</span> <span class="token string">"formatting"</span> description <span class="token operator">=</span> <span class="token string">"Fix Kotlin code style deviations."</span> classpath <span class="token operator">=</span> configurations <span class="token punctuation">.</span> <span class="token function">getByName</span> <span class="token punctuation">(</span> <span class="token string">"ktlint"</span> <span class="token punctuation">)</span> main <span class="token operator">=</span> <span class="token string">"com.pinterest.ktlint.Main"</span> args <span class="token operator">=</span> <span class="token function">listOf</span> <span class="token punctuation">(</span> <span class="token string">"-F"</span> <span class="token punctuation">,</span> <span class="token string">"src/**/*.kt"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> dependencies <span class="token punctuation">{</span> <span class="token function">ktlint</span> <span class="token punctuation">(</span> Deps <span class="token punctuation">.</span> ktlint <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
A few points to note in this pile of code :
- I created 1 configurations to declare dependency named
ktlint
(likeimplementation
,testImplementation
orkapt
, …) - Create 2 gradle task :
ktlintCheck
: used to check Kotlin code style and convention . Here I just do the simple check and create thehtml
report in the pathapp/build/reports/ktlint/ktlint.html
ktlintFormat
: used to format the code automatically.
- If you skip step 1, you can replace the dependencies as follows:1234dependencies <span class="token punctuation">{</span><span class="token function">ktlint</span> <span class="token punctuation">(</span> <span class="token string">"com.pinterest:ktlint:0.36.0"</span> <span class="token punctuation">)</span><span class="token punctuation">}</span>
3. Go to the app/build.gradle.kts
file to complete the final steps
- Add references to the file
ktlint.gradle.kts
, notefrom
, here I am to level with filebuild.gradle.kts
.
1 2 3 4 5 6 7 8 9 10 11 | plugins <span class="token punctuation">{</span> <span class="token function">id</span> <span class="token punctuation">(</span> Plugins <span class="token punctuation">.</span> androidApp <span class="token punctuation">)</span> <span class="token function">kotlin</span> <span class="token punctuation">(</span> Plugins <span class="token punctuation">.</span> kotlinAndroid <span class="token punctuation">)</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token comment">// Add this references</span> buildscript <span class="token punctuation">{</span> <span class="token function">apply</span> <span class="token punctuation">(</span> from <span class="token operator">=</span> <span class="token string">"ktlint.gradle.kts"</span> <span class="token punctuation">)</span> <span class="token comment">// path to ktlint.gradle.kts file</span> <span class="token punctuation">}</span> android <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> |
4. Custom a few rules
If you feel it is necessary to fit your project or code style with you and your team members .
- Create an
.editorconfig
file located in the outermost directory (rootDir
) of the project. Edit the necessary config , see full instructions here . For my project, currently I only setup a little bit:
1 2 3 4 5 6 | <span class="token comment"># this use for ktlint config</span> <span class="token comment"># see more at https://github.com/pinterest/ktlint#editorconfig</span> <span class="token punctuation">[</span> <span class="token operator">*</span> <span class="token punctuation">.</span> <span class="token punctuation">{</span> kt <span class="token punctuation">,</span> kts <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token comment"># Comma-separated list of rules to disable</span> disabled_rules <span class="token operator">=</span> no <span class="token operator">-</span> wildcard <span class="token operator">-</span> imports |
5. Test results
- Run the command
./gradlew ktlintFormat
after you finish coding to automatically format the code . - After running the command, usually the auto format will succeed. If there is a problem, you can go to
app/build/reports/ktlint
to view the report and fix it manually. According to my experience, 99.69% must be successful, the remaining percentage is due to the automatic format algorithm and there are a few minor errors and need to fix with rice (yaoming).
Report html format:
Install detekt
Meet detekt, a static code analysis tool for the Kotlin programming language. It operates on the abstract syntax tree provided by the Kotlin compiler.
Basically this is a linter , the analyzer is used to check code smell , create a report of code complexity like LOCs , cyclomatic complexity
and number of code smell …
detekt is also a wrapper based on ktlint so it can format code , but I used ktlint separately (always the latest) so I don’t want to use detekt for auto format .
And similar ktlint, I just apply at a fast, compact as possible, limiting change too much of detekt config available. You can change it to suit your project.
Let’s get started!
1. Setup dependency
Declaring dependency
1 2 3 4 5 6 7 | <span class="token keyword">object</span> Versions <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">val</span> detekt <span class="token operator">=</span> <span class="token string">"1.9.1"</span> <span class="token punctuation">}</span> <span class="token keyword">object</span> Plugins <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">val</span> detekt <span class="token operator">=</span> <span class="token string">"io.gitlab.arturbosch.detekt"</span> <span class="token punctuation">}</span> |
In the app/build.gradle.kts
file :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | plugins <span class="token punctuation">{</span> <span class="token function">id</span> <span class="token punctuation">(</span> Plugins <span class="token punctuation">.</span> androidApp <span class="token punctuation">)</span> <span class="token function">kotlin</span> <span class="token punctuation">(</span> Plugins <span class="token punctuation">.</span> kotlinAndroid <span class="token punctuation">)</span> <span class="token function">id</span> <span class="token punctuation">(</span> Plugins <span class="token punctuation">.</span> detekt <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">version</span> <span class="token punctuation">(</span> Versions <span class="token punctuation">.</span> detekt <span class="token punctuation">)</span> <span class="token comment">// or id("io.gitlab.arturbosch.detekt").version("1.9.1")</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> detekt <span class="token punctuation">{</span> config <span class="token operator">=</span> <span class="token function">files</span> <span class="token punctuation">(</span> <span class="token string">" <span class="token interpolation variable">$rootDir</span> /config/detekt/detekt.yml"</span> <span class="token punctuation">)</span> <span class="token comment">// config rules file</span> input <span class="token operator">=</span> <span class="token function">files</span> <span class="token punctuation">(</span> <span class="token string">"src/main/java"</span> <span class="token punctuation">)</span> <span class="token comment">// note: java or kotlin???</span> <span class="token punctuation">}</span> tasks <span class="token punctuation">{</span> withType <span class="token operator"><</span> Detekt <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token comment">// Target version of the generated JVM bytecode. It is used for type resolution.</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> jvmTarget <span class="token operator">=</span> <span class="token string">"1.8"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Note :
Detekt
runs needjvmTarget 1.8
- Note the input source directory of your project is
src/main/java
, your could besrc/main/kotlin
or something.
2. Generate default detekt config
Run the command ./gradlew detektGenerateConfig
, this task will automatically generate the detekt.yml
file containing all the default config rules .
3. Test results
- Run the command
./gradlew detekt
. - I think the project has implemented a long time ( maintain ), it will fail quite a bit. I recommend right now, do the following steps in the correct order:
- Go to the report folder to see all the errors.
- Fix heavy bugs about code smell , as much as possible because it’s not good for the project in nature.
- Rerun and check the report to fix further if there are still many serious errors.
- After fixing all the errors that should be fixed , if unable to pass , try editing some rules to suit the project – step 4.
4. Custom config rules
Important Note : Since these are standard and suitable configurations for most kotlin projects, please think before you change.
Ok, the truth is that if you apply detekt on a project for too long, it really is very difficult to do notes on, and another fact is some config in android other than Kotlin net, even so but Custom work should be kept as low as possible. The rule to change is:
Check for errors and make sure to fail in the report that it should be used in the project.
Open config/detekt.yml
file and make changes. Here are the parts I have modified , you can refer to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token comment"># this is some of sample rules can be updated or not</span> <span class="token key atrule">maxIssues</span> <span class="token punctuation">:</span> <span class="token number">3 </span> <span class="token comment"># default is 0, we should consider on this :D </span> <span class="token key atrule">TooGenericExceptionCaught</span> <span class="token punctuation">:</span> <span class="token key atrule">active</span> <span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">excludes</span> <span class="token punctuation">:</span> <span class="token string">"**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"</span> <span class="token key atrule">exceptionNames</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> ArrayIndexOutOfBoundsException <span class="token punctuation">-</span> Error <span class="token comment"># - Exception My project needs to catch Exception... :(</span> <span class="token punctuation">-</span> IllegalMonitorStateException <span class="token punctuation">-</span> NullPointerException <span class="token punctuation">-</span> IndexOutOfBoundsException <span class="token punctuation">-</span> RuntimeException <span class="token comment"># - Throwable My project needs to catch Throwable... :(</span> <span class="token key atrule">ForbiddenComment</span> <span class="token punctuation">:</span> <span class="token key atrule">active</span> <span class="token punctuation">:</span> <span class="token boolean important">false </span> <span class="token comment">#true, I think while implementation phase, TODO, FIXME comments should be kept.</span> <span class="token key atrule">ReturnCount</span> <span class="token punctuation">:</span> <span class="token key atrule">active</span> <span class="token punctuation">:</span> <span class="token boolean important">false </span> <span class="token comment">#true</span> <span class="token punctuation">...</span> <span class="token key atrule">WildcardImport</span> <span class="token punctuation">:</span> <span class="token key atrule">active</span> <span class="token punctuation">:</span> <span class="token boolean important">false </span> <span class="token comment">#default is true</span> |
Conclustion
Above are all the basic steps to help the android ( kotlin ) project to increase a little quality in the implementation code stage of convention , code style , quality … Hope everyone applies successfully!
Source code reference:
Happy Coding!