RxJava architecture library for Android

Related tags

App reark
Overview

Reference Architecture for Android using RxJava

Build Status

This is an ambitious reference project of what can be done with RxJava to create an app based on streams of data.

High-level architecture

In the repository you will find several library-like solutions that might or might not be reusable. Parts of the architecture are, however, already used in real applications, and as the parts mature they are likely to be extracted into separate libraries. If you follow the general guidelines illustrated here, your application should be in a position to have portions of it easily replaced as more or less official solutions emerge. The architecture is designed to support large-scale applications with remote processes, such as those used in widgets.

The project uses the open GitHub repositories API. You might hit a rate limit if you use the API extensively.

Including in an Android Project

allprojects {
    repositories {
      ...
      maven { url 'https://jitpack.io' }
    }
}

dependencies {
    implementation 'com.github.reark:reark:0.3'
}

Alternatively, you may consider including Reark as a Git submodule.

Application Structure

To use the app start writing a search in the text box of at least 3 characters. This will trigger a network request and the five first results of which will be shown as a list. The input also throttled in a way that makes it trigger when the user stops typing. This is a very good basic example of Rx streams.

.filter((string) -> string.length() > 2) .debounce(500, TimeUnit.MILLISECONDS)

The Basic Data Flow

As seen above, the user input is turned into a series of strings in the View layer, which the View Model then processes.

When we are ready to execute the actual search, an Intent is broadcast and the NetworkService handles it, starting a network request in its own remote process. Once the network request is finished, NetworkService inserts the fetched data into the ContentProviders of GitHubRepositorySearchStore and GitHubRepositoryStore. All of the interested processes are notified through the onChange mechanism of ContentProviders.

The search store contains only the ids of the results of the query, while the actual repository POJOs are written in the repository store. A repository POJO can thus be contained in multiple searches, which can often be the case if the first match stays the same, for instance.

This structure nevertheless enables us to keep the data consistent across the application—even if the same data object is updated from multiple APIs.

Tests

You can run the test(s) on command line in the project folder:

./gradlew test

Currently the tests are not extensive, but we are working on it. The View Models in particular will be fully unit tested.

View Models

This architecture makes use of View Models, which are not commonly used in Android applications.

View Model (VM) encapsulates the state of a view and the logic related to it into a separate class. This class is independent of the UI platform and can be used as the “engine” for several different fragments. The view is a dumb rendering component that displays the state as constructed in the VM.

Possible values that the view model can expose:

  • Background color
  • Page title text resource id
  • Selected tab index
  • Animation state of a sliding menu

The same VM can be used with entirely different views—for instance one on tablet and another one on mobile. The VM is also relatively easy to unit test and immune to changes in the platform.

An Android Approach to View Models

For our purposes let us treat fragments as the entry point to the view layer. The view layer is an abstraction and is not to be confused with the Android View class, the descendants of which will still be used for displaying the data once the Fragment has received the latest values from the VM. The point is our fragments will not contain any program logic.

The goal is to take all data retrieval and data processing away from the fragments and encapsulate it into a nice and clean View Model. This VM can then be unit tested and re-used where ever applicable.

In this example project we use RxJava to establish the necessary observable/observer patterns to connect the view model to the view, but an alternative solution could be used as well.

The View Model Life Cycle

View Model has two important responsibilities:

  • Retrieve data from a data source
  • Expose the data as simple values to the view layer (fragments)

The suggested VM life cycle is much like that of fragment, and not by coincidence.

View Model life cycle

The data layer offers a permanent subscription to a data source, and it pushes a new item whenever new data arrives. This connection should be established in onViewCreated and released in onViewDestroyed. More details of a good data layer architecture will follow in another article.

It is a good idea to separate the data subscription and the data retrieval. You first passively subscribe to a data stream, and then you use another method to start the retrieval of new items for that particular stream (most commonly via http). This way, if desired, you are able to refresh the data multiple times without needing to break the subscription—or in some cases not to refresh it at all but use the latest cached value.

Exposing View Model Properties

Strictly speaking there are two kinds of properties

  • Actual properties, such as boolean flags
  • Events

In RxJava the actual properties would typically be stored as BehaviorSubjects and exposed as Observables. This way whoever subscribes to the property will immediately receive the latest value and update the UI, even if the value had been updated while paused.

In the second case, where the property is used for events, you might want to opt for a simple PublishSubject that has no “memory”. When onResume is called the UI would not receive any events that occurred while paused.

Threading and Testing View Models

