All Products
Search
Document Center

ApsaraVideo VOD:Strategy system

Last Updated:Sep 09, 2026

The strategy system is a core architectural design of AliPlayerKit. It uses an event-driven mechanism to encapsulate player monitoring, analysis, and optimization logic into independent strategy components. This approach decouples the player's business logic, making it reusable and easy to extend.

Built-in strategies

The system includes the following recommended strategies out of the box:

Strategy name

Class name

Description

Optional callbacks

First frame statistics

FirstFrameStrategy

Calculates the time taken from prepare to prepared and from prepare to rendered.

onPrepared, onFirstFrame

Stutter detection

StutterDetectStrategy

Monitors playback and loading states to calculate the number of stutters, total stutter duration, and stutter rate.

onStutterDetected

Note

Built-in strategies require no configuration and are enabled automatically after the player is created. To retrieve data from a strategy, register a new instance of it with the desired callbacks.

Usage

Using default strategies

Built-in strategies run automatically without any configuration:

final data = AliPlayerWidgetData(
  videoSource: videoSource,
);
controller.configure(data);

Replacing a built-in strategy

To retrieve runtime data, replace a default built-in strategy by registering a new instance with the same name using the register() method. This allows you to provide callbacks:

controller.strategyManager
  ..register(FirstFrameStrategy(
    onPrepared: (cost) {
      print('Time to prepare: ${cost.inMilliseconds}ms');
    },
    onFirstFrame: (cost) {
      print('Time to first frame: ${cost.inMilliseconds}ms');
    },
  ))
  ..register(StutterDetectStrategy(
    onStutterDetected: (info) {
      print('Stutters: ${info.stutterCount}, Stutter Rate: ${(info.stutterRate * 100).toStringAsFixed(1)}%');
    },
  ));
Note

Registering a strategy with an existing name automatically replaces the previous instance. You do not need to remove it manually.

Registering a custom strategy

Register a custom strategy instance with a unique name using register() to extend the player's behavior:

controller.strategyManager.register(ResumePlayStrategy(
  onPositionSaved: (position) {
    print('Position saved: ${position.inSeconds}s');
  },
));
Note

To learn how to develop a custom strategy, see the Custom strategy development section below.

Managing strategies

Use strategyManager to remove strategies at runtime:

// Remove a strategy by name
controller.strategyManager.unregister('MyCustomStrategy');

// Remove all strategies
controller.strategyManager.clear();

Custom strategy development

Extending BasePlayerStrategy

Create a custom strategy by extending BasePlayerStrategy. The framework automatically manages the listener lifecycle:

class MyCustomStrategy extends BasePlayerStrategy {
  @override
  String get name => 'MyCustomStrategy';

  @override
  void onStart(AliPlayerWidgetController controller) {
    // Subscribe to notifiers with listen(). The framework automatically cleans them up in onStop.
    listen<int>(controller.playStateNotifier, (state) {
      // Handle playback state changes
    });

    listen<Duration>(controller.currentPositionNotifier, (position) {
      // Handle playback position changes
    });
  }

  @override
  void onReset() {
    // Reset internal state when the video source changes (the strategy remains active)
  }
}

Lifecycle methods

Method

Trigger

Responsibility

onStart

When the strategy starts.

Subscribe to state signals and initialize internal state.

onStop

When the strategy stops (for example, when the controller is destroyed).

Release resources. BasePlayerStrategy automatically cleans up listeners.

onReset

When the video source changes.

Reset internal counters or timestamps. The strategy remains active.

Callback safety mechanism

Strategies typically use callbacks to report data externally, for example, to notify the UI of updates. When a strategy is stopped, the associated UI might have already been disposed. To prevent potential issues, the framework provides two layers of protection:

Mechanism

Scope

Description

listen() built-in guard

Notifier-driven callbacks

After the strategy stops, the listener no longer triggers onChanged.

notify() convenience method

Active callback in onStop()

Safely triggers external callbacks and automatically skips them if the strategy has stopped.

class ResumePlayStrategy extends BasePlayerStrategy {
  final void Function(Duration)? onPositionSaved;

  @override
  void onStop() {
    super.onStop();
    _savePosition();
  }

  void _savePosition() {
    // Persistence always runs (for data safety)
    storage.save(position);

    // Safely trigger the external callback with notify() (skipped if stopped)
    notify(onPositionSaved, position);
  }
}
Note

Design Principle: Persistence (for data safety) and notification (for UI updates) are separate concerns. You can perform persistence operations in onStop(), but external notifications must be guarded by the notify() method.

Data sources and state signals

A strategy is essentially the basic organizational unit of business logic. Because it involves business logic, it inevitably involves data acquisition and processing. A strategy obtains a complete reference to the controller in onStart and can obtain the required data through various methods.

Observable state signals

Strategies subscribe to ValueNotifier objects on the controller by using the listen() method:

Notifier

Type

Description

prepareNotifier

int

Event for the prepare() call.

isPreparedNotifier

bool

The data source is prepared.

isRenderedNotifier

bool

The first frame is rendered.

playStateNotifier

int

Playback state change.

currentPositionNotifier

Duration

Current playback position.

totalDurationNotifier

Duration

Total video duration.

isLoadingStateNotifier

bool

Loading state.

Note

Design Principle: A strategy observes the player's state but does not directly control its behavior.

Other data sources

Strategies can also get data from any public API on the controller:

Source

Description

Example

ValueNotifier listener

Reactive, push-based approach suitable for logic driven by state changes.

listen(controller.playStateNotifier, ...)

Configuration data

Read configuration information, such as the data source or scene type.

controller.widgetData?.videoSource

Active query

Call asynchronous methods on the controller to get real-time data.

await controller.getCurrentPosition()

Constructor injection

Pass external dependencies through the strategy's constructor parameters.

MyStrategy(apiClient: dio)

If the data you need does not have a corresponding Notifier, you can:

class MyCustomStrategy extends BasePlayerStrategy {
  late AliPlayerWidgetController _controller;

  @override
  String get name => 'MyCustomStrategy';

  @override
  void onStart(AliPlayerWidgetController controller) {
    _controller = controller;

    // Method 1: Use an existing Notifier to trigger an active query in the callback.
    listen<int>(controller.playStateNotifier, (state) {
      // When the playback state changes, actively query the current playback position.
      controller.getCurrentPosition().then((pos) {
        // ...handle business logic
      });
    });

    // Method 2: Read configuration information directly.
    final videoSource = controller.widgetData?.videoSource;
    // ...execute different logic based on the video source type.
  }
}
Note

Notifiers are the preferred data source because they are reactive and automatically cleaned up. However, strategies are not limited to them. Any public API on the controller is a valid way to retrieve data.

How the strategy system works

The strategy system operates through the following mechanisms:

  1. Automatic registration: The StrategyManager registers recommended built-in strategies by default upon creation.

  2. Lifecycle binding: Strategies are started with the controller's configure() method and destroyed with the destroy() method.

  3. Exception isolation: An exception in one strategy does not affect the operation of other strategies.

  4. Callback safety: The framework uses an isActive flag to ensure that external callbacks are not triggered after a strategy has stopped.

Practical example

See example/lib/pages/strategy/strategy_demo_page.dart for a complete example of using the strategy system, including a full implementation of a custom strategy for resuming playback.

The strategy system lets you encapsulate business concerns into independent strategies, which you can combine flexibly for different scenarios.