This guide is different from others you may see, here we will be focousing for a smooth onboarding process for development to follow some guidlines which we follow accross every project we build, We created this guide to keep the code in our projects consistent so every developer can unite anytime without any hustle
Our overarching goals are conciseness, readability and simplicity.
This style-guide is somewhat of a mash-up between the existing Kotlin language style guides, and a tutorial-readability focused Swift style-guide. The language guidance is drawn from:
- The Android Kotlin style guide
- The Kotlin Coding Conventions
- The Android contributors style guide
- The Google Java Style Guide.
- The Raywenderlich Guide.
It is possible to get Android Studio to adhere to these style guidelines, via a rather complex sequence of menus. To make it easier, we've provided a coding style that can be imported into Android Studio.
The file can be found here.
To install the file, open Android Studio Settings and go to Editor > Code Style > Kotlin, then click the gear menu and choose Import Scheme....
From now on, projects you create should follow the correct style guidelines and similer package structurs .
we use package structure which is combination of package by layer, type and features
The whole Project structure guidlines can he found here Project structure which we follow
we would like to commit code more often so the big tasks can be broken in to pieces and committed stage wise, so we can drilldown development bugs easily on grass root levels, task based push will avoid mixup of more features in single commit which would keep commits history cleaner, following this give us advantages of reverting entire commit when unstable code surfaces, frequent commit eases development with less conflicts :]
Good commit messages have 3 critical benefits:
- Help speed up the code review process
- Help in writing good release notes
- Help future maintainers (and your future self) understand why a change was made, or what exactly a particular bug was
(syntax would be as below)
main line (70 chars or less)
- sub lines / related small bullets points if nessary(50 chars or less)
- related collateral changes bullets points if nessary(50 chars or less)
(example would be as below)
-single line-
fix: Fixed issue with headerText going under status text (internal fixes which wont have tikets :])
fix: IN-753 - Changed date to UTC value
-multi line-
Add: IN-749 - Create API to fetch Prom Details
- Create Prom Response data class
- Define API to fetch Prom details
- Main line start with words like “Add:”, “Update:”, and “Fix:”
- Main line should have a Jira ticket numbers associated with the task / subtask / story / bug - eg - IN-674
- Main lines should be followed by a short note, never end with a period
- Sub lines can be some short details in bullet form (not mandatory to have one)
- The explanatory text isn’t required but encouraged, especially when a commit fixes a bug. It’s good practice to explain clearly why a certain issue was occurring and how the commit fixes it.
- Nomenclature
- Declarations
- Spacing
- Brace Style
- When Statements
- Types
- String constants, naming, and values
- Arguments in Fragments and Activities
- XML Guidance
On the whole, naming should follow Java standards, as Kotlin is a JVM-compatible language.
Package names are similar to Java: all lower-case, multiple words concatenated together, without hypens or underscores:
BAD:
com.orgnization.some_widget
GOOD:
com.orgnization.somewidget
Written in UpperCamelCase. For example ArcSlider
.
Written in lowerCamelCase. For example setValue
.
Generally, written in lowerCamelCase.
Example field names:
class MyClass {
var publicField: Int = 0
val person = Person()
private var privateField: Int?
}
Constant values in the companion object should be written in uppercase, with an underscore separating words:
companion object {
const val THE_CONSTENT = 46
}
Written in lowerCamelCase.
Single character values must be avoided, except for temporary looping variables.
In code, acronyms should be treated as words. For example:
BAD:
XMLHTTPRequest
URL: String?
findPostByID
GOOD:
XmlHttpRequest
url: String
findPostById
Only include visibility modifiers if you need something other than the default of public.
BAD:
public val wideOpenProperty = 1
private val myOwnPrivateProperty = "private"
GOOD:
val wideOpenProperty = 1
private val myOwnPrivateProperty = "private"
Access level modifiers should be explicitly defined for classes, methods and member variables.
Prefer single declaration per line.
GOOD:
username: String
twitterHandle: String
Exactly one class per source file, although inner classes are encouraged where scoping appropriate.
Prefer data classes for simple data holding objects.
BAD:
class Person(val name: String) {
override fun toString() : String {
return "Person(name=$name)"
}
}
GOOD:
data class Person(val name: String)
Enum classes without methods may be formatted without line-breaks, as follows:
private enum CompassDirection { EAST, NORTH, WEST, SOUTH }
Spacing is especially important in raywenderlich.com code, as code needs to be easily readable as part of the tutorial.
Indentation is using spaces - never tabs.
Indentation for blocks uses 2 spaces (not the default 4):
BAD:
for (i in 0..9) {
Log.i(TAG, "index=" + i)
}
GOOD:
for (i in 0..9) {
Log.i(TAG, "index=" + i)
}
Indentation for line wraps should use 4 spaces (not the default 8):
BAD:
val widget: CoolUiWidget =
someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line)
GOOD:
val widget: CoolUiWidget =
someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line)
Lines should be no longer than 100 characters long.
There should be exactly one blank line between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods.
When they are needed, use comments to explain why a particular piece of code does something. Comments must be kept up-to-date or deleted.
Avoid block comments inline with code, as the code should be as self-documenting as possible. Exception: This does not apply to those comments used to generate documentation.
Only trailing closing-braces are awarded their own line. All others appear the same line as preceding code:
BAD:
class MyClass
{
fun doSomething()
{
if (someTest)
{
// ...
}
else
{
// ...
}
}
}
GOOD:
class MyClass {
fun doSomething() {
if (someTest) {
// ...
} else {
// ...
}
}
}
Conditional statements are always required to be enclosed with braces, irrespective of the number of lines required.
BAD:
if (someTest)
doSomething()
if (someTest) doSomethingElse()
GOOD:
if (someTest) {
doSomething()
}
if (someTest) { doSomethingElse() }
Unlike switch
statements in Java, when
statements do not fall through. Separate cases using commas if they should be handled the same way. Always include the else case.
BAD:
when (anInput) {
1 -> doSomethingForCaseOneOrTwo()
2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
}
GOOD:
when (anInput) {
1, 2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
else -> println("No case satisfied")
}
Always use Kotlin's native types when available. Kotlin is JVM-compatible so [TODO: more info]
Type inference should be preferred where possible to explicitly declared types.
BAD:
val something: MyType = MyType()
val meaningOfLife: Int = 46
GOOD:
val something = MyType()
val meaningOfLife = 42
Constants are defined using the val
keyword, and variables with the var
keyword. Always use val
instead of var
if the value of the variable will not change.
Tip: A good technique is to define everything using val
and only change it to var
if the compiler complains!
** TODO: A bunch of stuff about companion objects **
Declare variables and function return types as nullable with ?
where a null
value is acceptable.
Use implicitly unwrapped types declared with !!
only for instance variables that you know will be initialized before use, such as subviews that will be set up in onCreate
for an Activity or onCreateView
for a Fragment.
When naming nullable variables and parameters, avoid naming them like nullableString
or maybeView
since their nullability is already in the type declaration.
When accessing a nullable value, use the safe call operator if the value is only accessed once or if there are many nullables in the chain:
editText?.setText("foo")
Many elements of the Android SDK such as SharedPreferences
, Bundle
, or Intent
use a key-value pair approach so it's very likely that even for a small app you end up having to write a lot of String constants.
When using one of these components, you must define the keys as a static final
fields and they should be prefixed as indicated below.
Element | Field Name Prefix |
---|---|
SharedPreferences | PREF_ |
Bundle | BUNDLE_ |
Fragment Arguments | ARGUMENT_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
Note that the arguments of a Fragment - Fragment.getArguments()
- are also a Bundle. However, because this is a quite common use of Bundles, we define a different prefix for them.
Example:
// Note the value of the field is the same as the name to avoid duplication issues
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// Intent-related items use full package name as value
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
When data is passed into an Activity
or Fragment
via an Intent
or a Bundle
, the keys for the different values must follow the rules described in the section above.
When an Activity
or Fragment
expects arguments, it should provide a public static
method that facilitates the creation of the relevant Intent
or Fragment
.
In the case of Activities the method is usually called getStartIntent()
:
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}
For Fragments it is named newInstance()
and handles the creation of the Fragment with the right arguments:
public static UserFragment newInstance(User user) {
UserFragment fragment = new UserFragment();
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
fragment.setArguments(args)
return fragment;
}
Note 1: These methods should go at the top of the class before onCreate()
.
Note 2: If we provide the methods described above, the keys for extras and arguments should be private
because there is not need for them to be exposed outside the class.
Because the lack of XML namespaces, makes managing Android resources tedious. And causes things to grow out of control easily, especially in large projects. So let’s introduce a simple scheme that will solve your pains.
- easy lookup of any resource (autocomplete)
- logical, predictable names
- clean ordering of resources
- strongly typed resources
All resource names follow a simple convention.
Let’s first describe every element briefly. After the advantages, we’ll see how this applies to each resource type.
<WHAT>
- Indicate what the resource actually represents, often a standard Android view class. Limited options per resource type.(e.g. MainActivity -> activity)
<WHERE>
- Describe where it logically belongs in the app. Resources used in multiple screens use all, all others use the custom part of the Android view subclass they are in. (e.g. MainActivity -> main, ArticleDetailFragment -> articledetail)
<DESCRIPTION>
- Differentiate multiple elements in one screen.(e.g. title)
<SIZE> (optional)
- Either a precise size or size bucket. Optionally used for drawables and dimensions.(e.g. 24dp, small))
The WHERE part describes what screen a resource belongs to. Hence it is easy to get all IDs, drawables, dimensions,… for a particular screen.
For resource IDs, the WHAT describes the class name of the xml element it belongs to. This makes is easy to what to cast your findViewById() calls to.
File browsers/project navigator usually sort files alphabetically. This means layouts and drawables are grouped by their WHAT (activity, fragment,..) and WHERE prefix respectively. A simple Android Studio plugin/feature can now display these resources as if they were in their own folder.
Because resource names are far more predictable, using the IDE’s autocomplete becomes even easier. Usually entering the WHAT or WHERE is sufficient to narrow autocomplete down to a limited set of options.
Similar resources in different screens are either all or have a different WHERE. A fixed naming scheme avoids all naming collisions.
Overall all resources will be named more logical, causing a cleaner Android project.
This naming scheme could be easily supported by the Android Studio offering features such as: lint rules to enforce these names, refactoring support when you change a WHAT or WHERE, better resource visualisation in project view,…
Layouts are relatively simple, as there usually are only a few layouts per screen. Therefore the rule can be simplified to:
Where<WHAT>
is one of the following:
Prefix | Usage |
---|---|
activity | contentview for activity |
fragment | view for a fragment |
view | inflated by a custom view |
item | layout used in list/recycler/gridview |
layout | layout reused using the include tag |
- activity_main: content view of the MainActivity
- fragment_articledetail: view for the ArticleDetailFragment
- view_menu: layout inflated by custom view class MenuView
- item_article: list item in ArticleRecyclerView
- layout_actionbar_backbutton: layout for an actionbar with a backbutton (too simple to be a customview)
The <WHAT>
part for Strings is irrelevant. So either we use <WHERE>
to indicate where the string will be used:
or all if the string is reused throughout the app:
- articledetail_title: title of ArticleDetailFragment
- feedback_explanation: feedback explanation in FeedbackFragment
- feedback_namehint: hint of name field in FeedbackFragment
- all_done: generic “done” string
<WHERE>
obviously is the same for all resources in the same view.
The <WHAT>
part for Drawables is irrelevant. So either we use <WHERE>
to indicate where the drawable will be used:
or all if the drawable is reused throughout the app:
Optionally you can add a <SIZE>
argument, which can be an actual size “24dp” or a size qualifier “small”.
- articledetail_placeholder: placeholder in ArticleDetailFragment
- all_infoicon: generic info icon
- all_infoicon_large: large version of generic info icon
- all_infoicon_24dp: 24dp version of generic info icon
For IDs, <WHAT>
is the class name of the xml element it belongs to. Next is the screen the ID is in, followed by an optional description to distinguish similar elements in one screen.
- tablayout_main -> TabLayout in MainActivity
- imageview_menu_profile -> profile image in custom MenuView
- textview_articledetail_title -> title TextView in ArticleDetailFragment
Apps should only define a limited set of dimensions, which are constantly reused. This makes most dimensions all by default.
Therefore you should mostly use:
and optionally use the screen specific variant:
Where <WHAT>
is one of the following:
Prefix | Usage |
---|---|
width | width in dp |
height | height in dp |
size | if width == height |
margin | margin in dp |
padding | padding in dp |
elevation | elevation in dp |
keyline | absolute keyline measured from view edge in dp |
textsize | size of text in sp |
Note that this list only contains the most used <WHAT>
s. Other dimensions qualifiers like: rotation, scale,… are usually only used in drawables and as such less reused.
- height_toolbar: height of all toolbars
- keyline_listtext: listitem text is aligned at this keyline
- textsize_medium: medium size of all text
- size_menu_icon: size of icons in menu
- height_menu_profileimage: height of profile image in menu
Copyright 2020.
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.