GithubHelp home page GithubHelp logo

Working with activities about jedux HOT 15 OPEN

trikita avatar trikita commented on June 9, 2024
Working with activities

from jedux.

Comments (15)

ElectricCookie avatar ElectricCookie commented on June 9, 2024

I simply passed the Activity as argument to the dispatched action. That way it isn't in the state to prevent memory leaks. The action shouldn't leak any memory, since it's garbage collected after it's been dispatched.

from jedux.

TillSimon avatar TillSimon commented on June 9, 2024

Thanks for the feedback. I would prefer to keep framework-classes completely out of actions/reducers/state, that's why I thought of middleware, where I often have to use framework classes anyhow. But I'll look into your approach.
Have you had any other challenge/constraints using the redux approach on Android?

from jedux.

ElectricCookie avatar ElectricCookie commented on June 9, 2024

Yeah I ran into some issues, but I'm still enjoying it.

  • Yeah, it's hard matching the current activity to the state.
  • I also had some issues with Anvil mounting child views. I used Fragments for that instead.
  • As for lists I used Standard RecyclerView Adapters with DiffUtils, which subscribe to the current state.
  • I also switched to Strings instead of Enums for identifying actions, since I needed to automatically generate actions for API Endpoints.
  • Using third party libraries is always a little tricky, sometimes you can encapsulate them, sometimes you need to actually pass Activity/Context with an action, which can get messy.

I also made a custom version of Jedux, since this version triggers a render after each middleware and doesn't allow to watch only a certain part of the State-Tree. There's a gist here

I will open issues for Anvil and maybe a PR for jedux once I'm no longer so busy

from jedux.

TillSimon avatar TillSimon commented on June 9, 2024

Great hints, thanks. One thing I was wondering is: The application process might be killed, but lateron recreated with the old task stack, i.e. an activity down the tree being recreated with its' savedInstanceState. At this point the app lost its global state (no matter if it was in the application subclass or a singleton). The current recreated activity might depend on some global state though that it cant or shouldnt recreate by itself (maybe data objects shared with other activities or a user object). Are you persistently storing the state tree (e.g. on every change)? I was thinking, this way, each activity could check for savedInState!= null and !app.isStateInit and then restore the state. Would be nicer in the application class, but doing this I couldn't distinguish between restoring with savedInstance and a clean relaunch, which would be nice.

from jedux.

ElectricCookie avatar ElectricCookie commented on June 9, 2024

I also was thinking about this. I am looking into using annotations in the State-Tree to mark sections that should be stored. You could also add a flag to the subscribe method which would indicate a fresh start maybe. This library should have support for seperating "parts" of the state tree so that you can have reducers which are only aware of a certain part of the state, like in the JS implementation of Redux. And with this partial tree support you could implement the persistence as well

from jedux.

vaughandroid avatar vaughandroid commented on June 9, 2024

I haven't actually tried this (currently just investigating Redux) but...

Your state needs to fully describe the current state the application should be in. That means it will be sufficient to work out what the current Activity should be. (In fact, it should be sufficient to describe the whole Activity stack.)

So, when the Activity is told that the state has been updated, it needs to check the state to see if it should still be active. If not, then it needs to decide whether it needs to finish() or launch another Activity.

Working out whether to call finish() or startActivity() should be fairly straightforward if you get the state data right.

A simple example might be: If HomeActivity receives the state "stateStack" : [ "home", "search" ]; then it can inspect the stack and see that "home" isn't the topmost state - it needs to launch the "search" state (i.e. SearchActivity). Similarly, if SearchActivity receives the state "stateStack" : [ "home" ]; it can see it isn't included in the stack, so it needs to finish().

A couple of points:

  1. Activities would only do startActivity()/finish() if they were in the active state.
  2. If you need Activities which can appear more than once in the stack, you need to make the data a bit more sophisticated. e.g. The state stack entries could include a unique ID.

from jedux.

ElectricCookie avatar ElectricCookie commented on June 9, 2024