While it is possible to use TestSchedulers, I believe it is best to keep the VMs simple and not to include threading in them. If there is a risk some of the inputs to the VM come from different threads you can use thread-safe types or declare fields as volatile.

The safest way to ensure the main thread is to use .observeOn(AndroidSchedulers.mainThread()) when the fragment subscribes to the VM properties. This makes the subscription to always trigger asynchronously, but usually it is more of a benefit than a disadvantage.

Data Layer

The application specific Data Layer is responsible for fetching and storing data. A fetch operation updates the centrally stored values in ContentProviders and everyone who is interested will get the update.

For a stream of data we use the DataStreamNotification, which differs slightly from the typical RxJava Notification.

  • The observable never completes: there could always be new data
  • Errors in fetching the data do not break the subscription: once the problem is resolved the data starts to flow again
  • FetchStart event is sent when a network operation is start and it can be used to display loading state in the UI

Currently only the GitHubRepositorySearch supports DataStreamNotifications, though it is not difficult to add to any data type.

Consuming Data in the View

When data comes in we render it in the view, simple as that. It does not actually matter what triggered the update of the data—it could be a background sync, user pressing a button or a change in another part of the UI. The data can arrive at any given time and any number of times.

Store

Store is a container that allows subscribing to data of a particular type and identification. Whenever data requested becomes available, a new immutable value is pushed as onNext to all subscribers. The Store does not concern itself with where the data comes from.

Conceptually the concrete backing of a Store can be a static in-memory hash, plain SQLite or a ContentProvider. In case the same data is to be used from multiple processes there needs to be a mechanism that detects changes in the persisted data and propagates them to all subscribers. Android ContentProviders, of course, do this automatically, making them the perfect candidates.

In this example project the Stores are backed by SQLite ContentProviders, which serializes the values as JSON strings with an id column for queries. This could be optimized by adding columns for all fields of the POJO, but on the other hand it adds boilerplate code that has to be maintained. In case of performance problems the implementation can be changed without affecting the rest of the application.

For caching purposes it is usually good to include timestamps into the structure of the Store. Determining when to update the data is not conceptually not a concern of the Store, though, it simply holds the data. Cache-expiry of the data is still on the list of things-to-do in this reference project.

Fetcher

The responsibilities of the Fetcher are:

  • Send and process the network requests
  • Populate Stores with the received values
  • Update the NetworkRequestState of fetch operations
  • Filter duplicate ongoing requests

The Fetchers run exclusively in the NetworkService, making them hidden from the rest of the application. NetworkService has its own ServiceDataLayer that allows it to trigger fetch operations.

License

The MIT License

Copyright (c) 2013-2017 reark project contributors

