Jesse A. Green / notebook

RxJava 4 Admin & Architecture Guide

Generated June 2026 from ReactiveX/RxJava at 6beb0e3

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is for engineers, architects, and contributors who need to understand RxJava 4 from the inside: how the library is structured, how operators are implemented, how schedulers and backpressure work under the hood, how to build and customize it, and where its current edges are. For day-to-day usage (installing, creating flows, operators), see the companion User Guide.

Note

RxJava 4 (io.reactivex.rxjava4, build version 4.0.0-SNAPSHOT) is a forward-looking rewrite of RxJava targeting Java 26 and java.util.concurrent.Flow, adding virtual-thread-native APIs. Where this guide describes APIs marked "in development" in the source, it says so explicitly.


1. Executive Summary

RxJava is the JVM implementation of ReactiveX — a library for composing asynchronous, event-based programs with observable sequences. It implements the observer pattern, extends it with hundreds of composition operators, and abstracts away threading, synchronization, and concurrent data structures.

Attribute Value
Project ReactiveX/RxJava
Version line 4.x (4.0.0-SNAPSHOT at this commit)
Package root io.reactivex.rxjava4
Maven coordinates io.reactivex.rxjava4:rxjava
Language / target Java 26 (build toolchain JDK 26)
Runtime dependencies None (single self-contained JAR)
Build system Gradle (java-library)
License Apache License 2.0
Distribution Maven Central (releases) + Sonatype snapshots
Packaging Plain JAR + JPMS module-info + OSGi (bnd) metadata, multi-release
Lead maintainer David Karnok (akarnokd)

What's new in 4.x at a glance:

  • Native Java 26 implementation built on java.util.concurrent.Flow.
  • Virtual thread support: Schedulers.virtual(), Flowable.virtualCreate(...) / virtualTransform(...), and the new Streamable<T> / Streamer<T> async-enumerable types.
  • record-based configuration types (FlatMapConfig, GenericConfig) to reduce operator overload bloat.
  • Java Cleaner API used for resource-leak detection and adaptive cleanup.
  • Schedulers.io() deprecated in favor of cached() / virtual().
  • System properties renamed to the rx4.* prefix.
  • No third-party runtime dependency (Reactive Streams is now satisfied via java.util.concurrent.Flow).

2. Architecture

2.1 The reactive type system

RxJava's design follows the Reactive Streams specification: every reactive type has a class (with factory + operator methods), a source interface, and a consumer interface.

Cardinality Class Source interface Consumer
0..N, backpressured Flowable Publisher (Reactive Streams / Flow.Publisher) Subscriber
0..N, unbounded Observable ObservableSource Observer
1 item or error Single SingleSource SingleObserver
0..1 item or error Maybe MaybeSource MaybeObserver
0 items (completion or error) Completable CompletableSource CompletableObserver

The base classes are declared as abstract; for example:

public abstract non-sealed class Flowable<@NonNull T>
        implements Publisher<T>, FlowableDocBasic<T> { ... }

Flowable is a Reactive Streams Publisher, making it interoperable with any spec-compliant library and with java.util.concurrent.Flow. Observable implements the RxJava-specific ObservableSource<T>.

Note

The ...Source interfaces (ObservableSource, SingleSource, etc.) are RxJava-specific and not part of the Reactive Streams spec — there is no FlowableSource because Reactive Streams already provides Publisher. Single, Maybe, and Completable have no equivalent in Rx.NET.

2.2 Signal contracts

Every flow obeys a strict signal protocol. Violating it produces a ProtocolViolationException.

Type Contract
Flowable / Observable onSubscribe onNext* (onError | onComplete)?
Single onSubscribe (onError | onSuccess)?
Maybe onSubscribe (onSuccess | onError | onComplete)?
Completable onSubscribe (onError | onComplete)?

Two lifecycle/cancellation abstractions exist:

  • Subscription — the Reactive Streams handle on a Flowable. Carries request(long n) (interactive flow control) and cancel().
  • Disposable — the lifecycle handle for non-backpressured types (Observable, Single, Maybe, Completable). Carries dispose() / isDisposed().

2.3 How operators are implemented

