{"id":19894821,"url":"https://github.com/bartoszjarocki/android-starter","last_synced_at":"2025-08-21T01:04:47.092Z","repository":{"id":83155756,"uuid":"76198055","full_name":"BartoszJarocki/android-starter","owner":"BartoszJarocki","description":"Project template for new Android applications","archived":false,"fork":false,"pushed_at":"2017-01-30T19:14:31.000Z","size":202,"stargazers_count":6,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-07T05:11:15.336Z","etag":null,"topics":["android","android-architecture","android-sdk","rxandroid","rxjava"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BartoszJarocki.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-11T20:28:52.000Z","updated_at":"2023-03-07T14:21:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"416acaac-9594-48c0-bb5f-2821ea057c18","html_url":"https://github.com/BartoszJarocki/android-starter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BartoszJarocki%2Fandroid-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BartoszJarocki%2Fandroid-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BartoszJarocki%2Fandroid-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BartoszJarocki%2Fandroid-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BartoszJarocki","download_url":"https://codeload.github.com/BartoszJarocki/android-starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252103960,"owners_count":21695392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-architecture","android-sdk","rxandroid","rxjava"],"created_at":"2024-11-12T18:34:46.098Z","updated_at":"2025-05-02T20:30:46.390Z","avatar_url":"https://github.com/BartoszJarocki.png","language":"Java","readme":"# Android Starter\n\nSample Android application used as a reference for a new projects. It showcases\narchitecture, tools and guidelines which I use when developing for Android.\n\n## Requirements\n* Android SDK\n* Latest Android version\n* Latest Android SDK Tools\n* Latest Android Build tools\n* Android Support Repository\n* JDK 1.8\n\n## External libraries\nApplication:\n* Retrofit - API consuming\n* OkHttp - HTTP client\n* GSON - JSON parsing\n* Picasso - image downloading and caching\n* Timber - easier logging\n* ButterKnife - view binding\n* Dagger 2 - dependency injection\n* RxJava, RxAndroid - reactive extensions for Android\n* Stetho - debugging\n* LeakCannary - detecting memory leaks\n* ObjectBox - data persistence \n\nTests\n* JUnit - unit testing framework\n* Espresso - UI testing framework\n* Mockito - mocking framework for unit tests in Java\n* RESTMock - mocking network layer\n\n## Building and running\nFrom the root of the project run:\n```\n./gradlew installRunDebug\n```\n\n## Tests\n* To run **unit** tests:\n```\n./gradlew test\n```\n\nUsually your presenters will execute asynchronous code. To make code synchronous for unit test purposes schedulers should be configurable See the code below:\n\n```java\npublic class ThreadConfiguration {\n    private Scheduler subscribeOnScheduler;\n    private Scheduler observeOnScheduler;\n\n    public ThreadConfiguration(final Scheduler subscribeOnScheduler,\n        final Scheduler observeOnScheduler) {\n        this.subscribeOnScheduler = subscribeOnScheduler;\n        this.observeOnScheduler = observeOnScheduler;\n    }\n\n    public \u003cT\u003e Observable.Transformer\u003cT, T\u003e applySchedulers() {\n        return observable -\u003e observable.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler);\n    }\n}\n```\nThreadConfiguration should be used with compose operator from RxJava:\n```java\npublic class ApiManager {\n\n    private final ThreadConfiguration threadConfiguration;\n    private final Api api;\n\n    @Inject\n    public ApiManager(@NonNull Api api, @NonNull ThreadConfiguration threadConfiguration) {\n        this.api = api;\n        this.threadConfiguration = threadConfiguration;\n    }\n\n    public Observable\u003cResponse\u003cList\u003cContributor\u003e\u003e\u003e contributors(String owner, String repo) {\n        return api.contributors(owner, repo).compose(threadConfiguration.applySchedulers());\n    }\n}\n```\nThen in unit test it's possible to use the same thread for both subscribeOn and observeOn schedulers:\n```java\n    ApiManager apiManager;\n    ThreadConfiguration threadConfiguration =\n        new ThreadConfiguration(Schedulers.immediate(), Schedulers.immediate());\n\n    @Before\n    public void before() {\n        MockitoAnnotations.initMocks(this);\n\n        apiManager = new ApiManager(api, threadConfiguration);\n    }\n    ...\n```\nAnd this will make your code synchronous.\n\n* To run **functional** tests:\n```\n./gradlew connectedAndroidTest\n```\n\n## Code analysis\n* PMD (PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. ) [more info here][71fd51a7]\n```\n./gradlew pmd\n```\n* FindBugs (another static analysis tool) [more info here][e0b190a2]\n```\n./gradlew findbugs \n```\n* Checkstyle (ensures that code style follows coding standard) [more info here][51d80db7]\n```\n./gradlew checkstyle\n```\n\n  [71fd51a7]: https://pmd.github.io/ \"PMD - website\"\n  [e0b190a2]: http://findbugs.sourceforge.net/ \"FindBugs - website\"\n  [51d80db7]: http://checkstyle.sourceforge.net/ \"Checkstyle - website\"\n\n## Check task\nTo run code analysis tools and unit tests run:\n```\n./gradlew clean check\n```\n\nThis will run **Checkstyle**-\u003e**Findbugs**-\u003e**PMD**-\u003e**Android Lint**-\u003e**Unit tests**\n\n## Architecture\n\nApplication follows the Model-View-Presenter architecture.\nSee the below diagram to get more details.\n\n![MVP](/art/mvp.png)\n\nThis application uses basic MVP setup. If you want to handle config changes (rotation etc.) you'll have to persist presenter instances. To read more about MVP I strongly recommend the following articles:\n* [Mosby][5a0918ea]\n* [Introduction to MVP on Android][5cf2f9e8]\n* [Presentation Model and Passive View in MVP — The Android way][75fd7fde]\n\n  [5a0918ea]: http://hannesdorfmann.com/mosby/mvp/ \"MOSBY MVP\"\n  [5cf2f9e8]: https://github.com/konmik/konmik.github.io/wiki/Introduction-to-Model-View-Presenter-on-Android \"Introduction to MVP on Android\"\n  [75fd7fde]: https://medium.com/@andrzejchm/presentation-model-and-passive-view-in-mvp-the-android-way-fdba56a35b1e#.s82zxg66f \"Presentation Model and Passive View in MVP — The Android way\"\n  \n## Offline\nEverytime data is fetched from API it is also saved in local database (currently ObjectBox). In case of no internet connection local data will be displayed. Application uses repository pattern to achieve this behavior.\n\n```java\npublic class AppRepository implements Repository {\n\n    private final LocalRepository localRepository;\n    private final RemoteRepository remoteRepository;\n\n    @Inject\n    public AppRepository(final LocalRepository localRepository,\n        final RemoteRepository remoteRepository) {\n        this.localRepository = localRepository;\n        this.remoteRepository = remoteRepository;\n    }\n\n    @Override\n    public Observable\u003cList\u003cContributor\u003e\u003e contributors(final String owner, final String repo) {\n        return Observable.concat(remoteRepository.contributors(owner, repo),\n            localRepository.contributors(owner, repo))\n            .first(contributorsResponse -\u003e !contributorsResponse.isEmpty());\n    }\n}\n```\n\n```java\npublic class RemoteRepository implements Repository {\n\n    private final LocalRepository localRepository;\n    private final ThreadConfiguration threadConfiguration;\n    private final GithubApi githubApi;\n    private final ContributorsJsonsToContributorListMapper\n        contributorsJsonsToContributorListMapper = new ContributorsJsonsToContributorListMapper();\n\n    @Inject\n    public RemoteRepository(final LocalRepository localRepository, @NonNull GithubApi githubApi,\n        @NonNull ThreadConfiguration threadConfiguration) {\n        this.localRepository = localRepository;\n        this.githubApi = githubApi;\n        this.threadConfiguration = threadConfiguration;\n    }\n\n    public Observable\u003cList\u003cContributor\u003e\u003e contributors(String owner, String repo) {\n        return githubApi.contributors(owner, repo)\n            .flatMap(response -\u003e {\n                if (response.isSuccessful()) {\n                    return Observable.just(response.body());\n                } else {\n                    return Observable.error(new RuntimeException());\n                }\n            })\n            .doOnNext(localRepository::put)\n            .flatMap(contributors -\u003e Observable.just(\n                contributorsJsonsToContributorListMapper.map(contributors)))\n            .onErrorResumeNext(throwable -\u003e {\n                if (throwable instanceof IOException) { // network errors\n                    return Observable.empty(); // show local data!\n                }\n\n                return Observable.error(throwable);\n            })\n            .compose(threadConfiguration.applySchedulers());\n    }\n}\n```\n\n```java\npublic class LocalRepository implements Repository {\n    private final Box\u003cContributorEntity\u003e localContributorBox;\n    private final ThreadConfiguration threadConfiguration;\n    private final ContributorsJsonsToContributorEntitiesMapper\n        contributorsJsonsToContributorEntitiesMapper =\n        new ContributorsJsonsToContributorEntitiesMapper();\n    private final ContributorsEntitiesToContributorsMapper entitiesToContributorsMapper =\n        new ContributorsEntitiesToContributorsMapper();\n\n    public LocalRepository(final Box\u003cContributorEntity\u003e localContributorBox,\n        final ThreadConfiguration threadConfiguration) {\n        this.localContributorBox = localContributorBox;\n        this.threadConfiguration = threadConfiguration;\n    }\n\n    @Override\n    public Observable\u003cList\u003cContributor\u003e\u003e contributors(final String owner, final String repo) {\n        localContributorBox.removeAll();\n\n        return Observable.defer(() -\u003e Observable.just(localContributorBox.getAll()))\n            .flatMap(contributorEntities -\u003e Observable.just(\n                entitiesToContributorsMapper.map(contributorEntities)))\n            .compose(threadConfiguration.applySchedulers());\n    }\n\n    public void put(final List\u003cContributorJson\u003e contributorJsons) {\n        localContributorBox.put(contributorsJsonsToContributorEntitiesMapper.map(contributorJsons));\n    }\n}\n```\n\n\n## Project setup\nIf you want to start a new project based on this boilerplate do the following steps:\n* Download this repository as a zip\n* Change the package name \n  * Rename packages in ```main```, ```androidTest``` and ```test``` \n  * Rename applicationId in ```build.gradle```\n  * Rename package name in ```src/main/AndroidManifest.xml``` and ```src/androidTest/AndroidManifest.xml```\n* Init a new git repository\n* Make sure you want all the dependencies included in this boilerplate\n\n## License\n```\nCopyright 2016 Bartosz Jarocki\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n  ```\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbartoszjarocki%2Fandroid-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbartoszjarocki%2Fandroid-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbartoszjarocki%2Fandroid-starter/lists"}