https://github.com/reark/reark/graphs/contributors

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 CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Comments
  • Data notifications should not start with stale status

    Data notifications should not start with stale status

    This should fix issue where new requests will start with old request status if the same resource was requester earlier. Idea behind the change is to register all requests with an id, and filter the data stream notifications so that only matching ids are let through.

    Additionally the request status store should be cleared at startup to avoid issues caused by duplicate ids across application restarts, but that is pending the delete PR #155.

    pending review 
    opened by apoi 11
  • Alternative for ContentProvider

    Alternative for ContentProvider

    You prepared a great example project. But I have one issue. Can we use alternative ways to observe data changes instead of ContentProvider. For example SQLBrite, or simple SQL query?

    opened by rafaelekol 11
  • Feature request/Implementation guideline to fetch a list from SingleItemContentProviderStore

    Feature request/Implementation guideline to fetch a list from SingleItemContentProviderStore

    I am trying to use this pattern for a sample app. I need to implement an API which returns a list of objects.Where I am stuck is how to leverage the SingleItemContentProviderStore in that case.Because this works based on an id identifier.Any ideas?

    EDIT : I can use getStream() , however this too would return individual item observables,however this still wont work well with a ListView,RecyclerView

    opened by notsatyarth 9
  • Fix javadoc generation errors by including dependencies

    Fix javadoc generation errors by including dependencies

    First build after cleaning currently results in 100 errors, which all come from javadoc generation. Javadoc is apparently a required for Maven artifacts. This change fixes most errors by adding missing dependencies to javadoc classpath.

    opened by apoi 7
  • add method getStream for StoreGetInterface

    add method getStream for StoreGetInterface

    If you created method "login", use "final Observable networkRequestStatusObservable = networkRequestStatusStore .getOnceAndStream(GitHubRepositorySearchFetcher.getUniqueId(searchString).hashCode()) .filter(NetworkRequestStatus::isSome);"

    Method "getOnceAndStream" return 200 with latest request. It is wrong. I think that " networkRequestStatusStore.getOnceAndStream" must replace for "networkRequestStatusStore.getStream"

    opened by ulx 5
  • Implement named fetchers

    Implement named fetchers

    By default the used fetcher is chosen by content URI. We may however want to choose a specific fetcher in case of multiple fetchers implementing the same content URI. This patch adds fetcher identifiers that can be used to request a specific fetcher.

    opened by apoi 5
  • Add a method for subscribing only when not subscribed yet

    Add a method for subscribing only when not subscribed yet

    By default ViewModel's subscribe() unsubscribes any existing subscription before re-subscribing. Add a method for skipping this in case we're happy with the existing subscription.

    opened by apoi 5
  • Replaced Map of Subjects with a single subject

    Replaced Map of Subjects with a single subject

    Map of subjects had an issue with a race condition. Additionally already created subjects were never cleaned. Replacing them with a single subject ensures that when a subscriber finishes the subscrition is being disposed. Keeping all the logic handling in a single monad makes sure that all race conditions are handeled properly.

    enhancement 
    opened by jaggernod 4
  • Handling Activity switch

    Handling Activity switch

    Can you please explain how you would handle switsches between Activities (or Fragments) in this approach? Which component would be responsible to start a new Activity after a Buttonclick in the view? Thanks & greetings

    opened by misrakli 4
  • Default groupOperations() from ContentProviderStoreCoreBase doesn't buffer last emitted item

    Default groupOperations() from ContentProviderStoreCoreBase doesn't buffer last emitted item

    Default groupOperations() from io.reark.reark.data.stores.cores.ContentProviderStoreCoreBase doesn't buffer last emitted item if groupMaxSize == total number of items emitted by source observable so far stackoverflow

    By the way are these changes reasonable to avoid possible multi-threading problems?

    @NonNull
    private final Subject<CoreValue<U>, CoreValue<U>> operationSubject =
            PublishSubject.<CoreValue<U>>create().toSerialized();
    
    private AtomicInteger nextOperationIndex = new AtomicInteger(0);
    
    completionNotifiers.put(index, PublishSubject.<Boolean>create().toSerialized());
    
    
    opened by bifri 2
  • Add store/core methods for emitting all values

    Add store/core methods for emitting all values

    I didn't implement getOnceAndStream() without id that would emit all items initially and continued with all future items since I'm not sure if such a method makes sense. It wouldn't be able to emit list of items the same way as getOnce() does (as the future items will arrive one-by-one), and emitting all initial items individually would make it impossible to differentiate between the initial emit and future emits.

    pending review 
    opened by apoi 2
  • ContentProviderStoreCoreBase.applyOperations() flatMap issue

    ContentProviderStoreCoreBase.applyOperations() flatMap issue

    https://github.com/reark/reark/blob/71d6319a3bb3fba680d4818ca5e43f32a195fbfe/reark/src/main/java/io/reark/reark/data/stores/cores/ContentProviderStoreCoreBase.java#L141

        private Observable<CoreOperationResult> applyOperations(@NonNull final List<CoreOperation> operations) {
            return Observable.fromCallable(() -> {
                        ArrayList<ContentProviderOperation> contentOperations = contentOperations(operations);
                        return contentResolver.applyBatch(getAuthority(), contentOperations);
                    })
                    .doOnNext(result -> Log.v(TAG, String.format("Applied %s operations", result.length)))
                    .flatMap(Observable::fromArray)
                    .zipWith(operations, CoreOperationResult::new);
        }
    

    As I understand this code, it tries to zip the input (operations) with the results of applyBatch(): then flatMap() must not be used, because it does not guarantee the order, right?

    opened by tmtron 0
  • Add possibility to use hashed URI in the network request store

    Add possibility to use hashed URI in the network request store

    For instance, for security reasons we might not want to store the full URI in the network request store. Suggested change is to provide a possibility to use a hash function for all URIs stored in the store. This would also make it possible to change the hash based on possible POST parameters.

    enhancement 
    opened by tehmou 0
Releases(0.3)
Owner
Reark
Organisation focused on FRP software systems
Reark
Movie discovery app showcasing MVP, RxJava, Dagger 2 and Clean Architecture

MovieGuide ?? Refactoring in progress ??‍♀️ ⛏ ?? ??️ ?? ?? ?? Comments and new issues are welcome. ?? Currently not accepting external PRs that touch

Arun Sasidharan 2.6k Dec 25, 2022
Learning RxJava for Android by example