Unlike RxJava 1.x (which wrapped an OnSubscribe callback and used lift), the base classes are abstract, stateless, and do not wrap a callback — saving an allocation per operator at assembly time. Operators are concrete subclasses of the base type.

The subscribe(...) method is final and delegates to a protected subscribeActual(...) that each operator implements:

@Override
public final void subscribe(Subscriber<? super T> s) {
    subscribeActual(hook.onSubscribe(s));   // assembly/subscribe hook applied here
}

protected abstract void subscribeActual(Subscriber<? super T> s);

A representative operator — map:

public final class FlowableMap<T, R> extends Flowable<R> {
    final Flowable<? extends T> source;
    final Function<? super T, ? extends R> mapper;

    public FlowableMap(Flowable<? extends T> source, Function<? super T, ? extends R> mapper) {
        this.source = source;
        this.mapper = mapper;
    }

    @Override
    protected void subscribeActual(Subscriber<? super R> subscriber) {
        source.subscribe(new FlowableMapSubscriber<>(subscriber, mapper));
    }

    static final class FlowableMapSubscriber<T, R> implements Subscriber<T>, Subscription { /* ... */ }
}

Each public operator method wraps construction in the assembly hook so plugins can observe/replace the assembled flow:

public final Flowable<R> map(Function<? super T, ? extends R> mapper) {
    return RxJavaPlugins.onAssembly(new FlowableMap<>(this, mapper));
}

Because Java lacks extension methods, you "add" operators externally via lift(operator) (operate at the consumer level) or compose(transformer) (operate at the flow level). Other Reactive Streams libraries are pulled in via Flowable.fromPublisher(Publisher).

2.4 Operator lifecycle rules

An operator must:

  1. Propagate subscribe upstream toward the producer.
  2. Obey the RxJava contract — serialize all events; treat onError/onComplete as terminal.
  3. Clean up resources by watching onError, onComplete, and cancel/dispose.
  4. Propagate cancel/dispose upstream.
  5. For Flowable, negotiate and propagate request(n).

The terminal subscribe does not call cancel/dispose after receiving onComplete/onError — terminal events already release the chain.

2.5 Operator fusion (performance internals)

RxJava optimizes the declarative chain through two levels of fusion. This is internal machinery, but understanding it helps when profiling or implementing custom operators.

Macro-fusion — assembly-time rewrites of operator pairs a().b():

  • b recognizes a and elides itself (empty().flatMap() → no-op).
  • b substitutes a more efficient operator (just().subscribeOn() → just().observeOn() / ScalarScheduledPublisher).
  • Adjacent same operators merge parameters (filter(p1).filter(p2) → filter(p1 && p2)).

Sources can opt in by implementing java.util.Callable (value extracted at/after subscription) or RxJava's ScalarCallable (value safely extractable at assembly time). A null from a Callable/ScalarCallable is treated as an empty source (Reactive Streams forbids null values).

Micro-fusion — reuses internal structures across operator pairs:

  • ConditionalSubscriber adds boolean tryOnNext(T value), letting operators like filter avoid wasteful request(1) calls when an item is dropped.
  • Queue-fusion (QueueSubscription / QueueFuseable) lets adjacent operators share one queue, attaching logic at poll(). It negotiates a mode via requestFusion(int) with bit flags NONE=0, SYNC=1, ASYNC=2, ANY=SYNC|ASYNC, BOUNDARY=4. In SYNC mode the request/onError rail is bypassed; in ASYNC mode availability is signalled via onNext(null).

Important

A consequence of queue-fusion: an intermediate operator must not forward an upstream Subscription directly to downstream.onSubscribe(...), or the fused chain may skip it. Operators implement both Subscriber and Subscription on themselves and delegate request/cancel upstream while calling child.onSubscribe(this).

2.6 The plugin / hook system (RxJavaPlugins)

