[HN Gopher] EventBus 3.1 with plain Java support
___________________________________________________________________
EventBus 3.1 with plain Java support
Author : qdghmkop
Score : 39 points
Date : 2021-06-19 14:11 UTC (8 hours ago)
(HTM) web link (greenrobot.org)
(TXT) w3m dump (greenrobot.org)
| billjings wrote:
| 1. I'm happy to see that EventBus has made this change. Let's
| hope the long overdue AndroidX migration (we're three years into
| AndroidX, folks) follows close on its heels.
|
| 2. Event buses are really, really bad. (At least, this kind of
| event bus is) The Android community has some battle scars on
| this, so I'll drop a little history for the broader audience
| here.
|
| Event buses were an architectural fad that were briefly explored
| to address the challenges of communicating in the immature
| application architectures of the era. The maintenance lifetime of
| Otto, a competing event bus, is a good reference point for when
| they might have been considered reasonable practice: 2012 through
| 2015: https://github.com/square/otto/tags
|
| This tool was abandoned by leading edge shops when they saw how
| rapidly it could make a complete hash of any thoughtfully laid
| out architecture. Connections made in an EventBus based
| application tend to be many-to-many, without the sender of an
| event having a direct reference to its recipient or vice versa.
| This is incredibly irritating to debug, and breeds communication
| patterns that are challenging even in a disciplined codebase. In
| an _undisciplined_ codebase they can be breathtakingly byzantine,
| even in small scale development.
|
| Instead of using this, many leading edge shops started switching
| to RxJava at around 2016. RxJava is a powerful tool with sharp
| edges and a steep learning curve, but the need was so imminent
| and the failings of the existing EventBus-style tools so clear
| that it caught on. Indeed, while Google understandably felt that
| RxJava was too complex to recommend as an introductory tool,
| their first party LiveData tool released a few years later was
| essentially RxJava with the edges sanded off.
|
| Of course, we're now even further down the road than that. Kotlin
| coroutines+Flow presents its own paradigm shift to contend with,
| but it's a clear step up from all the other solutions, and has
| Google's blessing as well. There's not much reason to start new
| development on top of anything except coroutines.
|
| So where does that leave EventBus?
|
| EventBus is at this point about as legacy as you can get without
| going all the way back to AsyncTask. Anytime I'm doing a code
| audit and see this dependency, red flags immediately go up: not
| only is it a sign that this code is far behind the times, but
| it's also a flag that I'm going to find some truly unfortunate
| and problematic design decisions.
|
| People need what they need, and it's of course good to see
| critical dependencies for legacy applications get upgrades. But I
| can't recommend strongly enough to avoid this tool.
| SV_BubbleTime wrote:
| Absolute side topic, but has anyone used an EventBus-like system
| for C? I have an RTOS project and would like to have some
| eventbus options to simplify inter task communication.
|
| As with everything, lots of options outside of C, not many in.
| trzy wrote:
| https://medium.com/prooftrading/proof-engineering-the-messag...
|
| Probably C++ and a bit more complex than you need but could
| serve as a source of inspiration for your own implementation.
| jafarlihi wrote:
| If you want to emit and catch events in Java with a lot less
| overhead check out my tiny event emitter library:
| https://github.com/jafarlihi/eemit
| an_opabinia wrote:
| Calling methods via strings where you control all the source code
| to your own application is pretty toxic.
|
| Since there are no network or persistence boundaries - it's a
| local app the developer owns 100% of - there are no engineering
| constraints that require the kind of decoupling of an event bus.
|
| The execution boundary thing ("post messages to other threads")
| is also pretty dumb. There are a dozen simpler ways to coordinate
| between a UI and background / business logic thread. Since most
| application's long-running behavior is _only_ HTTP calls, today
| 's developer doesn't have to think about it at all - the callback
| or promise (CompleteableFuture) returned by their URL fetcher
| forces them to do the right thing.
|
| RxJava is almost always the better choice to be more expressive
| about UIs. Otherwise, an Android developer should adopt Kotlin
| for coroutines. Or, frankly, reduce timeouts for HTTP calls to be
| closer to frame rendering times (16-32ms), pay the extra pennies
| to serve networked services better, and focus on low-latency
| remote APIs.
| optymizer wrote:
| I find your comment overly dismissive of EventBus and I
| respectfully disagree with you on not needing to decouple
| components for a local app.
|
| I've worked with GreenRobot's EventBus on a large (100M+
| installs) app years ago. I worked with RxJava as well. I've
| also worked with more feature-rich proprietary solutions to
| EventBus.
|
| EventBus is a library with a straightforward API that junior
| developers can use with little issues. "Launch this event and
| add this annotation to make sure it gets processed off the main
| thread". This is a very real practical advantage, that is not
| unique to EventBus, but it works well for large teams and does
| not require you to rewrite methods to return Observable objects
| just to be able to chain some things together. Does EventBus
| lead to some eventual problems? Yes. Are they manageable? Yes.
|
| On the other hand, RxJava is a framework and, once it's in, it
| starts spreading Observables through all the classes in the
| codebase. Most developers need significant time to ramp up to
| become productive with it. I used to work with 30+ developers
| on an Android app. The app I work on now at a different company
| has thousands of developers working on it. People at these
| companies collectively concluded that RxJava doesn't bring
| sufficient benefits to these large teams. Both companies have
| ripped it out pretty quickly after the initial excitement wore
| off and the downsides started kicking in.
|
| Kotlin made things much better in the recent years, but it
| doesn't mean that EventBus is now 'dumb' or 'toxic'. Coroutines
| on Android weren't widely used in these large apps a few years
| ago, before Google announced official support, so people were
| calling "new Thread()", using AsyncTask or, more likely, having
| ExecutorService threads created by various components in the
| design without any regard to CPU contention or priority of
| execution, and instantiating "new Runnable" all over the place.
|
| Also, using the HTTP handler's thread to process business logic
| _blocks that thread_ and makes it unavailable until the logic
| is done running - which could take a long time. Boundaries for
| execution are absolutely required, unless you're working on a
| small app that doesn't need to care about these issues and
| where pretty much anything goes. You want to have control over
| which logic runs on which thread, otherwise you'll needlessly
| spend time debugging race conditions and deadlocks in
| production, like me. HTTP threads for delivering HTTP results,
| image threads for processing images, database threads for
| writing to the database, and you want to set priority for
| background, I/O and foreground threads.
|
| That last point you have about reducing timeouts to be closer
| to frame rendering times is also incredibly narrowly useful.
| You can invest a ton of time and have the server respond in
| microseconds but if your users in Bangladesh are using cheap
| $20 Android phones over 3G connections you still have the same
| problem and you won't be anywhere near 32ms.
| an_opabinia wrote:
| > Also, using the HTTP handler's thread to process business
| logic _blocks that thread_ and makes it unavailable until the
| logic is done running - which could take a long time
|
| This is true.
|
| > if your users in Bangladesh are using cheap $20 Android
| phones over 3G connections you still have the same problem
| and you won't be anywhere near 32ms.
|
| This is of course the crux of the issue. In my opinion,
| either wait until the infrastructure catches up, or don't
| write an _interactive_ networked service, for that audience.
|
| Anyway, to at least advance the conversation, I searched for
| some big open source projects that use EventBus. (https://gre
| p.app/search?q=org.greenrobot&filter[path.pattern...). Signal
| for Android is a good example (https://grep.app/search?q=org.
| greenrobot&filter[repo][0]=sig...).
|
| Let's just take a look at the first class that appears in
| search results. "SqlCipherMigrationConstraintObserver." Okay,
| how in practice does this random thing get registered on the
| EventBus?
|
| Oh look, it's a file that is just instantiating a bajillion
| classes: (https://github.com/signalapp/Signal-
| Android/blob/master/app/...) Because dependency injection is
| stupid on GUI applications, and it messes with all sorts of
| benefits of direct invocation, they do direct invocation
| anyway. Except, instead of just like, calling whatever it is
| they need in SqlChiperMigrationConstraintObserver (or I don't
| know, just using a Subscription, it's literally called an
| Observer), they do the _same exact work_ just in a more
| puzzling way, via instantiating in one class and invoking
| in... who knows where.
|
| Again, this is why I say toxic. You're going to poke around
| the repos of what's going on when people actually use
| EventBus, and you'll discover, in the long term, that people
| will end up doing O(~direct invocation) work anyway, but
| _stringly typed_ (or in this case, _annotatively typed_ ).
|
| RxJava has its significant downsides. It's still indirect,
| it's very hard to debug call stacks with them! RxJava call
| stacks are meaningless. This is the right criticism. The
| wrong criticism is, "Bad developers can't handle it."
| randiRando wrote:
| I agree with your take on event busses but I feel like it
| describes RxJava pretty well too.
|
| > RxJava has its significant downsides. It's still
| indirect, it's very hard to debug call stacks with them!
| RxJava call stacks are meaningless. This is the right
| criticism. The wrong criticism is, "Bad developers can't
| handle it."
|
| How about "It's still indirect, it's very hard to debug
| call stacks with them! RxJava call stacks are meaningless"
| and because of that bad developers can't handle it ?
|
| Context: Have worked with RxJava (1&2) for about 6 years on
| different products and I agree that Rx is powerful but it's
| incredibly easy to over-use and abuse to the point of being
| a net-negative for a codebase. I think it takes a very
| disciplined developer to work with Rx. I love/hate Rx
| mwcampbell wrote:
| > Or, frankly, reduce timeouts for HTTP calls to be closer to
| frame rendering times (16-32ms), pay the extra pennies to serve
| networked services better, and focus on low-latency remote
| APIs.
|
| To be clear, are you suggesting that network requests in an
| Android app should block the UI thread, on the assumption that
| everyone will have a good enough connection (and one close
| enough to your server) to receive the response quickly?
| an_opabinia wrote:
| If you really want to achieve a great experience for an
| _interactive_ networked application, you should engineer
| things where everyone is close enough to your servers to get
| a response quickly. Or design an experience that does not
| need to be network interactive.
|
| > are you suggesting that network requests in an Android app
| should block the UI thread
|
| I'm suggesting that if network requests over a cellular
| connection could just be made within a frame (really, a tick,
| nothing is that fast), like they are on Fortnite or Roblox
| for Android, for example - if people applied the same
| engineering practices and standards to GUI applications as
| for realtime multiplayer games - yes, the apps would be way
| better, and as a side effect, way easier to author. The
| server and deployment story would get more complicated. But
| maybe that's a good thing, to put the hard work where it's
| easier to do.
|
| For a concrete example of what that would look like, the
| io_uring thing posted here just a bit ago was really
| fascinating. It's probably the way HTTP should have been done
| in Java from the get go - a request that returns immediately,
| but you must poll in the GUI that actually needs its results
| to render, and you must poll at the right time. Clunky, but
| still, all synchronous and all direct.
| Marazan wrote:
| Err network games deal with latencies in the hundreds of
| milliseconds.
| mwcampbell wrote:
| My understanding was that unreliable and high-latency
| mobile connections are inevitable, especially outside of
| major metro areas in the developed world, so we have to
| design around that.
| sgt wrote:
| Will also need a lot of unit tests that would normally be
| unnecessary.
| ncann wrote:
| Maybe I'm dumb but I've read your comments a few times over and
| I still haven't quite understand what you were trying to say.
|
| "Calling methods via strings where you control all the source
| code to your own application " -> what do you mean by this and
| how is it relevant to this library?
| an_opabinia wrote:
| EventBus uses an annotation processor and reflection to reify
| a call. A ton of things look like this. Like that's also sort
| of what a REST API is. It makes a ton of sense for crossing
| boundaries from owning the code to not owning the code, and
| back again.
|
| But since you own the code, you own 100% of the source in
| your GUI application, why would you need indirection? There's
| no reason to hide part A from part B. That's why giant
| companies who author all their stuff in house use monorepos.
| I can't believe I'm saying this, but hiding stuff, like
| trying to mark code as proprietary or "not maintained by my
| team" (as Amazon would do), is a social construct.
|
| This is relevant to the library because many times, people
| who have the experience of using this library in real Android
| apps and having a positive experience with it - they are
| using the library for to do, "my team does this part, and
| this other team does that part." "This developer is stupid
| and junior, so must decouple their work." Toxic! Amazon:
| extremely toxic. If I were a code anthropologist, DI and
| EventBus would be, "Artifacts of toxicity."
|
| When you look at how it's used by other apps, like Signal for
| Android, with small teams, it just looks stupid, and they
| work around its warts anyway, and use direct calling anyway,
| just in a far more roundabout fashion.
| ncann wrote:
| > But since you own the code, you own 100% of the source in
| your GUI application, why would you need indirection?
|
| So what is your proposed non-indirection way? I'm assuming
| you meant the traditional listener/observable way of
| registering event producers and event listeners together,
| in which case this FAQ explains pretty well their downsides
| compared to an event bus:
|
| https://github.com/google/guava/wiki/EventBusExplained#faq
| wffurr wrote:
| Instead of posting a string and a payload to the event bus,
| which a listener picks up and unpacks to call method in the
| receiver, just call the method.
|
| It does introduce a direct dependency where it was indirect
| before, but is otherwise much simpler and significantly
| faster than all the string comparisons.
| chris_overseas wrote:
| There is no string comparison here, the subscriptions and
| posting of events is based on the class of the POJO being
| sent.
| ncann wrote:
| Ah ok I guess my confusion is from the fact that there's no
| explicit "string" being passed around with this library,
| but the concept of a payload makes sense.
| void_mint wrote:
| > It does introduce a direct dependency where it was
| indirect before
|
| Which is okay if you've used any amount of DI and
| testability-focused thinking to set up your code.
|
| The event bus pattern works great if you don't know (or
| want to know) what's going to happen to an event once it's
| emitted. If the only thing that happens is a different
| method in your app gets triggered, you should really just
| trigger that method and skip the event bus altogether.
| hota_mazi wrote:
| Introducing the direct dependency is _exactly_ what you
| want to avoid and what event buses provide.
|
| With your approach, you are coupling views and model with
| n*m links, with an event bus, it's just n+m. Much easier to
| decouple your logic.
|
| I do agree with you that I'm not a fan of the loss of
| static typing that event buses usually introduce.
| an_opabinia wrote:
| > Introducing the direct dependency is exactly what you
| want to avoid
|
| Why? It's a GUI application on Android. Everything that
| listens to this event bus is a direct dependency
| configured at build time. Every modern optimization, like
| ahead of time compilation and code stripping, benefits
| from direct dependencies. This is why I used the word
| toxic, and not stupid.
| Quekid5 wrote:
| I think they might be referring to Stringly Typed
| Programming.
| tarkin2 wrote:
| Edit. I shouldn't post on waking up!
| EvilEy3 wrote:
| This has nothing to do with JS.
| happyweasel wrote:
| What's the advantage of this eventbus over good ole' Guava
| Eventbus from google? It's lean and mean .. and more-or-less
| typesafe..
___________________________________________________________________
(page generated 2021-06-19 23:01 UTC)