Yarikx / reductor

Redux for Android. Predictable state container library for Java/Android
Apache License 2.0
463 stars 27 forks source link

Is this implementation thread safe? #28

Open ysyyork opened 7 years ago

ysyyork commented 7 years ago

I plan to use dispatch action from a background service within a different thread. Is this implementation thread safe or I have to do it myself? Thanks.

Yarikx commented 7 years ago

Hi @ysyyork

The short answer is YES, the implementation should be thread safe. Each action is applied to reduce function guarded by synchronised block. So you can dispatch actions from any thread.

However, you may still have some problems with those. On each action, Reductor notifies all state listeners on the thread where the action was applied. That might be the problem sometimes, for example:

In Activity, UI code is listening for state updates and render it on each update. If you will send any action from a background thread (for instance when data is loaded from a network) - render will also be executed from that thread, leading to updating a UI not in main thread.

Usually, I solve that problem by enforcing all the actions to be propagated through Main Thread. I use simple middleware to do that:

public class MainThreadMiddleware<T> implements Middleware<T> {
    private final Handler handler;
    private MainThreadMiddleware() {
        handler = new Handler(Looper.getMainLooper());
    }

    public static <T> MainThreadMiddleware<T> create() {
        return new MainThreadMiddleware<>();
    }

    @Override
    public Dispatcher create(Store<T> store, Dispatcher nextDispatcher) {
        return action -> {
            if (Looper.myLooper() != Looper.getMainLooper()) {
                handler.post(() -> nextDispatcher.dispatch(action));
            } else {
                nextDispatcher.dispatch(action);
            }
        };
    }
}

The idea is simple: if action is dispatched from other thread then Main looper - re-dispatch it! So now I do not need to care about where to dispatch an action. I still need to care about not doing blocking operation in .subscribe(listener) method, but that's where RxJava can help with .observeOn(scheduler) method.

ysyyork commented 7 years ago

Thanks, this is really helpful. On Mon, May 15, 2017 at 05:21 Yaroslav notifications@github.com wrote:

Hi @ysyyork https://github.com/ysyyork

The short answer is YES, the implementation should be thread safe. Each action is applied to reduce function guarded by synchronised block. So you can dispatch actions from any thread.

However, you may still have some problems with those. On each action, Reductor notifies all state listeners on the thread where the action was applied. That might be the problem sometimes, for example:

In Activity, UI code is listening for state updates and render it on each update. If you will send any action from a background thread (for instance when data is loaded from a network) - render will also be executed from that thread, leading to updating a UI not in main thread.

Usually, I solve that problem by enforcing all the actions to be propagated through Main Thread. I use simple middleware to do that:

public class MainThreadMiddleware implements Middleware { private final Handler handler; private MainThreadMiddleware() { handler = new Handler(Looper.getMainLooper()); }

public static <T> MainThreadMiddleware<T> create() {
    return new MainThreadMiddleware<>();
}

@Override
public Dispatcher create(Store<T> store, Dispatcher nextDispatcher) {
    return action -> {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            handler.post(() -> nextDispatcher.dispatch(action));
        } else {
            nextDispatcher.dispatch(action);
        }
    };
}

}

The idea is simple: if action is dispatched from other thread then Main looper - re-dispatch it! So now I do not need to care about where to dispatch an action. I still need to care about not doing blocking operation in .subscribe(listener) method, but that's where RxJava can help with .observeOn(scheduler) method.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Yarikx/reductor/issues/28#issuecomment-301421979, or mute the thread https://github.com/notifications/unsubscribe-auth/AGYi5bQyKE-xzniN4kbAim2TSrHi20DFks5r6BkCgaJpZM4NZ_U3 .

devhed commented 6 years ago

Any suggestions on how to propagate actions dispatched from a background worker thread to the main thread for non-Android specific Java projects?