io.reactivex.rxjava4.plugins.RxJavaPlugins is the global extension point. It exposes:

  • Assembly hooks — setOnFlowableAssembly, setOnObservableAssembly, setOnSingleAssembly, setOnMaybeAssembly, setOnCompletableAssembly, setOnConnectableFlowableAssembly. Used for instrumentation, debugging (e.g. assembly-stack tracing), and testing.
  • Scheduler hooks — setInit{Computation,Cached,Single,NewThread,Virtual}SchedulerHandler (one-time, at init) and set{...}SchedulerHandler (runtime override), plus createComputationScheduler(ThreadFactory) etc. for fresh instances.
  • Error handling — setErrorHandler(Consumer<Throwable>) for undeliverable errors; onError(Throwable) is where UndeliverableException/OnErrorNotImplementedException are routed.
  • Schedule hook — setScheduleHandler(Function<Runnable, Runnable>) wraps every task submitted to a scheduler.
  • Safety toggles — setFailOnNonBlockingScheduler(boolean) makes blocking on computation()/single() throw; lockdown() freezes all plugin configuration (call after app init to prevent later tampering); reset() restores defaults.

Tip

In production, configure RxJavaPlugins once during application startup, set a global setErrorHandler(...) as a safety net for undeliverable errors, then call RxJavaPlugins.lockdown() to prevent runtime reconfiguration.

2.7 Configuration records (4.x)

The io.reactivex.rxjava4.core.config package introduces record-based configuration to collapse the combinatorial explosion of operator overloads:

Record Fields Used by
FlatMapConfig boolean delayErrors, int maxConcurrency, int bufferSize flatMap-family operators
GenericConfig boolean delayErrors, int bufferSize, int prefetch prefetch/buffer-tunable operators

Both validate positivity of numeric fields (via ObjectHelper.verifyPositive) and default sizes to Flowable.bufferSize(). They carry convenience constructors so callers can specify only the fields they care about.

// equivalent ways to construct a flatMap config
new FlatMapConfig();                       // no delay, default sizes
new FlatMapConfig(true);                   // delay errors
new FlatMapConfig(8);                      // maxConcurrency = 8
new FlatMapConfig(true, 8, 256);           // fully customized

Note

Source comments note these are records "once value classes are available" — they're a stepping stone toward Project Valhalla value classes.


3. Scheduler Internals

The io.reactivex.rxjava4.schedulers.Schedulers class is a final utility holding lazily-initialized, shared Scheduler singletons (COMPUTATION, CACHED, VIRTUAL, SINGLE, NEW_THREAD, TRAMPOLINE), each created through an init...SchedulerHandler hook and accessed through an on...Scheduler hook.

3.1 Standard scheduler implementations

Factory Backing implementation Notes
computation() ComputationScheduler — pool of single-threaded ScheduledExecutorServices, sized to Runtime.availableProcessors() Default for async operators; do not block on it
cached() CachedScheduler — dynamically growing pool that reuses idle workers New in 4.x; classic blocking-I/O scheduler; can grow unbounded
virtual() DeferredExecutorScheduler over Executors.newVirtualThreadPerTaskExecutor() per worker New in 4.x; for virtual-blocking / many-IO work
single() SingleScheduler — one shared background thread, FIFO Sequential work; do not block on it
newThread() NewThreadScheduler — a new single-threaded executor per unit of work Unbounded thread creation; rarely the right choice
trampoline() TrampolineScheduler (singleton) Queues on the current thread; not overridable via plugins
io() Delegates to cached() Deprecated since 4.0.0

3.2 Wrapping executors

  • from(Executor) — basic wrapper; non-interruptible tasks; timed tasks borrow single() for the delay.
  • from(Executor, interruptibleWorker) — disposing a task interrupts it.
  • from(Executor, interruptibleWorker, fair) — fair=true reschedules between tasks so the underlying executor can interleave other work.
  • fromSupplier(Supplier<Executor>, interruptible, fair) — each worker obtains its own Executor.

If the underlying executor supports ScheduledExecutorService, timed/periodic tasks use it directly; otherwise they fall back to single() for the timing. A RejectedExecutionException from the executor is routed to RxJavaPlugins.onError(...).

3.3 Lifecycle

Schedulers.shutdown() and Schedulers.start() stop/start the standard schedulers (idempotent, thread-safe). Custom schedulers created via RxJavaPlugins.create...Scheduler(ThreadFactory) require a manual Scheduler.shutdown() so the JVM (or a J2EE container) can unload cleanly.

3.4 Scheduler tuning via system properties

Set these before the Schedulers class is first referenced:

