fix(player): 修复切换频道时复用已释放的视频控制器

This commit is contained in:
Zhou Weidong committed 2026-10-21 20:00:15 +08:00
1 parent 45a53180d9
commit f3ec12e3c3
6 files changed
+229 -6

No files matched your search

+53 -2
View File
@@ -123,6 +123,38 @@ void main() {
await session.close();
});
test(
'should_detach_retiring_player_before_disposal_finishes',
() async {
final disposeStarted = Completer<void>();
final disposeGate = Completer<void>();
final factory = FakeFactory(
firstDisposeStarted: disposeStarted,
firstDisposeGate: disposeGate,
);
final session = PlaybackSession(
episodes: episodes,
factory: factory.create,
canPlay: (episode) => episode.isFree,
);
await session.select(0);
final retiring = factory.created.single;
final switching = session.select(1);
await disposeStarted.future;
expect(retiring.disposed, isFalse);
expect(session.active, isNull);
expect(session.preparedPlayerAt(0), isNull);
disposeGate.complete();
await switching;
expect(retiring.disposed, isTrue);
expect(session.active, isA<FakePlayer>());
await session.close();
},
);
test('should_keep_user_pause_when_reselecting_the_same_episode', () async {
final factory = FakeFactory();
final session = PlaybackSession(
@@ -164,8 +196,14 @@ void main() {
}
class FakeFactory {
FakeFactory({this.holdFirstInitialize = false});
FakeFactory({
this.holdFirstInitialize = false,
this.firstDisposeStarted,
this.firstDisposeGate,
});
final bool holdFirstInitialize;
final Completer<void>? firstDisposeStarted;
final Completer<void>? firstDisposeGate;
final List<FakePlayer> created = [];
final Completer<void> firstCreated = Completer<void>();
int live = 0;
@@ -176,6 +214,8 @@ class FakeFactory {
if (live > peakLive) peakLive = live;
final player = FakePlayer(
holdInitialize: holdFirstInitialize && created.isEmpty,
disposeStarted: created.isEmpty ? firstDisposeStarted : null,
disposeGate: created.isEmpty ? firstDisposeGate : null,
onDispose: () => live--,
);
created.add(player);
@@ -185,9 +225,16 @@ class FakeFactory {
}
class FakePlayer implements PlayerPort {
FakePlayer({required this.holdInitialize, required this.onDispose});
FakePlayer({
required this.holdInitialize,
required this.onDispose,
this.disposeStarted,
this.disposeGate,
});
final bool holdInitialize;
final void Function() onDispose;
final Completer<void>? disposeStarted;
final Completer<void>? disposeGate;
final Completer<void> _initializeGate = Completer<void>();
bool initialized = false;
bool disposed = false;
@@ -211,6 +258,10 @@ class FakePlayer implements PlayerPort {
@override
Future<void> dispose() async {
if (disposed) return;
if (disposeStarted != null && !disposeStarted!.isCompleted) {
disposeStarted!.complete();
}
await disposeGate?.future;
disposed = true;
onDispose();
}