Hacker News new | past | comments | ask | show | jobs | submit login
EventBus 3.1 with plain Java support (greenrobot.org)
52 points by qdghmkop on June 19, 2021 | hide | past | favorite | 34 comments



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.


> 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?


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.


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.


> ... like they are on Fortnite or Roblox for Android, for example

Go look up dead reckoning, those types of games are built to handle 100-250ms of latency which is way outside a frame/tick.


Err network games deal with latencies in the hundreds of milliseconds.


> 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.

It's very kind of you to commit to building a cell tower in the grounds of the rural hotel I was at yesterday.


Will also need a lot of unit tests that would normally be unnecessary.


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?


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.


> 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.


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.


> 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.


Calling by strings is not decoupling. You just fooled the type system. Or yourself, if you want.


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.


There is no string comparison here, the subscriptions and posting of events is based on the class of the POJO being sent.


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.


> 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


I think they might be referring to Stringly Typed Programming.


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.


> 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://grep.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."


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


What do you mean by "you own"? I can shine a wide spectrum of things I can make commits to vs not at all


What's the advantage of this eventbus over good ole' Guava Eventbus from google? It's lean and mean .. and more-or-less typesafe..



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


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.


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.


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.


Say what you want about EventBus but at least it's simple, non-intrusive and straightforward. You annotate your listener method, register your listener, post an event and boom done. In contrast I've heard war stories about RxJava, it's complex with a high learning curve, it's intrusive and spreads like cancer in the codebase, and overall much harder to debug. The downsides honestly cancel out any benefits it may have.


If you are working in a shop that considers EventBus a best practice, my advice to you is to level up your peers if you can, or investigate and advocate for new and better architectural tools like coroutines if you cannot.

I expect that you will not be able to successfully make the case to migrate off of EventBus, because it will be difficult to restore the dependency tree and extricate it from the codebase.


Does anyone have real life experiences and thoughts on how EventBus compares to Guava?


Edit. I shouldn’t post on waking up!


This has nothing to do with JS.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: