birdsofparadise

    birdsofparadise

    1 year ago
    The Compose's NavigationController doesn't allow for "free-form" navigation. I get that having a laid out static navigation graph is great, but in a considerably large project I don't think its tenable. I was able to create a "hacky" solution. However I'm curious why "free-form" navigation isn't directly supported? Answer: A static graph must to be created at start up to handle resuming after process death Hopefully something better will come along soon!
    Adam Powell

    Adam Powell

    1 year ago
    What happens when your app process is terminated in the background and the user returns to the app?
    birdsofparadise

    birdsofparadise

    1 year ago
    I haven't tested that out yet, but what do you expect?
    Adam Powell

    Adam Powell

    1 year ago
    The reason for the static/parameterized navigation graph is so that the system can always restart your app from a deep destination without re-running all of the navigation code that got you there. I would expect that with the code above, the navigation system would try to navigate to a route that doesn't exist, since the code that navigated to it never ran.
    so either your app would crash when the user returns to it, or it would restart from home from scratch after getting confused. Which one of these is preferable depends on your perspective 🙂
    birdsofparadise

    birdsofparadise

    1 year ago
    How does compose's navigation normally handle this?
    Adam Powell

    Adam Powell

    1 year ago
    Normally since the routing is declared up front, the navigation can restore from the saved instance state and know what the last state of the back stack was
    so you'll go straight there
    birdsofparadise

    birdsofparadise

    1 year ago
    I see which is the purpose of having a static navigation graph
    that said most apps don't bother handling process death
    Adam Powell

    Adam Powell

    1 year ago
    those apps are deeply broken. All it takes is launching a web browser or the camera on most android devices and all other apps get killed in the background.
    we'd prefer to get people set up for success 🙂
    You could write an alternative navigation system that didn't require explicitly declaring everything up front, but at a minimum it's going to have to be able to restore your navigation state and back stack from a flattened/serialized/parceled form when your app is launched from a cold start
    so you would need some sort of functional mapper from navigation state => calling the right composable with the right parameters
    birdsofparadise

    birdsofparadise

    1 year ago
    Do you think Google will ever provide such a solution?
    Adam Powell

    Adam Powell

    1 year ago
    Maybe! We're certainly open to going where the developer interest is if we can make it work. I see @Ian Lake lurking in the emoji reactions above so if he feels strongly against it he can correct me here 😛
    i

    Ian Lake

    1 year ago
    What makes you think the current approach isn't tenable in a large project? Note that you don't have to build your graph in one file - you can call methods, use a for loop to go through subgraph providers, etc. You have all the power of Kotlin code
    Adam Powell

    Adam Powell

    1 year ago
    to a first approximation though, the current setup of navigation with arguments is already this same kind of functional mapping
    birdsofparadise

    birdsofparadise

    1 year ago
    Maintaining a routing table for every view in large app is a big ask
    i

    Ian Lake

    1 year ago
    For all the reasons Adam mentioned above, defining the graph ahead of time is a conscious decision by the Navigation Component
    birdsofparadise

    birdsofparadise

    1 year ago
    Obviously theres no other solution, so thats what we'll do. Its just creates a more rickety app in the end.
    i

    Ian Lake

    1 year ago
    I think if you approach it in terms of building small, modular features with their own subgraphs, each individual graph becomes a lot more manageable
    Adam Powell

    Adam Powell

    1 year ago
    many of the patterns that are applicable to something like ktor or spring boot also can be used here; declaring extension functions on the navigation scope, etc.
    birdsofparadise

    birdsofparadise

    1 year ago
    Yes it seems like a web server approach to a UI application
    Adam Powell

    Adam Powell

    1 year ago
    I'm fascinated by the fear that this forms something "rickety" and would like to know more 🙂
    my inclination is the opposite; that calling
    navigator.navigate {
      SomeOtherComposable(params)
    }
    from arbitrary event handlers would be very difficult to maintain as an app grows. I'd love to understand more of the perspective you're approaching this from
    birdsofparadise

    birdsofparadise

    1 year ago
    Well you seem quite set in your beliefs so I doubt I will convince but here goes For starters comparing
    navigator.navigate { SomeOtherComposable(params) }
    to
    navigator.navigate("some/route?id=3")
    . The first one is clearer and you get the benefits of the compiler checking the view and route works. The second one while it taps into a central routing system, it's more arbitrary than the first. 1. Having a centralized routing table works for server side development because routing between "views" already has a large level of indirection. Also with a web server theres not often much connection between individual routes. UI Applications routing between views is a direct operation and its often time theres a strong connection between views. 2. Having an explicit "entry" points for a "view" is a must for a large project. This is an area Android has traditionally suffered. The navigation component team has improved this with named generated routes. This has been super helpful. 3. When managing an App with 100 views and roughly 2 entries per view, thats 200 different routes. A views individual route should almost always be stored along side the view itself. This has been done by having static constructors with Fragments or just named constructors for VCs on the iOS side. This isn't always the case, but it is most of the time. So the current "best practice" with Compose's navigation system is: 1. A individual Compose view includes its possible routes in its own file 2. A module or subset of Compose views collects all the possible routes into a list. You probably include your route manipulation logic here if needed. 3. The app collects all possible modules routes into one master list. 4. You then route via a string path vs 1. A individual Compose view includes its possible routes in its own file 2. You route by directly using the exposed Compose view routes Other aspects this improves 1. When figuring out how to route to a view, there is only one source of truth 2. Adding and removing view does not involve editing a high traffic file 3. Removing a view causes a compiler error I get the process death requirement, I just wish there was a better solution. Thanks 🙂
    Adam Powell

    Adam Powell

    1 year ago
    Thanks! I don't have a terribly strong opinion here beyond meeting the process death requirement, so I'd consider anything on the table; it's still early days here
    I could imagine some sort of setup where you push some sort of object implementing an interface or similar that can serialize its captured parameters as a back stack entry rather than a raw composable lambda, which might get closer to what you're looking for here. It also starts looking suspiciously like a fragment with an abstract composable method depending on your angle
    birdsofparadise

    birdsofparadise

    1 year ago
    Yes but there needs to be some sort of abstracted "ViewState" that handles taking in inputs and surfacing outputs
    With Compose the benefit is that the two can finally be cleanly separated
    and connecting the two together is a lot simpler
    similar that can serialize its captured parameters as a back stack entry rather than a raw composable lambda
    That would work, I guess you would serialize a class type which surfaces a the Composable function
    Adam Powell

    Adam Powell

    1 year ago
    Yeah, something like that. The trouble with arbitrary lambdas is that they're so useful, half the stuff you end up capturing just works and it starts building expectations
    And then you're hanging onto an arbitrarily large object graph
    birdsofparadise

    birdsofparadise

    1 year ago
    Ya especially when Android's process death is such a pervasive issue that direct injection of arguments into "ViewState"s shouldn't happen
    Adam Powell

    Adam Powell

    1 year ago
    But we've been burned endlessly by saving a class type and instantiating by reflection. After a decade of it on android I think we're over it; explicit serialization/factories are generally the better bet, but then it's more boilerplate to write
    And we've been really hesitant to tell people that they need a DI framework to tie their shoes
    birdsofparadise

    birdsofparadise

    1 year ago
    My own implementation wasn't meant to be taken as a good one btw
    Adam Powell

    Adam Powell

    1 year ago
    It's a real one though; it expresses what you reached for and that's useful data
    birdsofparadise

    birdsofparadise

    1 year ago
    Ya but Androids application framework is set up to somewhat require a DI framework
    Adam Powell

    Adam Powell

    1 year ago
    Mostly to solve some particular own-goals we performed over the years
    I'm still holding out hope we can back up and reverse those root causes rather than give into it. Might be a fool's errand, but still
    DI is great, but compose has us so close to Android being a platform you can credibly learn to program for the first time with. DI as a price of admission is pretty steep.
    birdsofparadise

    birdsofparadise

    1 year ago
    Ya
    Compose has been great
    Adam Powell

    Adam Powell

    1 year ago
    Thanks 😄
    birdsofparadise

    birdsofparadise

    1 year ago
    I work a lot both iOS and Android
    Adam Powell

    Adam Powell

    1 year ago
    Some of the stuff here has me hopeful for DI-like use cases: https://youtrack.jetbrains.com/issue/KT-10468
    birdsofparadise

    birdsofparadise

    1 year ago
    I know you probably don't hear this to often, but I prefer Android as a dev platform
    my biggest complaints are around activities, fragments, and navigation lol
    y'all do great work ❤️
    Adam Powell

    Adam Powell

    1 year ago
    Hah, yeah, and having worked in the guts of all three over the years, there's a ton of good reason for them to be complaints
    No technology would get real good complaints without them being too useful to dismiss entirely though 🙂
    birdsofparadise

    birdsofparadise

    1 year ago
    haha true true
    Adam Powell

    Adam Powell

    1 year ago
    Anyway, truly, I appreciate the thoughtful feedback here. I know sometimes it looks like we're set in our ways or holding the party line, but the ground truth feedback like this is the best kind we can get
    birdsofparadise

    birdsofparadise

    1 year ago
    Thanks!
    i

    Ian Lake

    1 year ago
    Yeah, it is always great to see different perspectives
    For example, many developers that have reached the "hundreds of screens" level have gone to a many, many module segmentation where there's no compile time dependency between different features (this is particularly relevant when avoiding circular dependencies). This makes the indirection through routes a huge boon
    So the approach of having each UI/feature module provide a graph, then composing them together in a higher level graph (and then up to your whole app level graph) has been a really natural thing for devs to be doing in Navigation for the last few years with good success. If anything, that is way easier to do in the Navigation Compose world and even easier to keep the Navigation code out of your composables themselves (as we mention as a best practice in our testing docs: https://developer.android.com/jetpack/compose/navigation#testing)
    I think where ever we end up, what we provide will have to ensure that the user doesn't lose their state even in the unpredictable world of Android. That's not always easy, but I think we'll get to a better spot by looking at a wide range of viewpoints and ideas. One of the best things about communities like this ❤️
    Colton Idle

    Colton Idle

    1 year ago
    @Adam Powell "those apps are deeply broken. All it takes is launching a web browser or the camera on most android devices and all other apps get killed in the background." I still wish Android made this easier to test. There should definitely be a big huge button in Android Studio that allows you to trigger process death. There's is a workaround in AS by pressing the stop button in the logcat pane, but even the functionality of that changed in some beta back in the day. I'd wager 99% of android devs don't even know of a reliable/easy way to trigger process death.
    birdsofparadise

    birdsofparadise

    1 year ago
    Hahaha totally, I remember opening up a ton of poorly optimized Wikia sites to force it
    Thanks @Ian Lake We haven't broken our views up into many modules. I tried pushing for it, but I failed at selling its benefits lol
    Sergey Y.

    Sergey Y.

    1 year ago
    @Colton Idle @birdsofparadise Android has "Don't keep activities" option at Developer options menu. It does exactly what you need 😃 Also, ADB has an equivalent command to test that particular behavior. Happy New Year 🥳
    birdsofparadise

    birdsofparadise

    1 year ago
    @Sergey Y. Thanks thanks. I do wish it was as easy as a single click, but its a billion times easier than loading up bad websites lol.
    Happy new year!
    Adam Powell

    Adam Powell

    1 year ago
    Don't keep activities can be useful, but it's also not a full process restart. If anything is relying on live references in the process you won't catch it
    Colton Idle

    Colton Idle

    1 year ago
    @Sergey Y. yea. my point wasn't that it doesn't exist, it's that it's not as easy as it should be. IMO if the android team cared about process death restoration then they would give you an easy way to trigger it. 5 years of working on Android apps and none of the teams have cared about process death. (i do, it's just that my teams haven't). Maybe just bad luck of teams on my part.
    Sergey Y.

    Sergey Y.

    1 year ago
    would give you an easy way to trigger it
    Come on, you are developer, an engineer, 5 min to read official documentation and you are good to go 🙂 Sometimes I regret that the official development language is not C++, there is no place for tenderness. Only brutal skills and reading manuals. Ah, modern developers... 😞
    Adam Powell

    Adam Powell

    1 year ago
    If I had a nickel for every time I heard, "if the android team cared about X..." 😂
    Colton Idle

    Colton Idle

    1 year ago
    @Adam Powell I'm unsure it's unfair of me to say. But process death has just become a thing that no one cares about seemingly. Idk maybe just my point of view tho. /Shruggie
    Adam Powell

    Adam Powell

    1 year ago
    Most of the reason for that is because we've spent years building tools and libraries that facilitate ignoring it. From activity stacks being managed by the system and stock views implementing savedInstanceState, to fragment back stacks handling it, to Room and encouraging more robust disk persistence rather than relying only on savedInstanceState, to arch components ViewModel and SavedStateHandle and now the savedInstanceState APIs for Compose. The reason Activity recreation/state restoration works the way it does is in no small part to encourage supporting the full process death case correctly and to deliberately make an app look more broken more often when it doesn't. 🙂
    Colton Idle

    Colton Idle

    1 year ago
    Yeah. I think I read that from Dianne way back on stack overflow. A hill I'd still die on is that process death should be a button in AS. I think I submitted a bug on issue tracker once they came out with the new process death apis. 😁 I only mention it because I care about process death restoration but I'm always on teams that don't care.
    Adam Powell

    Adam Powell

    1 year ago
    adb shell kill -9
    has worked for longer than android studio has existed 😛
    There's an ongoing discussion about the big red stop button in AS with the WorkManager team though
    Currently it performs a full force stop, not just a process kill. Sticky services don't restart, etc.
    And system jobs are cancelled
    If you're testing background work like with WorkManager, this is deeply disruptive and they don't like this behavior
    But if you're working on an app that is misbehaving and it just keeps restarting itself and continuing to misbehave, that's no good either
    The possible use cases for stopping an app from running and just what a developer means and expects when they say those words out loud are not universal
    Colton Idle

    Colton Idle

    1 year ago
    Yeah. That's what I mean. There's different things available to us like that. Swipe the app away, force stop in settings > app, kill via adb, stop button in AS. It would just be nice to have a little pane in AS that helps test these situations deterministically. That's all I'm advocating for. 😁
    Adam Powell

    Adam Powell

    1 year ago

    http://m.quickmeme.com/img/f5/f5bb63fcd28e68c2305fc6276babf73a7d2417e8ece8901e08e2afe3f328e8ab.jpg

    😁
    To be clear, I'm not dismissing the idea or request, but there is a nontrivial UX question involved that can't be handwaved either