Organizing Android Code
August 8, 2021 by Hrithik Sharma
Tips to keep your android project files and code organized
Overview
Android applications should always be neatly organized with a clear folder structure that makes your code easy to read. In addition, proper naming conventions for code and classes are important to ensure your code is clean and maintainable.
The importance of an organized code structure
It’s all about readability. Big projects can be difficult to organize due to the amount of classes, resource files and libraries used.
Having an organized code structure involves separating classes into different packages according to their purpose and having proper names for your resource files.
1. Project guidelines
1.1 Project structure
New projects should follow the Android Gradle project structure that is defined on the Android Gradle plugin user guide.
1.2 File naming
Java Classes
-
Class names are written in UpperCamelCase.
-
Android classes should be named with a particular convention that makes their purpose clear in the name. For example all activities should end with
Activity
as inMoviesActivity
. The following are the most important naming conventions:
Name | Convention | Inherits |
---|---|---|
Activity | CreateTodoItemActivity |
AppCompatActivity , Activity |
List Adapter | TodoItemsAdapter |
BaseAdapter , ArrayAdapter |
Database Helper | TodoItemsDbHelper |
SQLiteOpenHelper |
Fragment | TodoItemDetailFragment |
Fragment |
Service | FetchTodoItemService |
Service , IntentService |
Resources file names are written in lowercase_underscore.
-
Drawable files
Naming conventions for drawables:
Asset Type Prefix Example Button btn_
btn_send_pressed.png
Dialog dialog_
dialog_top.png
Divider divider_
divider_horizontal.png
Action bar ab_
ab_stacked.png
Icon ic_
ic_star.png
Menu menu_
menu_submenu_bg.png
Notification notification_
notification_bg.png
Tabs tab_
tab_pressed.png
Asset Type | Prefix | Example |
---|---|---|
Icons | ic_ |
ic_star.png |
Launcher icons | ic_launcher |
ic_launcher_calendar.png |
Menu icons and Action Bar icons | ic_menu |
ic_menu_archive.png |
Status bar icons | ic_stat_notify |
ic_stat_notify_msg.png |
Tab icons | ic_tab |
ic_tab_recent.png |
Dialog icons | ic_dialog |
ic_dialog_info.png |
-
Layout files
Layout files should match the name of the Android components that they are intended for but moving the top level component name to the beginning. For example, if we are creating a layout for the
SignInActivity
, the name of the layout file should beactivity_sign_in.xml
.
Component | Class Name | Layout Name |
---|---|---|
Activity | UserProfileActivity |
activity_user_profile.xml |
Fragment | SignUpFragment |
fragment_sign_up.xml |
Dialog | ChangePasswordDialog |
dialog_change_password.xml |
-
Values files
Resource files in the values folder should be plural, e.g.
strings.xml
,styles.xml
,colors.xml
,dimens.xml
,attrs.xml
2. Code guidelines
2.1 Java language rules
- Don’t ignore exceptions:
It can be tempting to write code that ignores an exception, such as:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
Don’t do this. While you may think your code will never encounter this error condition or that it isn’t important to handle it, ignoring this type of exception creates mines in your code for someone else to trigger some day. You must handle every exception in your code in a principled way. (Android code style guidelines)
- Don’t catch generic exception
You should not do this:
try {
someComplicatedIOFunction(); // may throw IOException
someComplicatedParsingFunction(); // may throw ParsingException
someComplicatedSecurityFunction(); // may throw SecurityException
} catch (Exception e) { // I'll just catch all exceptions
handleError(); // with one generic handler!
}
In almost all cases, it’s inappropriate to catch generic Exception
or Throwable
(preferably not Throwable
because it includes Error
exceptions).
It obscures the failure- handling properties of your code, meaning if someone adds a new type of exception in the code you’re calling, the compiler won’t point out that you need to handle the error differently. In most cases you shouldn’t be handling different types of exceptions in the same way.
Catch each exception separately as part of a multi-catch block, for example:
try {
...
} catch (ClassNotFoundException | NoSuchMethodException e) {
...
}
- Fully qualify imports
This is bad: import foo.*;
This is good: import foo.Bar;
2.2 Java library rules
There are conventions for using Android’s Java libraries and tools. In some cases, the convention has changed in important ways and older code might use a deprecated pattern or library. When working with such code, it’s okay to continue the existing style. When creating new components however, never use deprecated libraries.
2.3 Java style rules
- Use Javadoc standard comments
Every file should have a copyright statement at the top, followed by package and import statements (each block separated by a blank line), and finally the class or interface declaration. In the Javadoc comments, describe what the class or interface does.
/*
* Copyright 2020 The Android Open Source Project
*
* 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.
*/
package com.android.internal.foo;
import android.os.Blah;
import android.view.Yada;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Does X and Y and provides an abstraction for Z.
*/
public class Foo {
...
}
- Fields definition and naming
Fields should be defined at the top of the file and they should follow the naming rules listed below.
- Private, non-static field names start with m.
- Private, static field names start with s.
- Other fields start with a lower case letter.
- Static final fields (constants) are ALL_CAPS_WITH_UNDERSCORES.
Example:
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
- String constants, naming, and values
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_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
2.4 Logging guidelines
Use the logging methods provided by the Log
class to print out error messages or other information that may be useful for developers to identify issues:
Log.v(String tag, String msg)
(verbose)Log.d(String tag, String msg)
(debug)Log.i(String tag, String msg)
(information)Log.w(String tag, String msg)
(warning)Log.e(String tag, String msg)
(error)
As a general rule, we use the class name as tag and we define it as a static final
field at the top of the file. For example:
public class MyClass {
private static final String TAG = MyClass.class.getSimpleName();
public myMethod() {
Log.e(TAG, "My error message");
}
}
VERBOSE and DEBUG logs must be disabled on release builds. It is also recommended to disable INFORMATION, WARNING and ERROR logs but you may want to keep them enabled if you think they may be useful to identify issues on release builds. If you decide to leave them enabled, you have to make sure that they are not leaking private information such as email addresses, user ids, etc.
To only show logs on debug builds:
if (BuildConfig.DEBUG) Log.d(TAG, "The value of x is " + x);
2.5 Line length limit
Code lines should not exceed 100 characters. If the line is longer than this limit there are usually two options to reduce its length:
- Extract a local variable or method (preferable).
- Apply line-wrapping to divide a single line into multiple ones.
There are some methods with the help of which we can organize the lines with much characters:
-
Method chain case
When multiple methods are chained in the same line - for example when using Builders - every call to a method should go in its own line, breaking the line before the
.
Picasso.with(context).load("foo.jpg").into(imageView);
Picasso.with(context) .load("foo.jpg") .into(imageView);
-
Long parameters case
When a method has many parameters or its parameters are very long, we should break the line after every comma
,
loadPicture(context, "foo.jpg", mImageViewProfilePicture, clickListener, "Title of the picture");
loadPicture(context, "foo.jpg", mImageViewProfilePicture, clickListener, "Title of the picture");
2.6 Methods and Classes limits
-
Any method should not contain more than 30 lines of code (not counting line spaces and comments). If your method goes above the limit then it is highly recommended to devide your task into sub-tasks and create new methods to handle them.
-
Any class should not contain more than 20 methods (resulting in up to 600 lines of code in a class). If your code breaks the limit then create a new class.
-
Any package should not contain more than 30 classes, thus comprising 18,000 lines of code.
-
Also, packages should be created according to the tasks. Like, a seperate package for making queries upon data on the cloud, a package that handle all the activity classes, a package for model classes, and-so-on.
Final Words
Each project is unique, and each case is a case but believe that keeping classes single-purpose and organized in a clean and easy to understand way, makes development easier and more enjoyable.
The important thing is that in the end you’ll be able to look at your code and feel that sweet light-weighted sensation of a well done job.
I hope you find it useful or at least it gives you some fresh ideas.
Thanks for reading 🙌🏼
Happy coding! 😇
BTW, I, am an Android Developer, solve real-world problems by building useful apps. Have a look at our portfolio.