diff --git a/README.md b/README.md index 731c15f..6e7a1d5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The project is a straightforward Github API client containing 3 screens (user se * repository - repository detail * base - module containing a code shared between all modules -![Project structure](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/app_diagram.png "Project structure") +![Project structure](docs/img/app_diagram.png "Project structure") ### Dependencies management It is easy to get lost with dependencies management across different project modules (especially with libs versions). To make it easier, take a look at `buildsystem/dependencies.gradle` where everything is configured. Each module has separate configuration, with additional two for testing and annotation processing. Like some other patterns, this was originally introduced in [Azimo](https://azimo.com) Android application by [@dbarwacz](https://github.com/dbarwacz). @@ -38,7 +38,7 @@ Here are some highlights from it: For the rest take a look at the code - should be self-explaining. As this is just self-invented setup (that works on production!), all kind of feedback is warmly welcomed. -![Dagger structue](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/dagger_diagram.png "Dagger structure") +![Dagger structue](docs/img/dagger_diagram.png "Dagger structure") ## Unit Testing @@ -108,7 +108,7 @@ or The repository contains shared configurations for running Unit Tests directly from Android Studio. -![Android Studio configs](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/as_run_configurations.png "Android Studio configs") +![Android Studio configs](docs/img/as_run_configurations.png "Android Studio configs") #### All tests at once @@ -118,14 +118,14 @@ Recently it is not always possible to run all tests at once (see troubleshooting Run `App and modules tests`. This configuration will run unit tests from all modules in separate tabs, one after another. To modify list of tests, click on `Edit Configurations` -> `App and modules tests` -> `Before Launch`. -![All tests sequential](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/all_tests_sequential.png "All tests sequential") +![All tests sequential](docs/img/all_tests_sequential.png "All tests sequential") #### Troubleshooting * **Class not found: ... Empty test suite.** There is a bug in Android Studio which prevents from launching all unit tests at once, before their code is generated (what happens after the first run of unit tests for every single module independently). For more take a look at Android Studio bug tracker: https://issuetracker.google.com/issues/111154138. -![Android Studio issues](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/failing_all_tests.png "Android Studio issues") +![Android Studio issues](docs/img/failing_all_tests.png "Android Studio issues") ## Test coverage for unit tests @@ -157,7 +157,7 @@ buildTypes { * Task `testDebugUnitTestCoverage` depends on `testDebugUnitTest` tasks (each module separately). Thanks to it all sources required for coverage report are available before gradle starts generating it (in `/build/...`. -![Coverage report](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/coverage_report.png "Coverage report") +![Coverage report](docs/img/coverage_report.png "Coverage report") ## Instrumentation Testing @@ -173,7 +173,7 @@ To run all Instrumentation tests from all modules at once launch emulator or plu ``` When all goes fine, you should see testing report in `app/build/reports/androidTests/connected/` directory. -![Instrumentation test report](https://raw.githubusercontent.com/frogermcs/MultiModuleGithubClient/master/docs/img/instrumentation_report_example.png "Instrumentation test report") +![Instrumentation test report](docs/img/instrumentation_report_example.png "Instrumentation test report") ### Functional vs End-to-end testing From the high level, Android Instrumentation tests can be split into two types: functional and end-to-end. You can check my [article](https://medium.com/azimolabs/automated-testing-will-set-your-engineering-team-free-a89467c40731) about how we do QA at Azimo to see what is the difference between both of them. @@ -195,3 +195,56 @@ It is, because `app` module doesn't have access to `RepositoryDetailsActivity` c What about end-to-end tests? They shouldn't be problematic, simply because tests shouldn't have knowledge about specific implementation, but rather how user interface is composed (so again, not: `withText("R.string.show_repos")` but `withText("Show repositories")`). More cases: TBD + + +## Proguard + +Proguard configuration isn't very different in standard and multi-feature project configuration. Minification process is enabled in `app/build.gradle` [file](app/build.gradle): + +```groovy +buildTypes { + debug { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), + 'proguard-rules.pro' + testProguardFile 'proguard-test-rules.pro' + } + //... +} +``` + +For proguard configuration and know-how we could create completely separate demo project and a bunch of articles. Instead, just take a look at screenshots that compare apk files built from this project, with and without minification enabled. + +#### Project without proguard + +![No proguard config](docs/img/no-proguard.png "No proguard config") + +#### Project with proguard + +![With proguard config](docs/img/with-proguard.png "With proguard config") + +### Proguard config for project modules + +It is also possible to Provide proguard configuration for each module separately. Why would you like to do this? Usually Proguard configuration is set in app's module gradle file. Also all global flags `-dontoptimize` also should be set there. +But sometimes there are module-specific configurations. So for example you would like to keep methods or classes, even if they aren't used in app's module. Also when you share .aar library file, you can provide it with Proguard configuration built in. +In this situation you should use `consumerProguardFiles`. For example, see `features/base/build.gradle` (file)[features/base/feature-base-proguard-rules.pro]: + +```groovy +buildTypes { + all { + consumerProguardFiles 'feature-base-proguard-rules.pro' + } +} +``` + +Configuration tells: + +``` +-keep class com.frogermcs.multimodulegithubclient.base.BaseActivity { + public void notUsedMethod(); +} +``` + +It means that method `notUsedMethod()` from class (BaseActivity)[features/base/src/main/java/com/frogermcs/multimodulegithubclient/base/BaseActivity.java] will be kept, no matter what. + +For more details, take a look at [this blog post](https://proandroiddev.com/handling-proguard-as-library-developer-or-in-a-multi-module-android-application-2d738c37890) that describes how to setup Proguard for multi-module android app. diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b4a15fd..b7ffcdd 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -117,6 +117,7 @@ # === App UI === + -keep class androidx.recyclerview.widget.RecyclerView { public ; } diff --git a/docs/img/no-proguard.png b/docs/img/no-proguard.png new file mode 100644 index 0000000..c922be9 Binary files /dev/null and b/docs/img/no-proguard.png differ diff --git a/docs/img/with-proguard.png b/docs/img/with-proguard.png new file mode 100644 index 0000000..8f575a0 Binary files /dev/null and b/docs/img/with-proguard.png differ diff --git a/features/base/build.gradle b/features/base/build.gradle index 318a488..927732b 100644 --- a/features/base/build.gradle +++ b/features/base/build.gradle @@ -5,6 +5,12 @@ apply from: '../../buildsystem/android_commons.gradle' android { baseFeature true + + buildTypes { + all { + consumerProguardFiles 'feature-base-proguard-rules.pro' + } + } } dependencies { diff --git a/features/base/feature-base-proguard-rules.pro b/features/base/feature-base-proguard-rules.pro new file mode 100644 index 0000000..2270543 --- /dev/null +++ b/features/base/feature-base-proguard-rules.pro @@ -0,0 +1,6 @@ +# === Base module proguard configuration === + +-keep class com.frogermcs.multimodulegithubclient.base.BaseActivity { + public void notUsedMethod(); +} + diff --git a/features/base/src/main/java/com/frogermcs/multimodulegithubclient/base/BaseActivity.java b/features/base/src/main/java/com/frogermcs/multimodulegithubclient/base/BaseActivity.java index 866875b..cc010ee 100644 --- a/features/base/src/main/java/com/frogermcs/multimodulegithubclient/base/BaseActivity.java +++ b/features/base/src/main/java/com/frogermcs/multimodulegithubclient/base/BaseActivity.java @@ -22,4 +22,15 @@ public void setContentView(int layoutResID) { protected abstract void setupActivityComponent(); public abstract String getScreenName(); + + /* + This method isn't used anywhere. But it won't disappear during minification process. It's + because we use added proguard configuration for module telling that this method should be + kept. See features/base/build.gradle -> consumerProguardFiles for more details. + */ + public void notUsedMethod() { + int a = 1; + int b = 3; + int ab = a + b; + } } \ No newline at end of file