badoo / mvicore Goto Github PK
View Code? Open in Web Editor NEWMVI framework with events, time-travel, and more
Home Page: https://badoo.github.io/MVICore/
License: Other
MVI framework with events, time-travel, and more
Home Page: https://badoo.github.io/MVICore/
License: Other
Downloaded and launched your demo project. Tried to record and playback my actions from DebugDrawer, but nothing happened, when I pressed play. Some time later toast popped up with the text, that playback finished. What I'm doing wrong?
Could you please help me to find out better solutions to configurate custom views?
I have 20 different features on one screen. Each feature has its own view. The app has a dynamic theme. Each view has one input/state. I need to set theme settings for each view.
I hope that anybody has some ideas or any examples of how to do it in the best way.
Since invocations of the reducer must always happen on the same thread, you must ensure that you observe results of your asynchronous jobs on that thread.
Why don't you use scan
to execute the reducer? It handle the sync so you don't care about the thread. Add the observeOn(main) in the actor is a bit ugly. If the user of the feature cares about the thread they can always add the observeOn
.
At the moment DiffStrategy
return true if the values are different. Perhaps they should be inverted to be consistent with normal comparators. Also Rx distingUntilChanged
lambdas return true if values are equal. Inverted behavour is confusing.
Hi guys. Do you have any examples, best practices how you tests yours features?
I have a NavFeature which handles routes.
NavWish(path: String) goes in and NavState(module: String, args : ...) goes out.
All my code depends on Store<NavWish, NavState>.
Now I want to use a stack with NavStates, but I don't want the rest of my code to know that I'm using a Stack.
What is the best way to expose a Store-interface without the stack, but use the stack interrnally?
Is it possible to interrupt operations started by Actor? In my case single Wish generate a chain of requests. How can i prevent execution of remaining requests when new Wish arrives?
I'm working on implementing MVICore on a complicated screen and have many Features that need to reduce down to one ViewModel. The demo app uses an example with two and a pair with an extension function wrapping Observable.combineLatest()
I am finding this is not scalable enough for most situations. The code I am referring to is below:
I have attempted to use RxJava's combineLatest
that takes a list and a Function
(docs) but have run into an issue with generics and inheritance from the Features
.
I have spent some time working on a solution, but have not yet found one.
I have created an extension function like so:
<T, R> fun combineFeatures(
features: Array<ObservableSource<T>>,
combine: (Array<T>) -> R
): ObservableSource<R> =
Observable
.combineLatest(list) {
combine(it)
}
However in attempting to use this like:
binder.bind(combineFeatures(arrayOf(feature1, feature2) {
var combinedState = CombinedState() // Initial State
it.forEach { feature ->
when (feature) {
is Feature1 -> combinedState.copy(fromFeature1 = feature.data)
is Feature2 -> combinedState.copy(fromFeature2 = feature.data)
}
}
combinedState
} ) to this using ViewModelTransformer
and making the appropriate changes to the ViewModelTransformer
to accept the CombinedState
to then derive the data for the View
. The generics/inheritance issues arise in the arrayOf(feature1, feature2)
creation. Even if I define the Any
type. (This looks like it could be a limitation of the JVM)
What I'm hoping for here is to see if there is already an appropriate solution to the N-Features for 1-View problem.
Please let me know if there is any other information I can provide.
Hi there.
First: when i place Logger in the so-called async actor
fun loadRandomImage(): Observable<Response> {
Log.d("!@#", "thread is ${Thread.currentThread().name}")
return service.getRandomImage()
.randomlyThrowAnException()
.observeOn(AndroidSchedulers.mainThread())
}
i got
com.badoo.mvicoredemo D/!@#: thread is main
Second: when i try to emulate delay with
is LoadNewImage -> loadRandomImage()
.delay(1000, TimeUnit.MILLISECONDS)
.map {
LoadedImage(it.url!!) as Effect
}
.startWith(just(StartedLoading))
.onErrorReturn { ErrorLoading(it) }
i got
Caused by: java.lang.AssertionError: Not on same thread as previous verification
from your
class SameThreadVerifier
what do i wrong?
Could you provide a view on how to implement tests that cover interactions with UI. In MVVM it was solely done inside ViewModel but with MVI I'm not sure where it belongs.
Say, inside a fragment we need to combine values from several edittexts and send a wish based on some criteria:
Observable.combineLatest(
RxTextView.textChanges(newPinEditText),
RxTextView.textChanges(pinRepeatEditText),
BiFunction<CharSequence, CharSequence, Boolean>() { v1, v2 -> v1.length == v2.length && v1.length == limit }
)
...
.map{ some wish }
Is there an mvi way to test the logic before the map
?
I know there's a concept of actor middleware that deals with async requests and other backend logic - do you think it should also cover ui-related checks? Wouldn't it bloat State?
Thanks!
Hello, Guys!
Would you provide simple example on how to show Snackbar? Is it SingleLiveEvent? Or you just put the state of Snackbar into your feature's ViewState?
Current implementation of the feature launches internal subscriptions when created. Although it is conceptually correct, the limitations of the Android framework sometimes force us to wait until onCreate
to have any meaningful data.
This proposal suggests to add an optional startWhen
parameter to the feature constructor, so its start can be done after creation.
Suggested update to the feature constructor:
class BaseFeature(
initialState: State,
...,
startWhen: Single<Unit> = Single.just(Unit)
)
In MVI core demo app you pass AndroidTimeCapsule in Feature2 through constructor.
class Feature2(
timeCapsule: TimeCapsule<Parcelable>? = null
) : ActorReducerFeature<Wish, Effect, State, News>(
...
How it supposed to be used with Android lifecycle methods onRestoreInstanceState
and onSaveInstanceState
?
Is there a channel somewhere? I didn't find one on kotlinlang on Slack.
Observable.wrap(feature).subscribe().dispose()
doesn't dispose the feature.
The problem is that wrap
treats feature as an ObservableSource
. And it doesn't have the concept of dispose.
I know that you usually use the Binder
s but if you don't this is kind of strange because you expose an ObservableSource
that you need to dispose. So you end with things like this:
disposable += Observable.wrap(feature).subscribe()
disposable += Observable.wrap(feature.news).subscribe()
disposable += feature
An idea is to use a real Observable
. This way all will work "as expected".
In the mvicore
module, binder
is declared as an api
dependency but the pom published to jitpack does not include it. Is this intentional?
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.badoo.mvicore</groupId>
<artifactId>mvicore</artifactId>
<version>1.2.6</version>
<dependencies>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
<version>2.2.10</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxkotlin</artifactId>
<version>2.2.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk7</artifactId>
<version>1.3.72</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.3.72</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.amshove.kluent</groupId>
<artifactId>kluent</artifactId>
<version>1.23</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.nhaarman</groupId>
<artifactId>mockito-kotlin</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Hi there!
See the duplicate strings in logcat from LifecycleDemoActivity:
2018-11-21 10:13:58.769 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onPause
2018-11-21 10:13:58.769 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onPause
2018-11-21 10:13:58.770 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onSaveState
2018-11-21 10:13:58.774 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onStop
2018-11-21 10:13:58.827 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onStart
2018-11-21 10:13:58.828 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onRestoreState
2018-11-21 10:13:58.828 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onRestoreState
2018-11-21 10:13:58.830 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onResume
2018-11-21 10:13:58.830 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onResume
2018-11-21 10:13:58.831 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onPostResume
2018-11-21 10:13:58.831 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onPostResume
2018-11-21 10:13:58.831 14219-14219/com.badoo.mvicoredemo D/LifecycleDemo2: onPostResume
meanwhile standard logging makes things right:
override fun onPostResume() {
super.onPostResume()
Log.d("!@#", "onPostResume")
// events.onNext("onPostResume")
}
---------------
2018-11-21 10:21:28.715 14219-14219/com.badoo.mvicoredemo D/!@#: onPostResume
We need to decide what to do in Bootstrapper
based in the initial state quite often. Right now this needs to be passed manually to implementation constructor, which is quite inconvenient when e.g deciding the initial state based on TimeCapsule
, as a reference cannot be stored for it inside constructor.
Proposed solution is to change Bootstrapper
and BaseFeature
so that initial state is passed automatically to invocation.
AndroidTimeCapsule use forEach method that is available only from API 24 (see https://developer.android.com/reference/java/util/HashMap#forEach(java.util.function.BiConsumer%3C?%20super%20K,%20?%20super%20V%3E) and it leads to runtime crash when trying to save state on API lower than 24:
fun saveState(outState: Bundle) = stateSuppliers.forEach { key, stateSupplier -> outState.putParcelable(key, stateSupplier()) }
cc @Litun
In the description it stated that is 100% Kotlin, but in the examples I see dependencies on RxJava.
Where I can have a look pure Kotlin example that could be suitable for Kotlin Multiplatform project? Ideally if this example can demonstrate how to use MviCore between iOS and Android.
Hey everyone!
How do you guys deal with problem of unnecessary view updates?
For example, you have a counter view which one is being updated each second with a new value. And you don't need to update other views, because their state is not being changed. It means that you have redundant view rendering.
Do you have any solution to avoid this?
It looks like a really great lib which solves the most common problems/missings of other MVI implementations. Thank you very much for share it with us! :)
Maybe I have only one "issue":
When you render the viewmodel, because the state changed, you always render the whole object. Forexample if it contains a loading indicator boolean and a string and only the boolean changed, you also render the string.
Is this correct or I missed something?
I'm using a modified version of this code to avoid this situation:
https://github.com/OlegSheliakin/state-binder
What do you think?
I've a problem with installing mvi core file templates plugin, I've downloaded plugin from here.
Fun fact, my collegue also tried to install that plugin and he've the same version of android studio and got success these LOL.
I want to test my actor and observeOn
prevent me to do it.
What is the best way to move out Android platform classes from my actor?
class InsultActor(
private val clipboard: Clipboard,
private val share: Share,
private val api: InsultRepository
) : Actor<InsultState, InsultWish, InsultEffect> {
override fun invoke(state: InsultState, wish: InsultWish): Observable<InsultEffect> =
when (wish) {
is InsultWish.LoadInsult -> api.generateInsult(state.lang.literal)
.map(Insult::insult)
.map(::LoadedInsult)
.cast(InsultEffect::class.java)
.startWith(just(StartedLoading))
.onErrorReturn(::ErrorLoading)
.observeOn(AndroidSchedulers.mainThread())
is InsultWish.CopyInsult -> {
clipboard.copy(wish.text)
Observable.just(InsultEffect.CopiedInsult)
}
is InsultWish.ShareInsult -> {
share.share(wish.text)
Observable.just(InsultEffect.SharedInsult)
}
is InsultWish.LanguageDialog -> just(ShowLanguageDialog)
is InsultWish.DismissDialog -> just(DismissLanguageDialog)
is InsultWish.ChangeLang -> just(ChangedLang(wish.lang))
is InsultWish.OpenAbout -> just(OpenAbout)
}
}
Add some new checks inside DefaultFeatureTest for:
Привет.
Столкнулись с такой проблемой.
В Bootstraper вызывается invoke() до того, как newsListener будет связан с представлением и фичей и из за этого мы пропускаем событие, отправленное при старте запроса (Observable.startWith()
)
Как результат - ProgressDialog не будет показан при старте запроса.
Как можно решить эту проблему?
class ToursToHotelFeature(
api: Api
) : ActorReducerFeature<ToursToHotelFeature.Wish, ToursToHotelFeature.Effect, ToursToHotelFeature.State, ToursToHotelFeature.News>(
initialState = State(),
actor = ActorImpl(api),
reducer = ReducerImpl(),
newsPublisher = NewsPublisherImpl(),
bootstrapper = BootstraperImpl()
) {
class BootstraperImpl() : Bootstrapper<Wish> {
override fun invoke(): Observable<Wish> = Observable.just(Wish.StartSearch)
}
data class State(...)
sealed class Wish {
object StartSearch : Wish()
}
sealed class News {
data class ShowProgressBar(val show: Boolean) : News()
}
sealed class Effect {
object StartedLoading : Effect()
data class FinishWithError(val error: Throwable) : Effect()
}
class ActorImpl(val api: ApiService.TourApi) : Actor<State, Wish, Effect> {
override fun invoke(state: State, wish: Wish): Observable<Effect> = when (wish) {
Wish.StartSearch -> {
api.someRequest()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith(Effect.StartedLoading)
.doOnError { Effect.FinishWithError(it) }
.onErrorReturn { Effect.FinishWithError(it) }
}
}
}
class NewsPublisherImpl : NewsPublisher<Wish, Effect, State, News> {
override fun invoke(action: Wish, effect: Effect, state: State): News? {
return when (effect) {
Effect.StartedLoading -> News.ShowProgressBar(true)
is Effect.FinishWithError -> News.ShowProgressBar(false)
else -> null
}
}
}
}
Sorry, this is a question more than an issue but I since there isn't a mail list or similar I'll write it here. Please tell me if this is not the correct place.
We use MVICore in our app and we are having two problems that I'd like to share to see if we are doing something wrong, we are missing something or they are really issues so we can create a PR to fix them:
switchMap
but we can't use it here.EditText
. When you start typing in the EditText you send wishes with that data. And you generates a new state with that data. And in the render you should set that data. If you do so you get an infinite loop because you are changing the EditText again so it launch another wish... Ok, this problem is kind of easy to solve: distinctUntilChange
. The problem now is that we have an observeOn(mainScheduler)
so we lose one frame to sync the data. If the user types fast (not really fast, just fast) we are setting an "old text" so the user loses the last typed character. Here we are fighting against the framework and losing. How do you handle this problem?Hi.
In best practices you wrote next code
class BootstrapperImpl(
private val feature1: Feature1
) : Bootstrapper<Wish> {
override fun invoke(): Observable<Wish> =
feature1.news.map { SomeWishOfFeature2 }
}
But i can't understand best way to do this map , because news is ObservableSource and don't have map function.
Maybe you have some ktx extensions for this?
Now i do this.
class BootstrapperImpl(
private val feature: Feature1
) : Bootstrapper<Wish> {
override fun invoke(): Observable<Wish> =
Observable.create {
emitter ->
val newsListener = object : Observer<Feature1.News>{
override fun onNext(news: Feature1.News) {
when(news){
else->emitter.onNext(Wish.Update())
}
}
override fun onComplete() {
emitter.onComplete()
}
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {}
}
feature.news.subscribe(newsListener)
}
}
Error message providing me by IDEA:
Cannot access 'io.reactivex.ObservableSource' which is a supertype of 'com.example.androidbasics.fragments.FirstFragmentFeature'.
Check your module classpath for missing or conflicting dependencies
And the same one more errors in same class but with different "can't access":
Cannot access 'io.reactivex.functions.Consumer'
Cannot access 'io.reactivex.disposables.Disposable'
It also shown randomly, for example, I just formatter file or add any symbol and code analyzer do wakeup :).
Deps related probably to this issue:
implementation "io.reactivex.rxjava3:rxkotlin:$rxkotlin_version"
implementation "io.reactivex.rxjava3:rxandroid:$rxandroid_version"
implementation "io.reactivex.rxjava3:rxjava:$rxjava_version"
where rxkotlin_version
, rxandroid_version
is 3.0.0 and rxjava_version
is 3.0.4
Fragment state to reproduce (some lines formatted to in-line): it's pure boilerplate code
class FirstFragmentFeature : ReducerFeature<Wish, State, Nothing>(
initialState = State(), reducer = ReducerImpl()
) {
data class State(val counter: Int = 0)
sealed class Wish {
object IncreaseCounter : Wish()
data class MultiplyBy(val value: Int) : Wish()
}
class ReducerImpl : Reducer<State, Wish> {
override fun invoke(state: State, wish: Wish): State {
return when (wish) {
Wish.IncreaseCounter -> state.copy(counter = state.counter + 1)
is Wish.MultiplyBy -> state.copy(counter = state.counter * wish.value)
}
}
}
}
Installed mvicore dependencies (in app/build.gradle): used 1.2.4 version
implementation "com.github.badoo.mvicore:mvicore:${mvicore_version}"
implementation "com.github.badoo.mvicore:mvicore-android:${mvicore_version}"
implementation "com.github.badoo.mvicore:mvicore-diff:${mvicore_version}"
Could you please give me a link to example app? Because I cannot find it.
I use DialogFragment with the common feature of parent Fragment and another ui event transformer.
class Fragment :
SourceFragment<FragmentUiEvent>(),
Consumer<FragmentViewModel> {
...
private val feature by scope.inject<Feature>
...
class Dialog :
SourceDialog<DialogUiEvent>(),
Consumer<DialogViewModel> {
...
private val feature by scope.inject<Feature>
...
But i not sure that is the best way of using dialogs.
Can somebody show example of using dialogs with MVICore?
Hi.
Is there best way to use navigation?
I using:
sealed class News {
object GoToFragment1 : News()
object GoToFragment2 : News()
}
sealed class Effect {
object Fragment1: Effect()
object Fragment2: Effect()
}
class ActorImpl() : Actor<State, Wish, Effect> {
override fun invoke(state: State, wish: Wish): Observable<Effect> {
return when (wish) {
Wish.Something -> if (some logic) Effect.Fragment1 else Effect.Fragment2
}
}
}
class NewsPublisherImpl : NewsPublisher<Wish, Effect, State, News> {
override fun invoke(wish: Wish, effect: Effect, state: State): News? {
return when (effect) {
Effect.Fragment1 -> News.GoToFragment1
Effect.Fragment2 -> News.GoToFragment2
}
}
}
But I have problem in Reducer.
class ReducerImpl : Reducer<State, Effect> {
override fun invoke(state: State, effect: Effect): State {
return when (effect) {
Effect.Fragment1 -> state // not changed
Effect.Fragment2-> state // not changed
}
}
}
state is not changed.
NewsListener works fine. Fragment changed.
But in first fragment called accept.
Is there way to don't call accept when state is not changed?
This may be by design but it was not immediately obvious and drove me insane this morning. I was looking for the logging for an Actor
in one of my Feature
s but I was not getting any output. Turned out that a refactor recently had the side-effect of the Feature
being instantiated before the LoggingMiddleware
was added.
Is this a case that could be handled or if not, maybe it is worth calling attention to in the wiki.
This proposal allows us to move on from RxJava only implementation.
The general idea is to have a minimalistic "core" as a Kotlin MPP module with our own implementation of the base types: Source<T>
, Consumer<T>
, Feature
and Binder
.
The framework level contains a set of extensions to convert between "core" types to framework ones (e.g. from Source<T>
to ObservableSource<T>
from Rx and vice versa). In current PoC, we need to provide the following implementations:
Source
and Consumer
to framework typesAs we don't do any thread/concurrency management in the "core" implementation, I suspect we need to have some checks (similar to current SameThreadVerifier
) in place to prevent race conditions.
We have a variety of complicated cards to use in a RecyclerView and we would like to use a Feature for each card. The cards observe multiple Observable sources, and need to be disposed of properly when recycled/backgrounded/etc.
Should we be calling .dispose() on a Feature that belongs to a card that is being recycled?
Is there a way to re-use a Feature after it has been .disposed()? We would rather not have to allocate new Features in onBindViewHolder() as the object allocations can lead to GC/jank.
How does the Binder that we use play into this? Do we need to call .dispose() on it as well? Or is that unnecessary if we are calling .dispose() on each bound Feature when it's no longer needed?
Thank you!
If SameThreadVerifier
is enabled we can capture stack trace where this object was created and then print it on assertion error (or add it as a cause). It should help with debugging such errors.
Hi! I'm creating a module that expose a feature to other modules to consume. The only things that I want to expose in my feature are: the Feature
, the Wish
s and the State
(and the News eventually). I want the other classes internal.
I don't want to expose the Action
s. Is there a way to set the Action
s internal
?
Here a really basic example:
class BasicFeature<T>(
seed: Single<Either<Throwable, T>>,
@Named("main") main: Scheduler
) : BaseFeature<BasicWish, BasicAction, BasicState<T>, BasicState<T>, Nothing>(
initialState = BasicState.Loading,
actor = BasicActor(seed, main),
reducer = { _, newState -> newState },
bootstrapper = { Observable.just(BasicAction.Load) },
wishToAction = {
when (it) {
is BasicWish.Refresh -> BasicAction.Load
}
}
)
sealed class BasicWish {
object Refresh : BasicWish()
}
internal sealed class BasicAction { // I can't set this internal because it's exposed by BaseFeature
object Load : BasicAction()
}
sealed class BasicState<out T> {
data class Data<T>(val data: T) : BasicState<T>()
object Loading : BasicState<Nothing>()
object Error : BasicState<Nothing>()
}
We represent our view model as sealed classes a lot of times and was looking to leverage ModelWatcher
but I'm struggling to come up with a good way to handle this. Take for example the following view model:
sealed class TransportControlsViewModel {
data class Show(
val startTime: Long,
val position: Long,
val duration: Int
) : TransportControlsViewModel()
object Hide : TransportControlsViewModel()
}
My initial approach was to set to watch the entire model but that turned out not to be very helpful because, when shown, there is a new model emitted every second with the only property usually changing is position
.
Have you had to handle any similar scenario in your since implementing this?
Encountered during a use case where feature and screen had longer lifecycle than a binder.
Hopefully, an example will illustrate it to some extent.
Let's say there is some feature and some events from UI:
class MyActivity: Activity() {
val uiEvents = ViewBinder();
val feature = MyFeature()
}
The feature lives in the scope of Activity and destroyed together with it.
Then consider that the feature should update the UI only between onStart()
and onStop()
while executing some actions in the background if needed (through Bootstrapper
or smth).
To achieve this one can introduce some lifecycle and rebind in onStart()
:
val lifecycle = Lifecycle.manual()
val binder = Binder.from(lifecycle)
override fun onStart() {
// Usually done in ViewBindings.setup, but still reflect actual case
binder.bind(uiEvents to feature using ::uiEventToWish)
binder.bind(feature to uiEvents using ::stateToViewModel)
}
override fun onStop() {
lifecycle.end()
}
However, these bindings need to be initialised every onStart()
event, which is kinda error prone. The binder interface could consider storing connections it has and reconnect them when lifecycle.begin()
is triggered.
Then, the user connect everything only once, and the lifecycle events will be managed by the framework (e.g. ViewBindings
abstraction that already exists).
val lifecycle = Lifecycle.manual()
val binder = Binder.from(lifecycle)
init {
// Usually done in ViewBindings.setup, but still reflect actual case
binder.bind(uiEvents to feature using ::uiEventToWish)
binder.bind(feature to uiEvents using ::stateToViewModel)
}
override fun onStart() {
lifecycle.begin()
}
override fun onStop() {
lifecycle.end()
}
The connections using repeatable Android lifecycles (Resume-Pause, Start-Stop) can be subscribed several times, as activity callbacks are triggered after onNewIntent.
Possible solutions:
It's very convenient to have the list of contents on the sidebar.
Hello!
I've faced a problem while trying to use StartStopBinderLifecycle
with fragments.
When going back and forth between fragments, binding happens every time and it creates duplicated connections and subscriptions. After that NewsListener's accept method called 2 times. (I'm not sure if reducer or actor are affected)
P.S. .distrinctUntilChanged()
doesn't help.
Thanks!
How can I handle multiple Feature to View bindings? Where ViewModels should be merged into one to render in View? Do you have any examples of this?
Is there a reason why the mvicore-android
artifact is no longer published as of 1.2.5
? Looks like the com.github.dcendents.android-maven
plugin was removed from the build script as part of #134.
Is it possible to use this framework with coroutine instead of RxJava?
Current implementation of NewsPublisher
and PostProcessor
has a signature of (Action, Effect, State) -> T?
, which can be somehow limiting, when you want to diff the previous and current state.
The common suggestion to update the signature to include the previous state, e.g. (State, Action, Effect, State) -> T?
, which seems to be the best idea for now.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.