Learning RxJava for Android by example This is a repository with real-world useful examples of using RxJava with Android. It usually will be in a cons

Kaushik Gopal 7.6k Jan 5, 2023
Android Templates: RxJava

Android Templates: RxJava Our optimized Android templates used in our android projects Setup Clone the project Checkout our main development branch ko

Danh Dev 0 Dec 29, 2021
Instagram clone App in android using Kotlin, LiveData, MVVM, Dagger, RxJava and Retrofit.

Instagram-Project-in-android-with-MVVM-architecture Project from MindOrks Professional Bootcamp with self practice work and added some additional feat

Abhishek Solanki 3 Feb 28, 2022
Weather Forecast App Used MVVM, Retrofit and RxJava

Weather_Forecast_App Used MVVM, Retrofit and RxJava Screen

Rumeysa Özer 0 Dec 31, 2021
:movie_camera: Movie discovery app showcasing Android best practices with Google's recommended architecture: MVVM + Repository + Offline support + Android Architecture Components + Paging library & Retrofit2.

Popular Movies Stage 1 + Stage 2 Discover the most popular and top rated movies playing. Movies data fetched using themoviedb.org API. ✨ Screenshots M

Yassin AJDI 189 Nov 26, 2022
🛒A Minimal Expense E-Commerce App built to demonstrate the use of modern android architecture components [Navigation, Room, MotionLayout, etc..] with MVVM Architecture. ✔

E-Store A Simple E-Commerce App ?? built to demonstrate the use of modern android architecture component with MVVM Architecture ?? . Made with love ❤️

Ameen Essa 14 Nov 3, 2022
A Simple Expense Tracker App 📱 built to demonstrate the use of modern android architecture component with MVVM Architecture

Expenso ?? A Simple Expense Tracker App ?? built to demonstrate the use of modern android architecture component with MVVM Architecture ?? . Made with

Michel Horacio 1 Dec 28, 2022
Weather application example with Android Architecture components and Clean Architecture

Weather application example with Android Architecture components and Clean Architecture Weather app that shows how to architect an android app in a cl

null 2 Dec 3, 2021
NewsSpac-MVVM-CleanArch-TDD - The App uses MVVM architecture together with Clean architecture

Aplicativo NewsSpace Arquitetura O App utiliza a arquitetura MVVM em conjunto co

null 1 Feb 11, 2022
ArchGuard is a architecture governance tool which can analysis architecture in container, component, code level, create architecure fitness functions, and anaysis system dependencies..

ArchGuard backend ArchGuard is a architecture governance tool which can analysis architecture in container, component, code level, database, create ar

ArchGuard 446 Dec 20, 2022
To Do List App is built in Kotlin using Material 3, Data Binding, Navigation Component Graphs, Room persistence library, Kotlin coroutines, LiveData, Dagger Hilt, and Notifications following MVVM Architecture.

ToDoListApp ToDoList App demonstrates modern Android development with Hilt, Coroutines, LiveData, Jetpack (Room, ViewModel), and Material 3 Design bas

Naman Garg 10 Jan 8, 2023
Shreyas Patil 2.1k Dec 30, 2022
NewsApp is a an android project based on modern Android application tech-stacks and MVVM architecture.

NewsApp NewsApp is a an android project based on modern Android application tech-stacks and MVVM architecture. This project is for focusing especially

null 1 Jan 14, 2022
A sample app illustrating Android development using Kotlin with MVVM architecture, Android Jetpack, and other commonly used libraries.

Anime Facts A sample app illustrating Android development using Kotlin with MVVM architecture, Android Jetpack, and other commonly used libraries. Ani

Eugene Javiñas 0 Dec 5, 2021
Android app which fetches a sample movies list to display. Built using Kotlin and latest Android tech stack, with an approach to clean architecture.

movies-sample-app This is an Android app which fetches a sample movies list to display. Built using Kotlin and latest Android tech stack, with an appr

Nadeem Ahmed 1 Oct 21, 2021
Android News App built in kotlin with implementation of MVVM architecture, android navigation components and retrofit. Displays news to users allowing them to share and save news.

News-App Android news app built in kotlin that fetches news data from news api with Retrofit and displays news to users. This App follow MVVM architec

Raj Manjrekar 16 Dec 29, 2022
📚 Sample Android Components Architecture on a modular word focused on the scalability, testability and maintainability written in Kotlin, following best practices using Jetpack.

Android Components Architecture in a Modular Word Android Components Architecture in a Modular Word is a sample project that presents modern, 2020 app

Madalin Valceleanu 2.3k Jan 3, 2023