I think the navigation via the current state is not really the problem we're facing right now. The problem I faced during the development of Redux Apps was that some methods executed in Reducers/Middleware might require the Context or Activity to perform an action.

Maybe the jedux library should add support to allow basic navigation stacks etc. without a lot of boilerplate code?

from jedux.

vaughandroid avatar vaughandroid commented on June 9, 2024

I intended to make a more general point, and sort of forgot. In my defence, it was last thing on a Friday. 😀

The general point was this: I think you ought to be possible - and desirable - to keep platform-specific code out of the middleware & reducers. The navigation example was the original example used in this post - one where you suggested passing the Activity, when there was a way to avoid doing so.

I could see it being useful/necessary to pass in the Context to some action creator methods, but I'd really want to avoid putting the Context into the actions themselves.

Anyway, this is hypothetical for me - I should probably shut my yap until I've actually tried putting this stuff into practice myself!

from jedux.

ElectricCookie avatar ElectricCookie commented on June 9, 2024

Keeping platform specific code out of middleware? I thought all of the "processing" is supposed to happen in middleware using Redux. Maybe I got the concept wrong in the first place, but it made sense and worked out while building my last app..

from jedux.

vaughandroid avatar vaughandroid commented on June 9, 2024

Sorry, I had my wires crossed re. the middleware - I think you're correct, it's inevitably going to need to use the Context and other Android system stuff.
I do stand by my point that it would be best to keep the Context out of Actions. I'll report back if I come up with an approach that works.

from jedux.

ElectricCookie avatar ElectricCookie commented on June 9, 2024

Another idea would be to move the actions and some middleware into the acitvities. That way you would have a more modular app which wouldn't need to do some of the nasty tricks that you currently have to perform (passing Context etc.)

from jedux.

TillSimon avatar TillSimon commented on June 9, 2024

Interesting idea to even reflect the task stack in the state tree. Haven't fully wrapped my head around this, whether there can actually be a really robust implementation of this. I feel that if the activity (or a middleware) has to listen to a general action ACTIVITY_TREE_CHANGED and analyze it anyway, I might as well listen to a specific action (e.g. from UI) and launch another act. or finish this one. I don't want to have it in the global state just for the sake of sticking to the pattern when its creating a lot of additional work.

Regarding activities that might be launched multiple times, in a single global state, you would have to somehow mark activity-related states and actions uniquely right? So a button press should only be recognized by the current instance and not by the other instance of the same activity that's sitting below in the task stack. Another action (e.g. API response) on the other hand should still be able to arrive in the background act. Matching this right, feels very cumbersome.

And as mentioned above, the saving and restoring of the global app state throughout the Android lifecycle doesn't feel right.
In my current approach I actually stepped away from the standard Redux pattern and use multiple stores - one global app store and one in each activity and fragment. This way I don't have to deal with above's problems and don't have these framework-related things in the state and still get the benefits of the unidirectional flow related to UI. Also I can gracefully handle savedinstancestate for each state according to its act/frags lifecycle. The app state remains for data that is either persisted/restored or is fine to restart with defaults (when app is killed and recreated).
What do you guys think?

Regarding the context passing: I don't put anything context-related in the reducer - those remain pure. And I also actually stepped away from having a lot of different middlewares. I tried this in the beginning, one for permission handling, one even for API calls,... Maybe that was a stupid idea in the first place, since you would have to manually match requests and responses. Switched to Rx with helper classes (that I invoke with context) for all of this. It feels a lot better keeping jedux-actions mostly for state-related events.

One thing that in my eyes doesn't nicely fit in the pattern is handling fragments - mostly when they are not fixed in a layout file, so when using the fragment manager or fragment adapters. If I handle the fragment manager from middleware, I need to have access to the framelayout container :( In a different scenario, if I use a viewpager+adapter in the Anvil-view, then I'd need access to the fragment manager in the view. Don't really find a way to keep those separated.

from jedux.

vaughandroid avatar vaughandroid commented on June 9, 2024