Property Type Effect
rx4.computation-threads int Thread count for computation() (default: available CPUs)
rx4.computation-priority int Thread priority for computation()
rx4.cached-keep-alive-time long Keep-alive for cached()/io() workers
rx4.cached-priority int Thread priority for cached()/io()
rx4.cached-scheduled-release boolean true = release workers only after the task finishes (scheduled mode); default false = eager
rx4.newthread-priority int Thread priority for newThread()
rx4.single-priority int Thread priority for single()
rx4.purge-enabled boolean Enable purging of backing thread pools (default true)
rx4.scheduler.use-nanotime boolean Use System.nanoTime() for Scheduler.now(...) instead of currentTimeMillis()
rx4.buffer-size int Default internal buffer size (Flowable.bufferSize(); default 128)

Warning

The rx4.* prefix is current as of this commit, but a rename to rxjava4.* is tracked in issue #8081. Pin to a specific release and re-check property names when upgrading within 4.x.

3.5 The TestScheduler

TestScheduler (in the schedulers package) is a non-thread-safe scheduler with a manually advanced virtual clock — essential for deterministic testing of time-based operators (debounce, interval, timeout). It queues tasks in a PriorityBlockingQueue ordered by virtual time, and you advance time explicitly. Companion testing tools are TestSubscriber and TestObserver.


4. Backpressure Internals

Backpressure is interactive flow control: a downstream Subscriber calls Subscription.request(n) to grant the upstream permission to emit up to n items. Only Flowable participates.

  • BackpressureStrategy ({MISSING, ERROR, BUFFER, DROP, LATEST}) — chosen when converting an Observable to a Flowable via toFlowable(strategy).
  • onBackpressureBuffer / onBackpressureDrop / onBackpressureLatest — applied to an existing Flowable for finer control. onBackpressureBuffer has bounded-capacity overloads that take a BackpressureOverflowStrategy ({ERROR, DROP_OLDEST, DROP_LATEST}).
  • Default buffer/prefetch size is 128, overridable via rx4.buffer-size and readable via Flowable.bufferSize().

When backpressure is violated, RxJava raises MissingBackpressureException (downstream not requesting) or QueueOverflowException (concurrent unsynchronized onNext, or a producer ignoring backpressure entirely).


5. Repository Structure

RxJava/
├── build.gradle              Gradle build (java-library, checkstyle, jmh, license, bnd/OSGi, maven-publish)
├── settings.gradle           rootProject.name = 'rxjava'
├── gradle.properties         group/version/POM metadata (group=io.reactivex.rxjava4)
├── gradlew / gradlew.bat     Gradle wrapper
├── DESIGN.md                 Library design: terminology, contracts, fusion, creation model
├── CONTRIBUTING.md           Contribution + AI-contribution policy, license header
├── SECURITY.md               Private vulnerability disclosure policy
├── README.md                 Project overview + usage guide
├── codecov.yml               Coverage gate (project & patch target 95%)
├── push_javadoc.sh           CI script: publishes Javadoc to the gh-pages branch
├── pmd.xml                   PMD ruleset
├── config/
│   ├── checkstyle/           checkstyle.xml + suppressions.xml
│   └── license/              License HEADER templates
├── gradle/                   wrapper + javadoc_cleanup.gradle
└── src/
    ├── main/
    │   ├── java/io/reactivex/rxjava4/
    │   │   ├── core/                Base types: Flowable, Observable, Single, Maybe, Completable,
    │   │   │   │                    Scheduler, Notification, BackpressureStrategy, Streamable,
    │   │   │   │                    Streamer, Virtual{Emitter,Generator,Transformer}, *Emitter, *Source
    │   │   │   ├── core/config/     FlatMapConfig, GenericConfig (record-based config)
    │   │   │   └── core/docs/       Javadoc helper interfaces (e.g. FlowableDocBasic)
    │   │   ├── annotations/         @Beta, @Experimental, @NonNull, @SchedulerSupport, etc.
    │   │   ├── disposables/         Disposable, CompositeDisposable, DisposableContainer
    │   │   ├── exceptions/          CompositeException, MissingBackpressureException, ...
    │   │   ├── functions/           Function, BiFunction, Consumer, Predicate, Supplier, ...
    │   │   ├── flowables/           ConnectableFlowable, GroupedFlowable
    │   │   ├── observables/         ConnectableObservable, GroupedObservable
    │   │   ├── observers/           Public observer base classes
    │   │   ├── subscribers/         Public subscriber base classes (incl. TestSubscriber)
    │   │   ├── parallel/            ParallelFlowable
    │   │   ├── processors/          PublishProcessor, BehaviorProcessor, ...
    │   │   ├── subjects/            PublishSubject, BehaviorSubject, ReplaySubject, ...
    │   │   ├── operators/           Public operator SPI types
    │   │   ├── plugins/             RxJavaPlugins (global hooks)
    │   │   ├── schedulers/          Schedulers, TestScheduler, Timed, SchedulerRunnableIntrospection
    │   │   └── internal/            PRIVATE API — operators/, schedulers/, observers/, fuseable/,
    │   │       │                    queue/, util/, functions/, jdk8/, subscriptions/, ...
    │   │       └── operators/       The actual operator implementations, by base type
    │   ├── module/                  module-info.java (JPMS)
    │   └── resources/META-INF/
    │       └── proguard/rxjava4.pro  R8/ProGuard consumer rules
    ├── test/                        Unit + Reactive Streams TCK tests (JUnit/TestNG)
    └── jmh/                         JMH microbenchmarks

