RxJava中.defer()的适用场景(除System.currentTimeMillis())及使用疑问
Great questions! Let's tackle them one by one to clear up the confusion.
Why Your Code Reuses the Player ID Without defer()
First, let's fix a small typo in your original code (using playerId instead of undefined id for clarity):
Single<Player> createPlayerSingle() { Integer playerId = Random.nextInteger(); return playersDao.createSingle(playerId); }
Here's the core issue: when you call createPlayerSingle(), the line Integer playerId = Random.nextInteger(); runs immediately—before anyone even subscribes to the Single. The Single returned by playersDao.createSingle(playerId) is permanently bound to that one generated ID.
So if you reuse the same Single instance across multiple subscriptions:
Single<Player> playerStream = createPlayerSingle(); playerStream.subscribe(player -> System.out.println("Player ID: " + player.getId())); // Uses ID 123 playerStream.subscribe(player -> System.out.println("Player ID: " + player.getId())); // Still uses ID 123
Both subscriptions get the same ID because the ID was generated once when you called createPlayerSingle(), not when each subscription happens.
When you wrap the logic in Single.defer():
Single<Player> createPlayerSingle() { return Single.defer(() -> { Integer playerId = Random.nextInteger(); return playersDao.createSingle(playerId); }); }
The lambda inside defer() executes every time a new subscription is made. That means each subscription triggers a fresh call to Random.nextInteger(), so you get a unique ID for every subscriber.
Other Practical Use Cases for Single.defer()
Beyond capturing dynamic values like System.currentTimeMillis(), defer() is essential whenever you need logic to run per subscription instead of once during stream creation. Here are common scenarios:
- Lazy resource initialization: Open database connections, files, or network sockets only when someone subscribes. This prevents wasting resources if the stream is never used.
Single<Connection> getDatabaseConnection() { return Single.defer(() -> { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db", "user", "pass"); return Single.just(conn); }); } - Dynamic dependency resolution: Fetch services or context objects that might change over time (like a current user session).
defer()ensures you get the latest instance when subscribing. - Avoiding premature side effects: If your code has side effects (like logging, making API calls, or updating counters), wrapping them in
defer()ensures those effects happen every time someone subscribes, not just once when the stream is created. - Reading mutable state: If you have a variable updated externally,
defer()lets you read its current value at subscription time, rather than the value it had when the stream was initialized.
Content of the question originates from Stack Exchange, asked by Alex Kokorin

