{"id":15407362,"url":"https://github.com/izumin5210/droidux","last_synced_at":"2025-07-21T15:11:26.180Z","repository":{"id":69098612,"uuid":"45388688","full_name":"izumin5210/Droidux","owner":"izumin5210","description":"\"Predictable state container\" implementation, inspired by Redux.","archived":false,"fork":false,"pushed_at":"2018-01-16T12:22:47.000Z","size":478,"stargazers_count":159,"open_issues_count":5,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-27T17:35:42.400Z","etag":null,"topics":["android","redux","rxjava"],"latest_commit_sha":null,"homepage":"https://github.com/izumin5210/Droidux","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/izumin5210.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-11-02T10:48:37.000Z","updated_at":"2025-02-28T06:55:12.000Z","dependencies_parsed_at":"2024-01-07T20:12:21.696Z","dependency_job_id":null,"html_url":"https://github.com/izumin5210/Droidux","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izumin5210%2FDroidux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izumin5210%2FDroidux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izumin5210%2FDroidux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izumin5210%2FDroidux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izumin5210","download_url":"https://codeload.github.com/izumin5210/Droidux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248815534,"owners_count":21165941,"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","redux","rxjava"],"created_at":"2024-10-01T16:28:33.924Z","updated_at":"2025-04-14T03:33:57.392Z","avatar_url":"https://github.com/izumin5210.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Droidux\n[![Build Status](https://travis-ci.org/izumin5210/Droidux.svg)](https://travis-ci.org/izumin5210/Droidux)\n[![Download](https://api.bintray.com/packages/izumin5210/maven/droidux/images/download.svg) ](https://bintray.com/izumin5210/maven/droidux/_latestVersion)\n[![Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/izumin5210/Droidux/blob/master/LICENSE.md)\n[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Droidux-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/2892)\n\nDroidux is \"predictable state container\" implementation, inspired by **[Redux][redux]**.\n\n## Features\nDroidux is influenced by [Three principles][three-principles] of Redux.\n\n\u003e * Single source of truth\n\u003e     - The state of your whole application is stored in an object tree inside a single store.\n\u003e * State is read-only\n\u003e     - The only way to mutate the state is to emit an action, an object describing what happened.\n\u003e * Mutations are written as pure functions\n\u003e     - To specify how the state tree is transformed by actions, you write pure reducers.\n\u003e\n\u003e [Three Principles | Redux][three-principles]\n\nFeatures of Droidux are following:\n\n* All mutations can be observed via Flowable from [RxJava][rxjava]\n* All mutations are automatically notified to views via [Data Binding][databinding]\n\n### Data flow\n![Droidux data flow](droidux.png)\n\nsee also: [Introduction to Redux // Speaker Deck](https://speakerdeck.com/axross/introduction-to-redux) (in Japanese)\n\n## Installation\nDroidux depends on [RxJava][rxjava] and [Data Binding][databinding].\nAdd to your project build.gradle file:\n\n```groovy\n\napply plugin: 'com.android.application'\n\ndependencies {\n  compile 'info.izumin.android:droidux:0.6.0'\n  compile 'io.reactivex.rxjava2:rxjava:2.1.8'\n  compile 'io.reactivex.rxjava2:rxandroid:2.0.1'\n  annotationProcessor 'info.izumin.android:droidux-processor:0.6.0'\n}\n```\n\nAnd also you need to setup [Data Binding][databinding].\n\nWhen you use `AsyncAction`, you need to add [droidux-thunk](https://github.com/izumin5210/Droidux/tree/master/middlewares/droidux-thunk).\n\n```groovy\ncompile 'info.izumin.android:droidux-thunk:0.3.0'\n```\n\n## Usage\n### Quick example\n\n```java\n/**\n * This is a state class.\n * It can be as simple as possible implementation, like POJO, or immutable object. \n */\npublic class Counter {\n    private final int count;\n\n    public Counter(int count) {\n        this.count = count;\n    }\n\n    public int getCount() {\n        return count;\n    }\n}\n\n/**\n * This is a reducer class.\n * It should be applied @Reducer annotation is given a state class as an argument.\n * It describe whether the reducer should handle which actions.\n */\n@Reducer(Counter.class)\npublic class CounterReducer {\n\n    /**\n     * This is a method to handle actions.\n     * It should be applied @Dispatchable annotation is given an action class as an parameter.\n     * It describe how to transform the state into the next state when dispatched actions.\n     * It should return the next state instance, and it is preferred instantiate the new state.\n     *\n     * This example handle IncrementCountAction,\n     + and it returns new counter instance that state is incremented.\n     */\n    @Dispatchable(IncrementCountAction.class)\n    public Counter increment(Counter state) {\n        return new Counter(state.getCount() + 1);\n    }\n\n    @Dispatchable(DecrementCountAction.class)\n    public Counter decrement(Counter state) {\n        return new Counter(state.getCount() - 1);\n    }\n\n    @Dispatchable(ClearCountAction.class)\n    public Counter clear() {\n        return new Counter(0);\n    }\n}\n\n\n/**\n * This is a store interface.\n * It should be applied @Store annotation and passing reducer classes as parameters.\n * Droidux generates an implementation of getter method, observe method and dispatch method from user-defined interface.\n */\n@Store(CounterReducer.class)\npublic interface CounterStore extends BaseStore {\n    Counter getCounter();\n    Flowable\u003cCounter\u003e observeCounter();\n}\n\n/**\n * They are action classes. They should extend Action class.\n */\npublic class IncrementCountAction implements Action {}\npublic class DecrementCountAction implements Action {}\npublic class ClearCountAction implements Action {}\n\n\n// Instantiate a Droidux store holding the state of your app.\n// Its class is generated automatically from Reducer class.\n// \n// The instantiating should use Builder class,\n// and it should register a reducer instance and an initial state.\n// \n// Its APIs in this example are following:\n// - Flowable\u003cAction\u003e dispatch(Action action)\n// - Flowable\u003cCounter\u003e observeCounter()\n// - Counter getCounter()\nCounterStore store = DroiduxCounterStore.builder()\n        .setReducer(new CounterReducer(), new Counter(0))\n        .build();                                       // Counter: 0\n\n// You can observe to the updates using RxJava interface. \nstore.observe((counter) -\u003e Log.d(TAG, counter.toString()));\n\n// The only way to mutate the internal state is to dispatch an action.\nstore.dispatch(new IncrementCountAction()).subscribe(); // Counter: 1\nstore.dispatch(new IncrementCountAction()).subscribe(); // Counter: 2\nstore.dispatch(new IncrementCountAction()).subscribe(); // Counter: 3\n\nstore.dispatch(new DecrementCountAction()).subscribe(); // Counter: 2\n\nstore.dispatch(new ClearCountAction()).subscribe();     // Counter: 0\n```\n\n### Data Binding\n\n```java\n// If you use databinding, yor store interface must extend `android.databinding.Observable`.\n@Store(CounterReducer.class)\npublic interface CounterStore extends BaseStore, android.databinding.Observable {\n    // You should annotate the getter method with @Bindable\n    @Bindable Counter getCounter();\n}\n\nCounterStore store = DroiduxCounterStore.builder()\n        // Pass the field id generated by DataBinding annotation processor.\n        .setReducer(new CounterReducer(), new Counter(0), BR.counter)\n        .build();\n```\n\nLayout file is following:\n\n```xml\n\u003clayout\u003e\n    \u003cdata\u003e\n        \u003cvariable android:name=\"store\" android:type=\"CounterStore\" /\u003e\n    \u003c/data\u003e\n    \u003cRelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" \u003e\n        \n        \u003cTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@{store.counter}\" /\u003e\n        \n    \u003c/RelativeLayout\u003e\n\u003c/layout\u003e\n```\n\n\n### Combined store\n\n```java\n@Store({CounterReducer.class, TodoListReducer.class})\nclass RootStore extends BaseStore {\n    Counter getCounter();\n    Flowable\u003cCounter\u003e observeCounter();\n    TodoList getTodoList();\n    Flowable\u003cTodoList\u003e observeTodoList();\n}\n\n\nRootStore store = DroiduxRootStore.builder()\n        .setReducer(new CounterReducer(), new Counter(0))\n        .setReducer(new TodoListReducer(), new TodoList())\n        .addMiddleware(new Logger())\n        .build();\n\nstore.dispatch(new IncrementCountAction()).subscribe();     // Counter: 1, Todo: 0\nstore.dispatch(new AddTodoAction(\"new task\")).subscribe();  // Counter: 1, Todo: 1\n```\n\n### Middleware\n\n```java\nclass Logger extends Middleware\u003cCounterStore\u003e {\n    @Override\n    public Flowable\u003cAction\u003e beforeDispatch(Action action) {\n        Log.d(\"[prev counter]\", String.valueOf(getStore().count()));\n        Log.d(\"[action]\", action.getClass().getSimpleName());\n        return Flowable.just(action);\n    }\n\n    @Override\n    public Flowable\u003cAction\u003e afterDispatch(Action action) {\n        Log.d(\"[next counter]\", String.valueOf(getStore().count()));\n        return Flowable.just(action);\n    }\n}\n\n// Instantiate store class \nCounterStore store = DroiduxCounterStore.builder()\n        .setReducer(new CounterReducer(), new Counter(0))\n        .addMiddleware(new Logger())        // apply logger middleware\n        .build();                           // Counter: 0\n\nstore.dispatch(new IncrementCountAction()).subscribe();\n// logcat:\n// [prev counter]: 0\n// [action]: IncrementCountAction\n// [next counter]: 1\n\nstore.dispatch(new IncrementCountAction()).subscribe();\n// logcat:\n// [prev counter]: 1\n// [action]: IncrementCountAction\n// [next counter]: 2\n\nstore.dispatch(new ClearCountAction()).subscribe();\n// logcat:\n// [prev counter]: 2\n// [action]: ClearCountAction\n// [next counter]: 0\n```\n\n### Undo / Redo\n\n```java\nclass TodoList extends ArrayList\u003cTodoList.Todo\u003e implements UndoableState\u003cTodoList\u003e {\n    @Override\n    public TodoList clone() {\n        // ...\n    }\n\n    public static Todo {\n        // ...\n    }\n}\n\n@Undoable\n@Reducer(TodoList.class)\nclass TodoListReducer {\n    @Dispatchable(AddTodoAction.class)\n    public TodoList add(TodoList state, AddTodoAction action) {\n        // ...\n    }\n\n    @Dispatchable(CompleteTodoAction.class)\n    public TodoList complete(TodoList state, CompleteTodoAction action) {\n        // ...\n    }\n}\n\n@Store(TodoListReducer.class)\npublic interface TodoListStore {\n    TodoList todoList();\n    Flowable\u003cTodoList\u003e observeTodoList();\n}\n\nclass AddTodoAction implements Action {\n    // ...\n}\n\nclass CompleteTodoAction implements Action {\n    // ...\n}\n\n\nTodoListStore store = DroiduxTodoListStore.builder()\n        .setReducer(new TodoListReducer(), new TodoList())\n        .build();\n\nstore.dispatch(new AddTodoAction(\"item 1\")).subscribe();        // [\"item 1\"]\nstore.dispatch(new AddTodoAction(\"item 2\")).subscribe();        // [\"item 1\", \"item 2\"]\nstore.dispatch(new AddTodoAction(\"item 3\")).subscribe();        // [\"item 1\", \"item 2\", \"item 3\"]\nstore.dispatch(new CompleteTodoAction(\"item 2\")).subscribe();   // [\"item 1\", \"item 3\"]\nstore.dispatch(new AddTodoAction(\"item 4\")).subscribe();        // [\"item 1\", \"item 3\", \"item 4\"]\n\nstore.dispatch(new UndoAction(TodoList.class)).subscribe();\n// =\u003e [\"item 1\", \"item 3\"]\n\nstore.dispatch(new UndoAction(TodoList.class)).subscribe();\n// =\u003e [\"item 1\", \"item 2\", \"item 3\"]\n\nstore.dispatch(new RedoAction(TodoList.class)).subscribe();\n// =\u003e [\"item 1\", \"item 3\"]\n```\n\n### Async action\n\nUse [droidux-thunk](https://github.com/izumin5210/Droidux/tree/master/middlewares/droidux-thunk).\n\n```java\nclass FetchTodoListAction implements AsyncAction {\n    private final TodoListApi client;\n\n    public FetchTodoListAction(TodoListApi client) {\n        this.client = client;\n    }\n\n    public Flowable\u003cReceiveTodoListAction\u003e call(Dispatcher dispatcher) {\n        return dispatcher.dispatch(new DoingFetchAction())\n                .flatMap(_action -\u003e client.fetch())\n                .map(todoList -\u003e {\n                    this.todoList = todoList;\n                    return new ReceiveTodoListAction(todoList);\n                });\n    }\n}\n\nclass ReceiveTodoListAction implements Action {\n    private final TodoList todoList;\n\n    public ReceiveTodoListAction(TodoList todoList) {\n        this.todoList = todoList;\n    }\n\n    public TodoList getTodoList() {\n        return todoList;\n    }\n}\n\n\nTodoListStore store = DroiduxTodoListStore.builder()\n        .setReducer(new TodoListReducer(), new TodoList())\n        .addMiddleware(new ThunkMiddleware())\n        .build();\n\n\nstore.dispatch(new FetchTodoListAction(client)).subscribe();\n```\n\n\n## Examples\n\n* [Counter](https://github.com/izumin5210/Droidux/tree/master/examples/counter)\n* [TodoMVC](https://github.com/izumin5210/Droidux/tree/master/examples/todomvc)\n* [Todos with Undo](https://github.com/izumin5210/Droidux/tree/master/examples/todos-with-undo)\n* [Todos with Dagger 2](https://github.com/izumin5210/Droidux/tree/master/examples/todos-with-dagger)\n\n\n## License\n\n```\nCopyright 2015 izumin5210\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[redux]: https://github.com/reactjs/redux\n[rxjava]: https://github.com/ReactiveX/RxJava\n[three-principles]: http://redux.js.org/docs/introduction/ThreePrinciples.html\n[databinding]: http://developer.android.com/tools/data-binding/guide.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizumin5210%2Fdroidux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizumin5210%2Fdroidux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizumin5210%2Fdroidux/lists"}