The single largest files are the base-type classes — Flowable.java (~1.2 MB), Observable.java (~980 KB), Maybe.java (~336 KB), Single.java (~305 KB), Completable.java (~180 KB) — because each declares the full operator surface (hundreds of methods and overloads). Flowable alone exposes ~169 instance operators plus dozens of static factories.

Caution

Everything under io.reactivex.rxjava4.internal.* is private API. It is the bulk of the codebase (the actual operator implementations live in internal/operators/{flowable,observable,single,maybe,completable,mixed,parallel}), but it can change at any time and must not be referenced by application code.


6. Build, Test & Release

6.1 Building

git clone git@github.com:ReactiveX/RxJava.git
cd RxJava/
./gradlew build
BUILD SUCCESSFUL

The build runs compilation, checkstyle, the JUnit and TestNG test suites, and JaCoCo coverage.

6.2 Toolchain and Java version

java {
    toolchain { languageVersion = JavaLanguageVersion.of("26") }
    sourceCompatibility = JavaVersion.VERSION_26
    targetCompatibility = JavaVersion.VERSION_26
}

An "Outreach Program" hook lets CI compile against an experimental JDK:

JDK_EXPERIMENTAL=27 ./gradlew build   # use a newer toolchain than the default 26

When JDK_EXPERIMENTAL is set, JaCoCo coverage is skipped (the jacoco plugin is only applied for the baseline build).

6.3 Test stack

Concern Tooling
Unit tests JUnit 4.13.2 + JUnit Jupiter 6.0.3 (useJUnitPlatform())
Secondary runner TestNG 7.12.0 (./gradlew testNG, wired into check)
Mocking Mockito 4.11.0
Spec compliance Reactive Streams TCK 1.0.4 (reactive-streams-tck, reactive-streams-tck-flow)
Test utilities Guava 33.6.0-jre
Coverage JaCoCo, gate at 95% project & patch (codecov.yml)
Benchmarks JMH 1.37 (me.champeau.jmh plugin; ./gradlew jmh -Pjmh=<regex>)

Tests run with maxHeapSize = "1200m" and parallel forks (all CPUs on CI, half locally).

Note

All runtime dependencies above are test-scope only. The shipped artifact has no third-party runtime dependency.

6.4 Packaging

The build produces a single JAR enriched with:

  • JPMS: module-info.java (from src/main/module/), assembled as a multi-release class for Java 9+.
  • OSGi: bnd (biz.aQute.bnd.builder) generates the bundle manifest — Bundle-SymbolicName: io.reactivex.rxjava4.rxjava, exporting io.reactivex.rxjava4.* while hiding io.reactivex.rxjava4.internal.*.
  • R8/ProGuard: META-INF/proguard/rxjava4.pro consumer rules.

