-
Notifications
You must be signed in to change notification settings - Fork 2
changed project structure guideline, switched from AOSP guidelines to… #3
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| ***This document is under construction*** | ||
|
|
||
| # Android Coding Guidelines Android | ||
| # Android Coding Guidelines | ||
|
|
||
| ## 1. Project structure | ||
|
|
||
|
|
@@ -10,327 +10,66 @@ New projects should follow the Android Gradle project structure that is defined | |
|
|
||
| It's recommended to structure in the following way: | ||
|
|
||
| ### App | ||
| mainpackage | ||
| * homescreen // 1.1.1. Activity level | ||
| * category // 1.1.2 Feature of the activity | ||
| * di // 1.1.3 Dependency Injection | ||
| * presenter // 1.1.4 Presentation layer | ||
| * repository // 1.1.5 Repository layer | ||
| * navigator // 1.1.6 Navigator layer | ||
| * search | ||
| * di | ||
| * presenter | ||
| * repository | ||
| * navigator | ||
|
|
||
| * network | ||
| * picasso | ||
| * dashboard | ||
| * matchfeed | ||
| * model | ||
| * injection | ||
| * modules | ||
| * component | ||
| * qualifiers | ||
| * ui | ||
| * dashboard | ||
| * notifications | ||
| * matchfeed | ||
| * persistence | ||
| * dashboard | ||
| * matchfeed | ||
| * analytics | ||
| * ads | ||
| * utils | ||
|
|
||
| #### 1.1.1 Network | ||
| #### 1.1.1 Activity level | ||
|
|
||
| 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. | ||
| Activity providing a context for multiple features | ||
|
|
||
| #### 1.1.2 Model | ||
| #### 1.1.2 Feature level | ||
|
|
||
| Contains all the model related files. | ||
| Feature level encapsulates all packages and classes required for delivering specific feature. | ||
|
|
||
| #### 1.1.3 Injection | ||
|
|
||
| We use depency injection usually using [Dagger 2](http://google.github.io/dagger/). This package will contain all the dependency injection related files (modules, components, qualifiers, scopes...). | ||
|
|
||
| #### 1.1.4 Ui | ||
| Injection package will contain all components, modules, qualifiers and scopes used by this feature. If this feature requires more elaborate injection it is worth to split the package further by role | ||
|
|
||
| Contains all ui related files (Views, adapters...). Try to group ui files for different parts of the app into different subpackages. | ||
|
|
||
| #### 1.1.5 Persistence | ||
| * components | ||
| * modules | ||
| * scopes | ||
| * qualifiers | ||
|
|
||
| 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. | ||
| #### 1.1.4 Presenter | ||
|
|
||
| #### 1.1.6 Analytics | ||
| Presentation layer will contain interface and implementation(s) of view logic | ||
|
|
||
| Contains all the analytics related files | ||
| #### 1.1.5 Repository | ||
|
|
||
| #### 1.1.7 Ads | ||
| Repository package will encapsulate all interfaces and logic around requesting data from external and internal sources. It may contain packages e.g. | ||
|
|
||
| Contains all the adverts related files | ||
| * parsers | ||
| * models | ||
| * handlers | ||
|
|
||
| #### 1.1.8 Utils | ||
| #### 1.1.6 Navigator | ||
|
|
||
| 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...) | ||
| Navigator interface defines all navigational operations that can be performed on Fragments/Activity inside this Activity. e.g. | ||
|
|
||
| public interface HomeSearchNavigator { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've seen "Navigator" objects implemented at TAB and in my personal opinion they end up always being a mess and implemented by the activities, which specially it we are following the MVC pattern this logic is part of the controller. The controller could delegate this logic to a "navigator" but generally if the app is not really complex it's not even worth it, plus I recommend the intent to be created as a static method in the activity you want to launch, that way is really easy to know entry points to that activity/fragment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be more straightforward to do it the way Juan was saying. Subject to complexity.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also second the static intent route There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that activity gets messy, depending on size and structure of app (activity/fragments). |
||
| void onFixedSearch(String search); | ||
| void onRawSearch(String search); | ||
| void onOpenSettings(); | ||
| void onExit(); | ||
| } | ||
|
|
||
| Implementation will handle creation of intents and dispatching them. | ||
|
|
||
|
|
||
| ## 2. Coding Style | ||
|
|
||
| At TAB we follow [Android Code Style](https://source.android.com/source/code-style.html#follow-field-naming-conventions) guidelines | ||
|
|
||
|
|
||
| On top of rules listed there we have few extra rules and exceptions: | ||
|
|
||
| ### 2.1 Name conventions | ||
|
|
||
| - **Do NOT use m or s prefix in fields inside POJO (models, events)** | ||
|
|
||
| *good:* | ||
|
|
||
| ``` java | ||
| 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:* | ||
|
|
||
| ``` java | ||
| 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() | ||
|
|
||
| ### 2.2 Constants | ||
|
|
||
| - Always user string resources for user facing strings. | ||
| - Define all Intent extra keys, FragmentArgument keys, FragmentManager tags, "magic" values, etc as constants | ||
|
|
||
| *good:* | ||
|
|
||
| ``` java | ||
| 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:* | ||
|
|
||
| ``` java | ||
| 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](https://developer.android.com/tools/debugging/annotations.html#enum-annotations) when possible | ||
|
|
||
| *good:* | ||
|
|
||
| ``` java | ||
| public static final int ONE = 0; | ||
| public static final int TWO = 1; | ||
| publci static final int THREE = 2; | ||
| ``` | ||
|
|
||
| *bad:* | ||
|
|
||
| ``` java | ||
| public enum Values{ | ||
| ONE(0), TWO(1), THREE(2) | ||
| } | ||
| ``` | ||
|
|
||
| ### 2.3 Use Standard Brace Style | ||
|
|
||
| - Avoid one line braceless conditions | ||
|
|
||
| *good:* | ||
|
|
||
| ``` java | ||
| if (condtion) { | ||
| body(); | ||
| } | ||
| ``` | ||
|
|
||
| *bad:* | ||
|
|
||
| ``` | ||
| if (condition) | ||
| body() | ||
| ``` | ||
|
|
||
| *or:* | ||
|
|
||
| ``` java | ||
| if (condition) body(); | ||
| ``` | ||
|
|
||
| ### 2.4 Switch statements | ||
|
|
||
| - 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:* | ||
|
|
||
| ``` java | ||
| 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:* | ||
|
|
||
| ``` java | ||
| 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:* | ||
|
|
||
| ``` java | ||
| switch (expresion) { | ||
| case CASE_1: | ||
| // code | ||
| break; | ||
| case CASE_2: | ||
| // code | ||
| break; | ||
| } | ||
| ``` | ||
|
|
||
| *bad:* | ||
|
|
||
| ``` java | ||
| switch (expresion) { | ||
| case CASE_1: | ||
| // code | ||
| break; | ||
| case CASE_2: | ||
| // code | ||
| break; | ||
| default: | ||
| } | ||
| ``` | ||
|
|
||
| ### 2.5 Use Standard Java Annotations | ||
|
|
||
| - 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) | ||
|
|
||
|
|
||
| ### 2.6 Log Sparingly | ||
|
|
||
| Implement or use Logger which allows filtering log calls per subsystem. It's recommended to use [Timber](https://github.com/JakeWharton/timber) | ||
|
|
||
| *good:* | ||
| We are migrating from AOSP (Android Open Source Project) code guidelines to Google Java Style which can be found here | ||
|
|
||
| TabLog.e(TabLog.UI,"Cannot determine image size"); | ||
| [Google Java Style](https://google.github.io/styleguide/javaguide.html) | ||
|
|
||
| *not so good:* | ||
|
|
||
| Log.e("App", "Cannot determine image size"); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personal opinion but I'm not a fan of having the network, model, parsers and persistence layer together
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Network should definitely separated from repository.