How I organize Android project structure

Rey Pham
3 min readJun 9, 2016

--

In my recent project Material Unit Converter , I have a chance to try out various interesting things like RxJava, MVP, Clean Architecture, …

I will have a post about my experiences with all those trending in other day, but today I want to show you my Android project structure that helps me a lot in managing my codebase.

I. Sensitive data

There are always some sensitive data in your project. Whether it be keystore’s password or third-party API secret key, you want to keep it out of version control system. You can put it in gradle.properties file of your project like below:

KEYSTORE_PASSWORD=<your keystore's password>
KEYSTORE_FILE=<your keystore file>
KEYSTORE_ALIAS=<your keystore's alias>

Later you can use those properties in build.gradle file:

signingConfigs {
release {
storeFile file(KEYSTORE_FILE)
storePassword KEYSTORE_PASSWORD
keyAlias KEYSTORE_ALIAS
keyPassword KEYSTORE_PASSWORD
}
}

Of course, you need to make sure not committing this file to VCS. But if you are lazy like me, you can put all those properties to gradle.properties file in GRADLE_HOME folder.

II.Dependencies

To manage all my project’s dependencies, I put it in a library.gradle file:

ext.version = [
support : "23.2.1",
rxjava : "1.1.0",
rxandroid : "1.1.0",

// rest of libraries
]

def version = ext.version;

ext.library = [
support_compat :
"com.android.support:appcompat-v7:${version.support}",
support_design : "com.android.support:design:${version.support}",
rxjava :
"io.reactivex:rxjava:${version.rxjava}",
rxandroid :
"io.reactivex:rxandroid:${version.rxandroid}",
// rest of libraries
]

The file is stored in buildsystem folder of the project.

Then in build.gradle file:

apply from: "$rootProject.projectDir/buildsystem/library.gradle"def library = ext.library;dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "${library.support_compat}"
compile "${library.support_design}"
compile "${library.rxjava}"
compile "${library.rxandroid}"
// rest of dependencies
}

III. Configuration

Like dependencies, I use config.gradle file to store all project configurations.

ext.configuration = [
applicationId : <your app package name>,
versionMajor : 1,
versionMinor : 0,
versionPatch : 2,
versionClassifier : "",
minimumSdkVersion : 14,
compileSdkVersion : 23,
targetSdkVersion : 23,
buildToolsVersion : "23.0.2"
]

def configuration = ext.configuration;

ext.buildVersionCode = {
return configuration.minimumSdkVersion * 10000000 + configuration.versionMajor * 10000 + configuration.versionMinor * 100 + configuration.versionPatch
}

ext.buildVersionName = {
String versionName = ext.buildVersionNameWithoutClassifier();
if (configuration.versionClassifier != null && !configuration.versionClassifier.isEmpty()) {
versionName = versionName + "-" + configuration.versionClassifier
}
return versionName;
}

You can see that I create buildVersionCode function to generate version code with first 2 digits for minimum SDK version, 1 digit for reverse, then follow with major, minor and patch number, each has 2 digits. For version name, I use format “ major.minor.patch-Classifier”.

Now we can use it in build.gradle file:

apply from: "$rootProject.projectDir/buildsystem/config.gradle"def configuration = ext.configuration;

android {
compileSdkVersion configuration.compileSdkVersion
buildToolsVersion configuration.buildToolsVersion

defaultConfig {
applicationId configuration.applicationId
minSdkVersion configuration.minimumSdkVersion
targetSdkVersion configuration.targetSdkVersion
versionCode buildVersionCode()
versionName buildVersionName()
}
}

IV. Source code packages

Here is my packages structure:

Each data, domain, repository package contains classes of a Clean Architecture’s layer.

common package contains some utility classes. dependency package contains classes that are used to provide dependency injection (Dagger).

Each app’s feature will have it own child package in feature package. It should be the presentation layer of Clean Architecture.

And the last, unitconverter package contains classes that depend on Android framework like Application, Activity, View.

V. Test source folder

I separate test codes to multi-source folders like below:

android {
...
sourceSets {
// Common code for all tests
test.java.srcDir 'src/commonTests/java'
androidTest.java.srcDir 'src/commonTests/java'

// Unit tests
test.java.srcDir 'src/unitTests/java'

// Integration tests
test.java.srcDir 'src/integrationTests/java'

// Unit tests for debug build variant
testDebug.java.srcDir 'src/debugUnitTests/java'

// Unit tests for release build variant
testRelease.java.srcDir 'src/releaseUnitTests/java'

// Functional tests
androidTest.java.srcDir 'src/functionalTests/java'
}
...
}

You can see we have commonTests source folder for any common class used in test. Each type of test is stored in different source folder, and each build variant also has it own test source folder.

VI. Conclusion

With this structure, my project has become more manageable. In future, I will update this post if I find out way to improve it.

--

--