6.5 Publishing & CI

  • Releases publish to Maven Central via com.vanniktech.maven.publish; signing (SIGNING_PRIVATE_KEY / SIGNING_PASSWORD) is applied in releaseMode=full. The version is driven by the BUILD_TAG env var (a leading v is stripped).
  • Snapshots publish to the Sonatype snapshots repository.
  • Javadoc is published to the gh-pages branch by push_javadoc.sh (CI uses the JAVADOCS_TOKEN secret; gracefully skips when absent). Javadoc excludes internal, test, perf, and jmh sources and links against the JDK 26 and Reactive Streams APIs.
  • A GitHub Actions Snapshot workflow and OpenSSF Scorecard run on the 4.x branch.

Important

The current development branch is 4.x (commit 6beb0e3). CONTRIBUTING.md still instructs contributors to target 3.x — a stale reference; submit 4.x work against the 4.x branch.


7. Security

Topic Detail
License Apache License 2.0 — Copyright (c) 2016-present, RxJava Contributors
Vulnerability reporting Privately, via GitHub Security Advisories (/security/advisories/new). Do not open public issues. Allow ≥90 days before public disclosure.
Supply chain No third-party runtime dependencies → minimal transitive attack surface
Posture signals OpenSSF Scorecard + OpenSSF Best Practices badges on the README
Maintenance Volunteer-maintained on a reasonable-effort basis

7.1 Operational security considerations for integrators

  • Error leakage. Unhandled errors flow to RxJavaPlugins.onError(...) and print to the thread's UncaughtExceptionHandler. Set a global setErrorHandler(...) to control where stack traces (which may contain sensitive data) are logged.
  • Resource exhaustion. cached() and newThread() can spawn an unbounded number of threads, and BackpressureStrategy.BUFFER / onBackpressureBuffer() can buffer unboundedly. Under adversarial or bursty load this is a denial-of-service vector. Bound concurrency (maxConcurrency), use bounded buffers with a BackpressureOverflowStrategy, and apply timeout(...).
  • Plugin tampering. RxJavaPlugins is process-global. Call RxJavaPlugins.lockdown() after startup so no library or code path can later swap schedulers or error handlers.
  • Leak detection. RxJava 4 uses the Java Cleaner API for resource-leak detection and adaptive cleanup — useful in long-running services to surface undisposed workers.

8. Operations & Tuning

8.1 Production checklist

  • [ ] Configure RxJavaPlugins (error handler, scheduler overrides) at startup, then lockdown().
  • [ ] Set rx4.computation-threads if the default (CPU count) doesn't match your workload.
  • [ ] Route blocking I/O to cached() or virtual(); keep computation()/single() non-blocking.
  • [ ] Consider RxJavaPlugins.setFailOnNonBlockingScheduler(true) in staging to catch accidental blocking.
  • [ ] Bound every fan-out (flatMap maxConcurrency) and every buffer.
  • [ ] Ensure lifecycle-scoped subscriptions are disposed (use CompositeDisposable).
  • [ ] Add -dontwarn java.util.concurrent.Flow* for plain ProGuard builds.

8.2 Diagnostics

Need Tool
Deterministic time-based tests TestScheduler + TestSubscriber/TestObserver
Trace where a flow was assembled Assembly hooks via RxJavaPlugins.setOn*Assembly(...)
Wrap/observe every scheduled task RxJavaPlugins.setScheduleHandler(...)
Unwrap a wrapped scheduled Runnable SchedulerRunnableIntrospection.getWrappedRunnable()
Centralize undeliverable errors RxJavaPlugins.setErrorHandler(...)

8.3 Performance notes

  • Operator fusion (macro + micro) removes intermediate allocations and queue copies automatically; you benefit without doing anything, but be aware that custom operators must implement the fusion interfaces correctly to participate.
  • The default buffer size (128) governs prefetch granularity; tune via rx4.buffer-size or per-operator bufferSize/prefetch arguments (or a GenericConfig).
  • JMH benchmarks live under src/jmh — use them to validate the performance impact of changes (includeTests = false, runs with -Djmh.ignoreLock=true).

9. Versioning & Stability

RxJava follows semantic-ish versioning:

  • Minor (4.x) — non-trivial new functionality or behavior-affecting enhancements (e.g. adding backpressure to an operator). Backwards-compatible but may differ in edge cases.
  • Patch (4.x.y) — bug fixes, trivial additions (method overloads), and new @Beta/@Experimental APIs.

Stability annotations (io.reactivex.rxjava4.annotations):

