Showcase is a sample project that presents a modern, 2021 approach to Android application development.
The goal of the project is to combine popular libraries/tools and demonstrate best developement practices by utilizing up to date tech-stack and presenting modern Android application Architecture that is modular, scalable, maintainable, and testable. This application may look simple,but it has all the pieces that will provide the rock-solid foundation for the larger app suitable for bigger teamsand long application lifecycle.
This project is being maintained to match industry standards. Please check CONTRIBUTING page if you want to help.
This project takes advantage of best practices, many popular libraries and tools in the Android ecosystem. Most of the libraries are in the stable version unless there is a good reason to use non-stable dependency.
Feature related code is placed inside one of the feature modules.We can think about each feature as the equivalent of microservice or private library.
The modularized code-base approach provides few benefits:
This diagram presents dependencies between project modules (Gradle sub-projects).
Note that due usage of Android dynamic-feature
module dependencies are reversed (feature modules are depending on app
module, not another way around).
We have three kinds of modules in the application:
app
module - this is the main module. It contains code that wires multiple modules together (dependency injection setup, NavHostActivity
, etc.) and fundamental application configuration (retrofit configuration, required permissions setup, custom application class, etc.).library_x
modules that some of the features could depend on. This is helpful if you want to share some assets or code only between few feature modules (currently app has no such modules)Clean architecture
is the "core architecture" of the application, so each feature module
contains own set of Clean architecture layers:
Notice that
app
module andlibrary_x
modules structure differs a bit from feature module structure.
Each feature module contains non-layer components and 3 layers with distinct set of responsibilities.
This layer is closest to what the user sees on the screen. The presentation
layer is a mix of MVVM
(Jetpack ViewModel
used to preserve data across activity restart) andMVI
(actions
modify the common state
of the view and then new state is edited to a view via LiveData
to be rendered).
common state
(for each view) approach derives fromUnidirectional Data Flow and Reduxprinciples.
Components:
LiveData
) state changes to the view and deals with user interactions (these view models are not simply POJO classes).NavHostActivity
(instead of separately, inside each view)This is the core layer of the application. Notice that the domain
layer is independent of any other layers. This allows to make domain models and business logic independent from other layers.In other words, changes in other layers will have no effect on domain
layer eg. changing database (data
layer) or screen UI (presentation
layer) ideally will not result in any code change withing domain
layer.
Components:
domain
layer independent from the data layer
(Dependency inversion).Manages application data and exposes these data sources as repositories to the domain
layer. Typical responsibilities of this layer would be to retrieve data from the internet and optionally cache this data locally.
Components:
Repository is exposing data to the domain
layer. Depending on application structure and quality of the external APIs repository can also merge, filter, and transform the data. The intention ofthese operations is to create high-quality data source for the domain
layer, not to perform any business logic (domain
layer use case
responsibility).
Mapper - maps data model
to domain model
(to keep domain
layer independent from the data
layer).
RetrofitService - defines a set of API endpoints.
DataModel - defines the structure of the data retrieved from the network and contains annotations, so Retrofit (Moshi) understands how to parse this network data (XML, JSON, Binary...) this data into objects.
Below diagram presents application data flow when a user interacts with album list screen
:
This project utilizes multiple mechanics to easily share the same versions of dependencies.
External dependencies (libraries) are defined using versions catalog feature in the settings.gradle file. These dynamic library versions are locked using Gradle locking dependency mechanism - concrete dependency versions are stored in MODULE_NAME/gradle.lockfile
files.
To update lock files run ./gradlew test lint s --write-locks
command and commit updated gradle.lockfile
files torepository.
Each feature module depends on the app
module, so dependencies are shared without need to add them explicitly in each feature module.
Gradle plugins are defined in pluginManagement block (settings.gradle file).
Dynamic versions aren't supported for Gradle plugins, so locking dependency mechanism can't be used (like for app library dependencies), and thus versions of some libraries & plugins have to be hardcoded in the gradle.properties file.
There is no easy way to share id between pluginManagement
block and buildSrc
folder, so plugin ids (also used within build scripts), have to be duplicated in the GradlePluginId file.
Gradle is missing proper built-in mechanism to share dependency versions between app library dependency and Gradle plugin dependency eg. Navigation component library uses Safe Args Gradle plugin with the same version.
To enable sharing all versions that are used for both plugins and libraries are defined in gradle.properties.
Unfortunately this technique cannot be applied to older Gradle plugins (added by classpath
, not by pluginManagement
), so some version in the gradle.properties are still duplicated.
CI is utilizing GitHub Actions. Complete GitHub Actions config is located in the .github/workflows folder.
Series of workflows runs (in parallel) for every opened PR and after merging PR to main
branch:
./gradlew lintDebug
- runs Android lint./gradlew detekt
- runs detekt./gradlew ktlintCheck
- runs ktlint./gradlew testDebugUnitTest
- run unit tests./gradlew connectedCheck
- run UI tests./gradlew :app:bundleDebug
- create app bundleThe update-dependencies task run periodically and creates a pull requestcontaining dependencyupdates(updated gradle .lockfile files used by Gradle’s dependency locking).
Read related articles to have a better understanding of underlying design decisions and various trade-offs.
The interface of the app utilizes some of the modern material design components, however, is deliberately kept simple tofocus on application architecture.
Checklist of all upcoming enhancements.
There are a few ways to open this project.
Android Studio
-> File
-> New
-> From Version control
-> Git
https://github.com/igorwojda/android-showcase.git
into URL field an press Clone
buttongit clone https://github.com/igorwojda/android-showcase.git
command to clone projectAndroid Studio
and select File | Open...
from the menu. Select cloned directory and press Open
buttonThis is project is a sample, to inspire you and should handle most of the common cases, but please take a look atadditional resources.
Other high-quality projects will help you to find solutions that work for your project:
Jetpack
sample covering alllibrariescommon state
approach together witch very gooddocumentationktlint
import-ordering
rule conflicts with IDE default formatting rule, so it have to be disabled. This is partially fixed in AS 4.2 (see Issue 527 and Issue KT-10974)Want to contribute? Check our Contributing docs.
MIT License
Copyright (c) 2019 Igor Wojda
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Flowing animations and are distributed under Creative Commons License 2.0
:
背景 我正在尝试在当前活动之上放置一层,以解释当前屏幕上正在发生的事情,类似于contact + app上发生的事情。 我知道有一些解决方案(例如showCase库和superToolTips库),并且我也知道可以通过将其添加到活动窗口中来创建视图并将其设置在顶部,但是我需要将整个视图 对话框顶部。 问题 无论我尝试什么,每种解决方案都无法按我需要的方式工作。 简而言之,我需要的是: 全屏对话框。
JNI绑定 Android上的Java资源 WebView代码组织
Native.js for Android封装一条通过JS语法直接调用Native Java接口通道,通过plus.android可调用几乎所有的系统API。 方法: currentWebview: 获取当前Webview窗口对象的native层实例对象 newObject: 创建实例对象 getAttribute: 获取对象(类对象/实例对象)的属性值 setAttribute: 设置对象(类对
Android++ 是一个免费的 Visual Studio 扩展,用于支持在 Visual Studio 上开发和调试原生的 Android 应用,主要基于 NDK 的 C/C++ 应用。同时包括可订制的发布、资源管理以及集成了 Java 源码编译。
Android(安卓)是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国谷歌公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由谷歌收购注资。2007年11月,谷歌与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后谷歌以Apache许可证的授
Android(安卓)是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国谷歌公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由谷歌收购注资。2007年11月,谷歌与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后谷歌以Apache许可证的授
简介 该库提供J2SE的Swing、AWT等类的安卓实现,引用该库便能在Android上运行J2SE应用程序。 该库实现大多数必需功能,但不是全部的J2SE。 成功示例HomeCenter服务器,该服务器基于J2SE,同时完全运行于Android之上。 使用指引 该库依赖于开源工程HomeCenter。 它不含Activity,需另建Android工程,并引用本库。 Activity和res需作为