@TillSimon It sounds like you've got my idea backwards. The state tree shouldn't reflect the task stack - the task stack should reflect the state tree. i.e. The action which we want to trigger the Activity change would be specific, and would just result in the state being updated. It would be up to the current Activity to spot that the state has changed and it needs to launch a new Activity.

This might sound like an unnecessary amount of work (why not just launch the new Activity and not send an action?) but it's fundamental to Redux to keep the state as the single source of truth. The payoff is stuff like being able to test and debug more easily, and do things like rewind/playback.

The "multiple instances of the same Activity" thing is definitely an edge case (I can't think I've ever had a real-world use case for needing to do this), but it would be easily solved by tagging actions and state subtrees with a unique ID.

Re. your idea of splitting the global state into per-activity state, that sounds interesting. You mention you still need a global activity state though, so I'm not sure you've actually solved the issue. Plus, you now have to deal with dispatching actions to multiple stores... Maybe do a writeup somewhere if it turns out to be successful for you?

Re. Fragments, I don't think middleware has any business managing Fragments. That would be a concern for the View(s).

from jedux.

TillSimon avatar TillSimon commented on June 9, 2024

@vaughandroid ah you're right. I got half of it, but forgot that specific actions would trigger that state change.
I agree that it'd be nice to strictly stick to the pattern (unless I define my approach as a new pattern :) ). It just feels on various ends that it doesn't nicely fit the Android structure/lifecycles. But maybe I'm just missing something.

I actually had activities with multiple instances various times and I want this approach to cover this by default. Think of an e-commerce app with CategoryActivtities navigating down the category tree or a movie recommendation app with MovieDetailsAct that links to other movies.
Will think about the tagging part a bit more. Ideally I wouldn't want to have to think about this structural necessity in every place where I dispatch an action.

Well I don't solve the global state issue per se, but I limit it to the amount that I would have to deal with in any architecture on Android. Activities would handle their own state also across savedinstance-out/in (and no matter how many instances exist left and right) and global app state would have to be persisted (on every write) or lost at any point since there is simply no savedinstancestate-handling on the app level. That's what I mean by, it's closer to the Android way. If the entire state remained on the app level, how would we handle/distinguish app creation/restoring after process kill (rf. to my 3rd post above)? I wouldn't want to constantly persist all of it.

Guess I agree, fragments feel more like a view-thing. That means I'd have to pass the fragmentmanager to the views so they're able to handle this by themselves. But how to handle this with Anvil?! I figure we'd need something else than the view() method of RenderableView anyway. How would you also reflect something like scrollTo() on a list? That's not a state, but something that's really only valid when a specific action occurs, like new data arrived. But then the view wouldn't just be a static reflection of the state, but needed some other method that reacts to actions as well. Or am I missing something?

A writeup of what we discuss or learn might indeed be a nice thing at some point.

from jedux.

vaughandroid avatar vaughandroid commented on June 9, 2024

Wow, lots to respond to!

You're right that the strict Redux approach doesn't fit all that well with Android. It is a web javascript architecture after all, and their lifecycle is more conventional. Personally I'm looking into Redux (along with others like Flux and Cycle.js) to see what works for Android and what doesn't. Unless you go for the single-activity approach, the lifecycle on Android is so different that some things just won't fit well.

Your approach does feel more like the "Android way", but there may be edge cases which cause problems. I can't think of anything right now that couldn't be worked around, so like I said I'm interested in how you get on.

For the multiple activity instances thing, perhaps an alternative to IDs would be making the "path" to the active activity part of the global state. The path could potentially double as a deep link and as a path through your state tree.

As for the global state issue, you can only detect the difference between a process restart and a relaunch based on whether onRestoreInstanceState is called. I think the reload of the global state will need to be driven from there.

Re. Fragments, when I said "View(s)" I meant Activities, in their role as Redux Views. There may be some subtleties I'm missing here though, as it's not something I've considered carefully yet. I try to avoid Fragments wherever possible, and haven't tried Anvil yet!

from jedux.

Related Issues (2)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.