Annotation Guarantee
@Beta May change or be removed at any time. Don't expose through a library API without shading.
@Experimental Will almost certainly change; do not use in production.
@Deprecated Supported until the next major release; migrate away.
(internal.* package) Private; no guarantees whatsoever.

Cross-line support: bug fixes are applied to both 3.x and 4.x where possible; new features go only to 4.x. RxJava 3.x will be supported for at least one year after the 4.x official release. RxJava 1.x (EOL 2018-03-31) and 2.x (EOL 2021-02-28) are end-of-life.


10. Improvements & Known Edges

Observed from the source and the project's own issue tracker at this commit.

10.1 In-development 4.x APIs

Several new types carry TODO proper docs / FIXME native implementation markers in their Javadoc and bodies:

  • Streamable / Streamer — documented as "TODO proper docs"; factory methods like Streamable.create(...) are currently implemented by delegating to Flowable.virtualCreate(...).toStreamable() rather than a native implementation (// FIXME native implementation). The default stream() notes it should use "a practically no-op disposable container instead".
  • Schedulers.virtual() Javadoc is a stub ("TODO explain all the rest").

These APIs work but are not finalized — pin to a release and re-verify behavior across patch versions if you depend on them.

10.2 Documentation / process debt

  • CONTRIBUTING.md still says to target the 3.x branch; current development is on 4.x.
  • README links to wiki pages still tracked for 4.x updates (issue #6132: "Update wiki to reflect 4.x").
  • Marble-diagram coverage for Flowable is tracked as incomplete (issue #5813).
  • System-property rename rx4.* → rxjava4.* is pending (issue #8081).

10.3 Community-reported issues (sample)

Issue Summary
#6958 Recursive concat can cause StackOverflowError
#7100 Flowable#groupBy race leading to a backpressure issue
#6931 parallel performs poorly at parallelism 10+
#7912 Make cache/replay operators more GC-friendly
#7548 Feature request: Observable.usingWhen() for Publisher-generated resources
#7422 Feature request: unfold construct for Flowable/Observable
#8054 Javadoc self-check to expand {@inheritDoc} usage
#8079 Convert JUnit 4 tests to JUnit 6

Note

Several open issues reference bug classes the 4.x rewrite explicitly targets — the README lists resolving anomalies in groupBy, window, and concat among its goals.


11. Quick Reference

11.1 Base types

Type Cardinality Backpressure Source iface Consumer
Flowable<T> 0..N yes Publisher Subscriber
Observable<T> 0..N no ObservableSource Observer
Single<T> 1 / error n/a SingleSource SingleObserver
Maybe<T> 0..1 / error n/a MaybeSource MaybeObserver
Completable 0 / error n/a CompletableSource CompletableObserver

11.2 Schedulers

Factory Use 4.x status
computation() CPU-bound stable
cached() blocking I/O new
virtual() virtual-thread blocking new
single() sequential stable
newThread() thread-per-task stable
trampoline() current-thread FIFO stable
io() → cached() deprecated
from(Executor, …) / fromSupplier(…) wrap your own executor stable

11.3 Key system properties

rx4.computation-threads, rx4.cached-keep-alive-time, rx4.cached-scheduled-release, rx4.scheduler.use-nanotime, rx4.purge-enabled, rx4.buffer-size (default 128).

11.4 Exception types

MissingBackpressureException, QueueOverflowException, OnErrorNotImplementedException, UndeliverableException, CompositeException, ProtocolViolationException; utilities Exceptions.propagate(...) / Exceptions.throwIfFatal(...).

11.5 New 4.x APIs

Schedulers.virtual() · Flowable.virtualCreate(VirtualGenerator[, Scheduler|ExecutorService]) · Flowable.virtualTransform(VirtualTransformer[, …]) · Flowable.toStreamable([ExecutorService]) · Streamable<T> / Streamer<T> · VirtualEmitter / VirtualGenerator / VirtualTransformer · FlatMapConfig / GenericConfig.

11.6 Resources

  • Javadoc: http://reactivex.io/RxJava/4.x/javadoc/ (snapshot: .../snapshot/)
  • Wiki: https://github.com/ReactiveX/RxJava/wiki
  • Issues: https://github.com/ReactiveX/RxJava/issues
  • Design doc: DESIGN.md in the repository root