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 version4.0.0-SNAPSHOT) is a forward-looking rewrite of RxJava targeting Java 26 andjava.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 newStreamable<T>/Streamer<T>async-enumerable types. record-based configuration types (FlatMapConfig,GenericConfig) to reduce operator overload bloat.- Java
CleanerAPI used for resource-leak detection and adaptive cleanup. Schedulers.io()deprecated in favor ofcached()/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
...Sourceinterfaces (ObservableSource,SingleSource, etc.) are RxJava-specific and not part of the Reactive Streams spec — there is noFlowableSourcebecause Reactive Streams already providesPublisher.Single,Maybe, andCompletablehave 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 aFlowable. Carriesrequest(long n)(interactive flow control) andcancel().Disposable— the lifecycle handle for non-backpressured types (Observable,Single,Maybe,Completable). Carriesdispose()/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:
- Propagate
subscribeupstream toward the producer. - Obey the RxJava contract — serialize all events; treat
onError/onCompleteas terminal. - Clean up resources by watching
onError,onComplete, andcancel/dispose. - Propagate
cancel/disposeupstream. - For
Flowable, negotiate and propagaterequest(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():
brecognizesaand elides itself (empty().flatMap()→ no-op).bsubstitutes 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:
ConditionalSubscriberaddsboolean tryOnNext(T value), letting operators likefilteravoid wastefulrequest(1)calls when an item is dropped.- Queue-fusion (
QueueSubscription/QueueFuseable) lets adjacent operators share one queue, attaching logic atpoll(). It negotiates a mode viarequestFusion(int)with bit flagsNONE=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 viaonNext(null).
Important
A consequence of queue-fusion: an intermediate operator must not forward an upstream
Subscriptiondirectly todownstream.onSubscribe(...), or the fused chain may skip it. Operators implement bothSubscriberandSubscriptionon themselves and delegaterequest/cancelupstream while callingchild.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) andset{...}SchedulerHandler(runtime override), pluscreateComputationScheduler(ThreadFactory)etc. for fresh instances. - Error handling —
setErrorHandler(Consumer<Throwable>)for undeliverable errors;onError(Throwable)is whereUndeliverableException/OnErrorNotImplementedExceptionare routed. - Schedule hook —
setScheduleHandler(Function<Runnable, Runnable>)wraps every task submitted to a scheduler. - Safety toggles —
setFailOnNonBlockingScheduler(boolean)makes blocking oncomputation()/single()throw;lockdown()freezes all plugin configuration (call after app init to prevent later tampering);reset()restores defaults.
Tip
In production, configure
RxJavaPluginsonce during application startup, set a globalsetErrorHandler(...)as a safety net for undeliverable errors, then callRxJavaPlugins.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 borrowsingle()for the delay.from(Executor, interruptibleWorker)— disposing a task interrupts it.from(Executor, interruptibleWorker, fair)—fair=truereschedules between tasks so the underlying executor can interleave other work.fromSupplier(Supplier<Executor>, interruptible, fair)— each worker obtains its ownExecutor.
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 torxjava4.*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 anObservableto aFlowableviatoFlowable(strategy).onBackpressureBuffer / onBackpressureDrop / onBackpressureLatest— applied to an existingFlowablefor finer control.onBackpressureBufferhas bounded-capacity overloads that take aBackpressureOverflowStrategy({ERROR, DROP_OLDEST, DROP_LATEST}).- Default buffer/prefetch size is
128, overridable viarx4.buffer-sizeand readable viaFlowable.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 ininternal/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(fromsrc/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, exportingio.reactivex.rxjava4.*while hidingio.reactivex.rxjava4.internal.*. - R8/ProGuard:
META-INF/proguard/rxjava4.proconsumer rules.
6.5 Publishing & CI
- Releases publish to Maven Central via
com.vanniktech.maven.publish; signing (SIGNING_PRIVATE_KEY/SIGNING_PASSWORD) is applied inreleaseMode=full. The version is driven by theBUILD_TAGenv var (a leadingvis stripped). - Snapshots publish to the Sonatype snapshots repository.
- Javadoc is published to the
gh-pagesbranch bypush_javadoc.sh(CI uses theJAVADOCS_TOKENsecret; gracefully skips when absent). Javadoc excludesinternal,test,perf, andjmhsources and links against the JDK 26 and Reactive Streams APIs. - A GitHub Actions Snapshot workflow and OpenSSF Scorecard run on the
4.xbranch.
Important
The current development branch is
4.x(commit6beb0e3).CONTRIBUTING.mdstill instructs contributors to target3.x— a stale reference; submit 4.x work against the4.xbranch.
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'sUncaughtExceptionHandler. Set a globalsetErrorHandler(...)to control where stack traces (which may contain sensitive data) are logged. - Resource exhaustion.
cached()andnewThread()can spawn an unbounded number of threads, andBackpressureStrategy.BUFFER/onBackpressureBuffer()can buffer unboundedly. Under adversarial or bursty load this is a denial-of-service vector. Bound concurrency (maxConcurrency), use bounded buffers with aBackpressureOverflowStrategy, and applytimeout(...). - Plugin tampering.
RxJavaPluginsis process-global. CallRxJavaPlugins.lockdown()after startup so no library or code path can later swap schedulers or error handlers. - Leak detection. RxJava 4 uses the Java
CleanerAPI 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, thenlockdown(). - [ ] Set
rx4.computation-threadsif the default (CPU count) doesn't match your workload. - [ ] Route blocking I/O to
cached()orvirtual(); keepcomputation()/single()non-blocking. - [ ] Consider
RxJavaPlugins.setFailOnNonBlockingScheduler(true)in staging to catch accidental blocking. - [ ] Bound every fan-out (
flatMapmaxConcurrency) 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 viarx4.buffer-sizeor per-operatorbufferSize/prefetcharguments (or aGenericConfig). - 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/@ExperimentalAPIs.
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 likeStreamable.create(...)are currently implemented by delegating toFlowable.virtualCreate(...).toStreamable()rather than a native implementation (// FIXME native implementation). The defaultstream()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.mdstill says to target the3.xbranch; current development is on4.x.- README links to wiki pages still tracked for 4.x updates (issue #6132: "Update wiki to reflect 4.x").
- Marble-diagram coverage for
Flowableis 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, andconcatamong 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.mdin the repository root