跪拜 Guibai
← Back to the summary

Android's Architecture Evolution Is a 15-Year War Against the Async Frankenstein

After a few years of work, I realized:

What really tortures people isn't threads.

It's these things:

Eventually, the project slowly becomes:

A giant asynchronous Frankenstein.

And this is actually the real source of complexity in large Android projects.


1. Android didn't have a unified async solution early on

Looking back at Android's development history, you'll find:

Android never lacked async solutions.

What it lacked was:

A unified async solution.


The Thread + Handler Era

At first, everyone wrote code like this:

Thread {
    val data = request()

    handler.post {
        updateUi(data)
    }
}.start()

When first learning, you'd think:

This is so simple.

Later, as the project grew, problems emerged:

Code started to fill with:

if(activity.isFinishing) return
if(fragment.isAdded)
WeakReference<Activity>

The whole codebase became increasingly strange and harder to maintain.


The AsyncTask Era

Later, Google thought:

Directly manipulating Threads is too hard.

So they introduced:

doInBackground()

onPostExecute()

At the time, it felt like a godsend.

But a few years later, everyone realized:

It simply:

Moved the callback to a different place.

It solved none of the problems.

Eventually, it was even officially deprecated.


The Listener Explosion Era

Later, the Android system became more and more complex.

Various Listeners started proliferating wildly:

And then you'd discover:

Every system component has its own way of doing things.

Some:

Callback on the main thread

Some:

Callback on a Binder thread

Some:

Must manually unregister

Some:

Lifecycle ends automatically

And some third-party SDKs:

The docs don't even tell you which thread the callback runs on.

Thus, the entire project entered:

The Era of Everything is a Callback.


2. The real complexity is the collaboration between threads.

Many people think:

Async difficulty = Multi-threading difficulty

That's completely wrong.

Take the most common business flow:

Request user info
↓

Get user ID

↓

Request order list

↓

Write to database

↓

Notify UI update

↓

Auto-cancel when page exits

The hardest part here isn't:

Spawning threads.

It's:

Who manages this entire workflow?

When to cancel?

How to propagate exceptions?

What to do when the page is destroyed?

Who is responsible for switching threads?

Who is responsible for calling back the UI?

These are the truly troublesome parts.


3. The scariest thing is every layer having its own async model

For example, an old project might look like this:

Network layer:

Callback

Database:

RxJava

UI:

LiveData

System events:

Handler

IM:

Listener

Eventually, the business code becomes:

Callback wrapping Rx

Rx converting to LiveData

LiveData converting to Flow

Flow finally being collected

The whole project resembles an asynchronous transfer station.

You have no idea:

Where the data actually came from.

Or when it will come back.


4. RxJava gave everyone hope for the first time

Why was RxJava so popular back then?

Because it told everyone for the first time:

Don't worry about network requests, button clicks, database changes.

They are all streams.

Network requests are streams.

Database changes are streams.

Button clicks are also streams.

Even timers are streams.

Suddenly:

The entire project finally started speaking the same language.

That's also why:

For those few years in Android:

If you didn't know RxJava, you were too embarrassed to send your resume.

5. But RxJava's biggest problem is:

It's ultimately just a library.

In other words:

You can only play if you know Rx.

If you don't?

You basically can't read the project.

Especially code like this:

flatMap()
switchMap()
zip()
combineLatest()
observeOn()
subscribeOn()

Newcomers basically surrender on sight.

And:

The Rx world.

The Callback world.

The Kotlin world.

Often, three systems coexist simultaneously.

The project still slowly becomes:

An asynchronous Frankenstein.

6. The truly powerful thing about Coroutines

The most powerful thing about Kotlin Coroutines is actually:

Async finally became a language capability, and complex async became linear and controllable.

Before:

Async relied on frameworks.

Now:

Async is written directly into the language.

Thus:

val user = api.getUser()

val order = api.getOrder(user.id)

dao.insert(order)

Looks synchronous.

Actually, it's all asynchronous.

Code finally started to look like it was written by humans again.


7. What Flow truly solves is:

Finally, no more converting back and forth everywhere.

Before:

Callback -> Rx -> LiveData -> UI

Now:

Flow -> UI

Network:

Flow<User>

Database:

Flow<List<Article>>

WebSocket:

Flow<Message>

Bluetooth status:

Flow<BluetoothState>

UI State:

StateFlow<UiState>

The entire project finally starts speaking one language.

This is actually much more important than performance optimization.

Because:

What architecture fears most is chaos


8. Why Compose madly embraces Flow

Because the two are from the same family.

Compose:

State changes
↓

UI auto-refreshes

Flow:

Data changes
↓

State auto-updates

So:

val uiState by viewModel.uiState.collectAsState()  // This is an example; use the correct withLifecycle one in production.

9. Android architecture upgrades over the years,

have actually been solving the same problem.

On the surface:

MVVM
MVI
Compose
Flow
StateFlow
UDF

Seems like various new terms keep popping up.

But essentially, they are all doing one thing:

Locking runaway async back into rules.

Because what's truly terrifying in large projects has never been:

Too many features.

It's:

Async out of control.

When does it execute?

When does it cancel?

Who comes back first?

Who comes back later?

Who is responsible for updating the state?

If no one manages these questions.

The project will eventually become:

Whoever changes it takes the blame.

Finally

The evolution of Android architecture over the years.

On the surface, it looks like:

Upgrading APIs
Replacing frameworks
Updating architecture

But underneath, it has always been doing the same thing:

Eliminating the asynchronous Frankenstein.

From Thread to Handler.

From AsyncTask to RxJava.

From RxJava to Coroutine.

From LiveData to Flow.

From XML to Compose.

Technology keeps changing.

But the battle between Android engineers and async, for over a decade, has finally reached its end with coroutines.

References

From Callback to Coroutines: The Evolution of Android Async Concurrency Solutions

The word "Structured" essentially means—turning chaotic things into organized, rule-bound, bounded things

From "Caller Walking on Thin Ice" to "Natural Semantics of the Interface": Insights from Room/DataStore/Retrofit

Why Modern Android Officially Recommends Repositories Expose suspend fun Instead of launch Internally

# Android Architecture Guide: Data Layer Should Stop Exposing start/stop: Use Flow to Manage Lifecycle

Stop launch(IO): 3 Hidden Anti-patterns of Coroutine Thread Switching

Understanding Android Clean Architecture from Food Delivery: Why the Boss Doesn't Need to Know What Car the Delivery Guy Drives

Getting Started with Android Clean Architecture Using a Small Demo

Modern Android Architecture Doesn't Need an Event Bus

Why I Don't Handle Exceptions Directly in Android ViewModel?

Comments

Top 3 of 5 from juejin.cn, machine-translated. The original thread is authoritative.

向新出发叭 1 likes

The perspective of this article is great; it clearly lays out the changes in async handling during Android's architectural evolution. I've felt this deeply in past projects — back when AsyncTask and Handler were everywhere, callback hell was a genuine nightmare. Later, the LiveData plus ViewModel combination really improved things, and now with coroutines and Flow, code readability and maintainability have taken a qualitative leap. From personal experience, architectural upgrades aren't just about technology choices; more importantly, the team's understanding of async lifecycles needs to keep up, otherwise even the best tools are easy to misuse. Thanks for sharing — just my two cents, for reference.

潜龙勿用之化骨龙

Thanks for the support

年轻的bing

Big bro's articles are always so inspiring [thumbs up]

潜龙勿用之化骨龙

Glad you like it

用户581914407356

Spot-on summary