Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fluxxan is an Android implementation of the Flux Architecture that combines concepts from both Fluxxor and Redux.

License

NotificationsYou must be signed in to change notification settings

thedumbtechguy/Fluxxan

Repository files navigation

Fluxxan is an Android implementation of the Flux Architecture that combines concepts from both Fluxxor and Redux with minimal dependencies and support for Android SDK 8.

Originally DroidFlux, it started as a direct Android port of the popularFluxxor library seeking to implement theFlux Architecture as popularised by Facebook for Android.The library has evolved into a hybrid of the original Flux andRedux, borrowing some of the great ideas from Redux while trying to be as close to the original dictates of Flux as much as possible.I ended up with something that looks like Flux, but works a lot like Redux.

Android ArsenalDownload

Users

Apps using Fluxxan in production

Umaplay

Current Version: 1.0.0

Fluxxan followsSemantic Versioning.

Fluxxan4j

If you are looking for a pure java implementation, you can check outFluxxan4j which strips out the android dependencies and supports Java 6.

Installation

Gradle

Fluxxan is available on jcenter.

compile'com.umaplay.oss:fluxxan:1.0.0'

Manual Installation

Download theaar artifact from theartifacts directoryand copy it into the libs directory of your app module.

Specifylibs as a repository in your root gradle file.

    allprojects {        repositories {...            flatDir { dirs'libs' }        }    }

Specify Fluxxan as dependency in your app's gradle file.

    dependencies {        compile fileTree(dir:'libs',include: ['*.jar'])        compile(name:'fluxxan-1.0.0',ext:'aar')...    }

Introduction

I won't attempt to teach you the concepts of Flux. There are enough articles on the internet for that. Facebook has a greatintroduction to flux to get you started.I will instead focus on showing you how to achieve Flux using Fluxxan. In this introduction tutorial, I will walk you through building the sample Todo app included in the source.

Fluxxan is composed of the

  1. State
  2. Dispatcher
  3. Actions
  4. Action Creators
  5. Reducers (called stores in traditional Flux)
  6. StateListeners
  7. Middlewares

How it works

You hold your application state in a singleState object tree. In order to update theState, you tell anActionCreator, which creates and dispatches anAction object describing what happened.TheDispatcher calls all registered middlewares in the same registered order to intercept the actions been dispatched and perform async operations. Following theDispatcher calls all registeredReducers with theState andAction and eachReducer specifies how theAction transforms the state tree.StateListeners are passed the returnedState and update the UI accordingly.

State

TheState is the Single Source of Truth of your application. It a single object tree containing the entire app state.

Unlike Redux, Fluxxan does not force you to use an immutable state even though the default implementation assumes you do.It is greatly encouraged you do as it will both improve your code, debugging and increase the overall performance of your application. There is a reason most of the new Flux implementations are going immutable.If you choose not to go Immutable, you will need to override couple of methods to help you short circuit the dispatch process.

I've foundImmutables to be a really great way to achieve Immutability. It's quick to setup and understand.

It’s a good idea to think of its shape before writing any code.For our Todo app will hold a list of Todo items and a Filter to define which Todos to show.

@Value.ImmutablepublicabstractclassAppState {@Value.ParameterpublicabstractMap<String,Todo>getTodos();@Value.DefaultpublicFiltergetFilter() {returnFilter.ALL;        }publicenumFilter {ALL,OPEN,CLOSED        }    }

We also define ourTodo object as an Immutable.

@Value.ImmutablepublicabstractclassTodo {@ValuepublicabstractStringgetUid();@ValuepublicabstractStringgetTitle();@ValuepublicabstractStatusgetStatus();publicenumStatus {OPEN,CLOSED        }    }

When we build our project,Immutables will generate concrete immutable versions of ourAppState andTodo models prefixed with "Immutable" to giveImmutableAppState andImmutableTodo.

Actions

Actions are objects that define aType and a dataPayload.

The actionType is a unique string to identify the given action and thePayload is any object that you wish to pass to theReducer.

Actions are created byActionCreators and passed to theDispatcher which in turn passes it to each reducer.

Action Creators

As the name implies,Action Creators are methods that create and dispatchActions.

Fluxxan does not dictate how you create yourAction Creators. You have full freedom in this regard. They can be static methods or instance methods.

We have some guidelines which you can follow.

  1. Create a class to group related action creators. e.g. in our app we will create aTodoActionCreator class.
  2. Use instances of each creator rather than static methods.
  3. Each creator should have a nestedCreator class that handles the actual creation of theActions using static methods. This gives ourAction Creators two roles and allows to dispatch multiple actions or async tasks while still ensuring we can test ourActions easily.
  4. Nested creator classes should be composed of pure static methods. They should return the sameAction each time when given the same arguments. They should cause no side effects.
  5. Don't limit your creators to only dispatchActions. They are an opportunity to centralize all actions that are taken from the ui. e.g. In a music player app, you can have aPlayerActionCreator class that has aplay() method that tells the media player to start playing and does not dispatch an action. Technically, this is not anAction Creator but it's nice that we can have all interactions with the player in one single place.

Let's see what we have in ourTodo app (simplified for brevity).

publicclassTodoActionCreatorextendsBaseActionCreator {publicstaticfinalStringADD_TODO ="ADD_TODO";publicvoidaddTodo(Stringtodo) {//get and dispatch the action from our creatordispatch(Creator.addTodo(todo));        }publicstaticclassCreator {publicstaticAction<String>addTodo(Stringtodo) {returnnewAction<>(ADD_TODO,todo);            }        }    }

We extendBaseActionCreator which gives usdispatch(Action). Remember to set the dispatcher on your Action Creator after instantiation usingsetDispatcher(Dispatcher) orFluxxan.inject(ActionCreator)

As we can see, when we calladdTodo, our creator gets the relevant action from the nested creator and dispatches it. We can do neat things inaddTodo if we wanted like post to a web service.

In pseudo code it would look something like this:

publicvoidaddTodo(Stringtodo) {//save on the serverpostToWebservice(todo).onStarted(c =>dispatch(Creator.addTodoStarted(todo))).onSuccess(c =>dispatch(Creator.addTodoSuccess(todo))).onFailure(c =>dispatch(Creator.addTodoFailed(todo)));    }

Since we are using a dedicated Creator, this allows us to test the actions without having to mock the dispatcher or the web service. We can simply do anywhere in our code:

dispatch(TodoActionCreator.Creator.addTodoStarted(todo));dispatch(TodoActionCreator.Creator.addTodoSuccess(todo));dispatch(TodoActionCreator.Creator.addTodoFailed(todo));

###ReducersReducers describe how ourState changes in response to anAction. Like ActionCreator Creators, Reducers need to be pure. That means, no side effects, no calling of an API etc. They should rely solely on the Action to transform the state.Given the same arguments, Reducers should return the same result each time.

To register aReducer, you need to callDispatcher.registerReducer(Reducer) andDispatcher.unregisterReducer(Reducer) if you wish to remove it.

Reducers implement theReducer interface.We provide two abstract implementations:BaseReducer andBaseAnnotatedReducer both coupled to the defaultDispatcher implementation.

BaseReducer requires you to implementreduce(State, Action) in which you can check if you want to handle that actionType.

@OverridepublicDispatchResult<State>reduce(Statestate,Actionaction)throwsException {if(action.Type.equals(TodoActionCreator.ADD_TODO)) {//do your thing here//return the new state and indicate that we handled this actionreturnnewDispatchResult<>(newState,true);           }returnnewDispatchResult<>(state,false);       }

BaseAnnotatedReducer uses reflection to determine handlers for each action and calls them for you. This keeps your code cleaner and more concise.You annotate the method with@BindAction(String) and ensure the method has the signatureState methodName(State state, PayloadType payload).

This is what our reducer looks like.

publicclassTodoReducerextendsBaseAnnotatedReducer<AppState> {@BindAction(TodoActionCreator.ADD_TODO)publicAppStateaddTodo(AppStatestate,Stringtodo) {TodoiTodo =ImmutableTodo.builder()                    .uid(UUID.randomUUID().toString())                    .title(todo)                    .status(Todo.Status.OPEN)                    .build();returnImmutableAppState.builder()                    .from(state)                    .putTodos(iTodo.getUid(),iTodo)                    .build();        }    }

You can callDispathcer.waitFor or the convenience method provided byBaseReducer and by extensionBaseAnnotatedReducer.
This allows the reducer to ensure that other reducers run before it.

StateListener

AStateListener register's itself with theDispatcher to be notified each time theState changes. It must implement theStateListener interface. It can be any object including an Activity, Fragment, View or Service (running in the same process) etc.

A listener is added using theDispatcher.addListener(StateListener) andDispatcher .removeListener(StateListener) to remove it.

hasStateChanged(State newState, State oldState) is a convenience method to help short-circuit the dispatch process if we aren't using an immutable state. You can localize your checks to certain nodes of the state tree specific to this listener.Since we assume your state is immutable, the default implementations usereturn newState != oldState. If this returns false,onStateChanged is not called.

onStateChanged(final State state) is called when the state has changed. This is not called on theMain orUI thread but on a dedicated background thread. If you wish to make any changes to any UI element, you will need to post a runnable. We provide you a utility method for this usingThreadUtils.runOnMain(Runnable).This design choice was made intentionally to allow you to be able to do any processing (like loops) you wish off the main thread before updating the UI. This allows the UI to remain responsive all the time.

We have base implementations likeStateListenerActivity,StateListenerFragment andStateListenerView that take care of handling the lifecycle and registering and unregistering of the listener.

###MiddlewaresMiddlewaresprovides a third-party extension point between dispatching an action, and the moment it reaches the reducer.The middlewares represents a good place for logging, crash reporting, talking to an asynchronous API, interact with databases, and more.

To register aMiddleware, you need to callDispatcher.registerMiddleware(Middleware) andDispatcher.unregisterMiddleware(Middleware) if you wish to remove it.

Middlewares implement theMiddleware interface.We provide one abstract implementations:BaseMiddleware coupled to the defaultDispatcher implementation.

BaseMiddleware requires you to implementintercept(State, Action) in which you can check if you want to handle that actionType.

publicclassLoggerMiddlewareextendsBaseMiddleware {@Overridepublicvoidintercept(Statestate,Actionaction)throwsException {Log.d("[LoggerMiddleware]",action.Type);     }  }

Dispatcher

We saved the best for last.

The dispatcher is the engine of Fluxxan. It manages the state, reducers and listeners, handles the dispatching of actions and the subsequent notifying of listeners.

After we create our dispatcher, we need to keep a reference to it so we can registerReducers andStateListeners as well as dispatchActions.We need to callstart andstop on the dispatcher to start or stop it.

Ideally, this would be done in a custom android Application so we can get a reference to it anywhere in our code.

The dispatcher checks if states have changed before notifying listeners, but since it assumes state is immutable, you will need to override it'shasStateChanged(State newState, State oldState) method to provide your own functionality.By default, it uses,return newState != oldState.

The dispatcher allows us to provide the ability for aReducer to wait for other reducers. This is an important feature in Flux not required in Redux.

Fluxxan

We provide you a coordinator to help manage the dispatcher. It's calledFluxxan.Fluxxan is used in default implementations so instead of dealing with theDispatcher directly (you can if you choose to) we useFluxxan.Fluxxan provides proxy methods that callDispatcher. By default, it uses theDispatcherImpl implementation.

In our app, let's see what this looks like.

AppStatestate =ImmutableAppState.builder().build();Fluxxan =newFluxxan<AppState>(state);Fluxxan.registerReducer(newTodoReducer());Fluxxan.start();

Proguard

For proguard, you will need to add this.

-keepclasseswithmembers class * {    @com.umaplay.fluxxan.annotation.BindAction <methods>;}

Contributing

Thank you for taking the time to contribute.But before you do, please read ourcontribution guidelines. They are simple, we promise.

Todo

  • Writing Tests

License

MIT

About

Fluxxan is an Android implementation of the Flux Architecture that combines concepts from both Fluxxor and Redux.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors5

Languages


[8]ページ先頭

©2009-2025 Movatter.jp