This document is under construction
New projects should follow the Android Gradle project structure that is defined on the Android Gradle plugin user guide.
It's recommended to structure in the following way:
- network
- picasso
- dashboard
- matchfeed
- model
- injection
- modules
- component
- qualifiers
- ui
- dashboard
- notifications
- matchfeed
- persistence
- dashboard
- matchfeed
- analytics
- ads
- utils
Contains all the network related files (Http services, deserializers, image loading...). Try to group network files for different parts of the app in different subpackages.
Contains all the model related files.
We use depency injection usually using Dagger 2. This package will contain all the dependency injection related files (modules, components, qualifiers, scopes...).
Contains all ui related files (Views, adapters...). Try to group ui files for different parts of the app into different subpackages.
Contains all the persistence related files (Shared preferences, sqlite, files storage...). Try to group persistence files for different parts of the app into different subpackages.
Contains all the analytics related files
Contains all the adverts related files
Contains all the utility related files, usually they are files containing only static methods that helps to do some specific task that it's repeated all over the app and doesn't really belong to any other part of the app (TimeUtils, FontUtils, ViewUtils...)
At TAB we follow Android Code Style guidelines
On top of rules listed there we have few extra rules and exceptions:
- Do NOT use m or s prefix in fields inside POJO (models, events)
good:
public class League {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
bad:
public class League {
private String mId;
private String mName;
public String getId() {
return mId;
}
public void setId(String id) {
mId = id;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
}
- Use full words
good:
mInitalDownloadTimestamp
bad:
mInitDlTimestamp
- Try to place most important part of the name in the first or last part of the name
good:
mInitialDownloadTimestamp
not so good:
mDownloadInitialTimestamp
(important part is that this is a timestamp and it is initial one)
- Be explicit in choosing names
good:
calculateDownloadDuration()
not so good:
duration()
- Always user string resources for user facing strings.
- Define all Intent extra keys, FragmentArgument keys, FragmentManager tags, "magic" values, etc as constants
good:
public class MyFragment extends Fragment{
private static final String ARG_FIRST = "arg1";
private static final String ARG_SECOND = "arg2";
public static MyFragment newInstance(String arg1, int arg2){
Bundle args = new Bundle();
args.put(ARGS_FIRST, arg1);
args.put(ARGS_SECOND, arg2);
.....
}
}
bad:
public class MyFragment extends Fragment{
public static MyFragment newInstance(String arg1, int arg2){
Bundle args = new Bundle();
args.put("arg1" , arg1);
args.put("arg2" , arg2);
.....
}
}
- Define constants into the file where they're mostly used or makes sense logically.
- Don't create specific files with loads of constants that it's not clear where they belong to
- Don't use enum when they can be replaced by constants. Use constants and make use of Enumerated Annotations when possible
good:
public static final int ONE = 0;
public static final int TWO = 1;
publci static final int THREE = 2;
bad:
public enum Values{
ONE(0), TWO(1), THREE(2)
}
- Avoid one line braceless conditions
good:
if (condtion) {
body();
}
bad:
if (condition)
body()
or:
if (condition) body();
- Braces should be on same line as case
- One space between the switch keyword and the open parenthesis, one space between the close parenthesis and the opening brace.
- Don't use "magic" words or numbers for each case, use always constants
good:
private static final int CASE_1 = 0;
private static final int CASE_2 = 1;
....
switch (expression) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
default:
// default code
}
bad:
switch (expression) {
case 0:
// code
break;
case 0:
// code
break;
default:
// default code
}
- Don't use the default case if it's not required
good:
switch (expresion) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
}
bad:
switch (expresion) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
default:
}
- Even a single annotation should be above the annotated field/method/class
good:
@Override
public void onCrate(Bundle savedInstanceState) { ... }
bad:
@Override public void onCreate(Bundle savedInstanceState) { ... }
(we want to keep method signature lines short as possible to use more explicit method names)
Implement or use Logger which allows filtering log calls per subsystem. It's recommended to use Timber
good:
TabLog.e(TabLog.UI,"Cannot determine image size");
not so good:
Log.e("App", "Cannot determine image size");