Cette rubrique décrit le contexte de conception, les concepts clés, les stratégies intégrées et l'utilisation du système de stratégies AliPlayerKit.
Contexte
Le système de stratégies a été conçu pour résoudre le conflit entre la personnalisation métier et l'évolution du framework dans les projets AUI Kit à grande échelle.
En tant que solution low-code, les kits AUI doivent prendre en charge la personnalisation de l'interface utilisateur et des processus principaux pour différents métiers. Si la logique métier est intégrée directement dans le code du framework, cela crée un couplage fort. Cela peut entraîner des conflits de code, rompre la logique métier lors des mises à niveau du framework et entraver les itérations de version.
Ce type de logique relève d'une exigence métier et non d'une fonctionnalité centrale du framework. Sans un mécanisme approprié pour la gérer, la logique spécifique au métier peut s'infiltrer dans le framework. Cela menace la stabilité du système et sa capacité à évoluer.
Le système de stratégies résout ce problème :
Il abstrait la logique spécifique au métier en unités de stratégie indépendantes et extensibles.
Il découple la logique métier du cœur du framework.
Les métiers peuvent étendre le système au niveau de la couche de stratégie sans modifier le framework.
Ce mécanisme permet aux capacités métier de devenir une base systématique et réutilisable. Il crée une base itérative, réutilisable et extensible qui soutient l'évolution à long terme d'AliPlayerKit.
Concepts
Qu'est-ce qu'un système de stratégies ?
Le système de stratégies est un mécanisme architectural qui unifie la gestion des composants de stratégie. Il est responsable du mécanisme d'enregistrement des stratégies, de la gestion du cycle de vie, de l'injection de contexte et de l'abonnement aux événements. Il gère également la création, le démarrage, la pause, la réinitialisation et la destruction des stratégies pendant l'exécution du lecteur. Grâce à ce mécanisme, les développeurs peuvent activer ou désactiver les stratégies selon leurs besoins, mettre en œuvre rapidement des politiques personnalisées ou remplacer complètement les stratégies par défaut pour répondre aux besoins de personnalisation métier.
Qu'est-ce qu'une stratégie ?
Une stratégie est une unité encapsulée de logique métier du lecteur. Elle met en œuvre une capacité métier spécifique ou une logique personnalisée, telle que les statistiques de temps jusqu'à la première image (TTFF), la détection des saccades, la protection du trafic ou la reprise de la lecture.Caractéristiques principales :
Responsabilité unique : se concentre sur un objectif fonctionnel indépendant.
Piloté par les événements : répond au statut du lecteur via le bus d'événements.
Indépendant et isolé : les stratégies n'interfèrent pas les unes avec les autres.
Grâce à une conception modulaire et composable, les capacités non essentielles sont abstraites en unités de stratégie. Cela maintient le cœur du lecteur simple tout en offrant des capacités d'extension flexibles.
Fonctionnalités
Problèmes résolus
Le système de stratégies répond aux points de douleur suivants :
Maintenance difficile : la logique métier dispersée est difficile à maintenir et à réutiliser.
Absence de configuration : différents scénarios nécessitent différentes combinaisons de logique, mais il n'existe aucun mécanisme de configuration flexible.
Modification du code source : la logique personnalisée nécessite de modifier le code source du framework.
Interférence entre instances : dans les scénarios multi-lecteurs, la logique métier entre les instances peut interférer les unes avec les autres.
Valeur principale
Le système de stratégies abstrait et encapsule la logique de surveillance et d'analyse du lecteur. Il permet aux clients de configurer et de combiner les stratégies selon leurs besoins.
|
Méthode d'utilisation |
Description |
Avantage |
|
Utiliser les stratégies par défaut |
Le composant lecteur utilise les stratégies par défaut officielles. |
Simplifie le processus d'intégration. Fonctionne dès la sortie de la boîte. |
|
Politique personnalisée |
Mettre en œuvre des stratégies pour des exigences métier spécifiques. |
Répond à divers besoins métier personnalisés. |
|
N'utiliser aucune stratégie |
Utiliser uniquement la fonctionnalité de lecture. |
Pour les scénarios de lecture pure sans dépendances logiques. |
Avantages architecturaux :
Découplage : les stratégies sont séparées de la logique centrale du lecteur, créant des responsabilités claires.
Réutilisabilité : les stratégies peuvent être réutilisées sur différentes instances de lecteur.
Extensibilité : vous pouvez personnaliser les stratégies sans modifier le framework.
Isolation : les stratégies sont indépendantes et n'interfèrent pas les unes avec les autres.
Capacités principales
|
Capacité |
Description |
|
Piloté par les événements |
Les stratégies s'abonnent aux événements du lecteur via le bus d'événements et s'exécutent de manière réactive. |
|
Gestion du cycle de vie |
Gère le démarrage, l'arrêt, la réinitialisation et la destruction des stratégies. |
|
Injection de contexte |
Les stratégies peuvent accéder au statut et aux données du lecteur via le contexte. |
|
Modèle Factory |
Prend en charge l'enregistrement global des usines de stratégies, qui sont automatiquement appliquées à toutes les instances de lecteur. |
Stratégies intégrées
AliPlayerKit fournit trois stratégies intégrées prêtes à l'emploi.
Politique de temps jusqu'à la première image (TTFF) (FirstFrameStrategy)
Cette stratégie calcule le temps nécessaire au lecteur pour passer de l'étape PREPARING au rendu de la première image. Elle est divisée en trois phases :
|
Phase |
Description |
|
Preparing |
PREPARING → Prepared |
|
Rendering |
Prepared → FirstFrameRendered |
|
Temps total |
PREPARING → FirstFrameRendered |
Scénarios : surveillance des performances, optimisation de l'expérience utilisateur et réglage du lecteur.
Politique de détection des saccades (StutterDetectStrategy)
Cette stratégie surveille les saccades pendant la lecture et collecte les statistiques de saccades :
|
Métrique |
Description |
|
Nombre de saccades |
Le nombre de fois où des saccades se produisent. |
|
Durée des saccades |
La durée de chaque événement de saccade. |
|
Durée totale des saccades |
La durée totale des saccades pour toute la session de lecture. |
|
Durée de lecture effective |
Le temps de visionnage réel, excluant les pauses et les saccades. |
Scénarios : surveillance de la qualité de lecture et analyse de l'expérience utilisateur.
Politique de protection du trafic (TrafficProtectionStrategy)
Cette stratégie écoute les changements d'état du réseau et notifie les utilisateurs dans des scénarios spécifiques :
|
Scénario |
Comportement |
|
Passage du Wi-Fi au réseau mobile |
Notifie l'utilisateur que la lecture utilise désormais les données mobiles. |
|
Démarrage de la lecture sur un réseau mobile |
Notifie l'utilisateur que les données mobiles sont utilisées. |
Scénarios : scénarios sensibles au trafic et notifications conviviales.
Utilisation de base
Le système de stratégies propose trois façons d'utiliser les stratégies. Choisissez celle qui correspond le mieux à vos besoins :
|
Politique |
Description |
Scénario |
|
Utiliser les stratégies par défaut |
La méthode la plus simple. Le composant lecteur enregistre automatiquement les stratégies par défaut. |
Intégration rapide et scénarios de lecture standard. |
|
Personnaliser certaines stratégies |
Enregistrer uniquement des stratégies spécifiques et ne pas utiliser les autres. |
Personnalisation partielle et activation des stratégies selon les besoins. |
|
Personnaliser entièrement les stratégies |
Personnaliser toutes les stratégies pour créer un système de surveillance entièrement personnalisé. |
Personnalisation approfondie et exigences métier spécifiques. |
Méthode 1 : Utiliser les stratégies intégrées
Il s'agit de la méthode la plus simple. Le composant lecteur enregistre automatiquement les stratégies par défaut :
// 1. Create a player controller. Default strategies are registered automatically.
AliPlayerController controller = new AliPlayerController(this);
// 2. Prepare the playback data.
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
// 3. Configure the data model.
controller.configure(model);
// 4. Attach the controller to the view.
playerView.attach(controller);
Les stratégies enregistrées par défaut incluent les éléments suivants :
FirstFrameStrategy (TTFF)
StutterDetectStrategy (détection des saccades)
TrafficProtectionStrategy (protection du trafic)
Méthode 2 : Personnaliser les stratégies
Enregistrez uniquement les stratégies dont vous avez besoin et désactivez les autres :
// 1. Create a player controller.
AliPlayerController controller = new AliPlayerController(this);
// 2. Get the strategy manager and clear the default strategies.
StrategyManager strategyManager = controller.getStrategyManager();
// 3. Register only the required strategies.
strategyManager.register(new FirstFrameStrategy());
strategyManager.register(new StutterDetectStrategy());
// 4. Prepare the playback data.
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
controller.configure(model);
playerView.attach(controller);
Méthode 3 : Personnaliser entièrement les stratégies
Implémentez des politiques personnalisées pour créer un système de surveillance entièrement personnalisé :
// 1. Create a player controller.
AliPlayerController controller = new AliPlayerController(this);
// 2. Get the strategy manager.
StrategyManager strategyManager = controller.getStrategyManager();
// 3. Register custom policies.
strategyManager.register(new MyCustomStrategy());
strategyManager.register(new MyAnalyticsStrategy());
// 4. Prepare the playback data.
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
controller.configure(model);
playerView.attach(controller);
Utilisation avancée
Comment implémenter une politique personnalisée
AliPlayerKit propose deux méthodes d'implémentation. Choisissez celle qui correspond à vos besoins :
|
Méthode |
Fonctionnalités |
Scénario |
|
Hériter de |
Moins de code. Le framework gère les abonnements aux événements. |
La plupart des stratégies de surveillance. |
|
Implémenter l'interface |
Grande flexibilité. Contrôle autonome complet. |
Nécessite une logique de contrôle spéciale. |
Méthode 1 : Hériter de BaseStrategy (recommandé)
L'héritage de BaseStrategy est la méthode la plus simple car le framework a déjà encapsulé l'abonnement aux événements et la gestion du cycle de vie.
Scénario : la plupart des stratégies de surveillance et d'analyse, telles que les statistiques, la détection et la journalisation.
-
Créez une classe de stratégie.
Héritez de
BaseStrategyet implémentez les méthodes nécessaires :public class MyAnalyticsStrategy extends BaseStrategy { private static final String TAG = "MyAnalyticsStrategy"; @NonNull @Override public String getName() { return TAG; // Return the policy name for logging and debugging. } @Nullable @Override protected List<Class<? extends PlayerEvent>> observedEvents() { // Declare the event types to subscribe to. return Arrays.asList( PlayerEvents.StateChanged.class, PlayerEvents.Prepared.class, PlayerEvents.Info.class ); } @Override public void onEvent(@NonNull PlayerEvent event) { // Filter out events from other players. if (!isCurrentPlayer(event)) return; // Process the event. if (event instanceof PlayerEvents.StateChanged) { handleStateChanged((PlayerEvents.StateChanged) event); } else if (event instanceof PlayerEvents.Prepared) { handlePrepared((PlayerEvents.Prepared) event); } else if (event instanceof PlayerEvents.Info) { handleInfo((PlayerEvents.Info) event); } } @Override public void onReset() { super.onReset(); // Reset the internal state to prepare for a new video. } private void handleStateChanged(PlayerEvents.StateChanged event) { // Handle state changes. } private void handlePrepared(PlayerEvents.Prepared event) { // Handle preparation completion. } private void handleInfo(PlayerEvents.Info event) { // Handle playback information updates. } } -
Commencez.
StrategyManager strategyManager = controller.getStrategyManager(); strategyManager.register(new MyAnalyticsStrategy());
Exemple : pour plus d'informations, consultez strategies/FirstFrameStrategy.java.
Méthode 2 : Implémenter l'interface IStrategy
L'implémentation directe de l'interface IStrategy offre une plus grande flexibilité, mais vous devez gérer vous-même les abonnements aux événements.
Scénario : vous avez besoin d'un contrôle total sur le comportement de la stratégie ou vous avez besoin d'une logique d'initialisation spéciale.
-
Créez une classe de stratégie.
Implémentez l'interface
IStrategyet gérez vous-même les abonnements aux événements :public class MyCustomStrategy implements IStrategy, PlayerEventBus.EventListener<PlayerEvent> { private static final String TAG = "MyCustomStrategy"; private StrategyContext mContext; @NonNull @Override public String getName() { return TAG; } @Override public void onStart(@NonNull StrategyContext context) { mContext = context; // Manually subscribe to events. PlayerEventBus.getInstance().subscribe(PlayerEvents.StateChanged.class, this); } @Override public void onStop() { // Manually unsubscribe from events. PlayerEventBus.getInstance().unsubscribe(PlayerEvents.StateChanged.class, this); mContext = null; } @Override public void onReset() { // Reset the internal state. } @Override public void onEvent(@NonNull PlayerEvent event) { // Process the event. } } -
Inscrivez-vous pour un compte.
StrategyManager strategyManager = controller.getStrategyManager(); strategyManager.register(new MyAnalyticsStrategy());
Comment implémenter une stratégie avec des rappels
Les stratégies peuvent utiliser des interfaces de rappel pour notifier la couche métier des résultats :
public class MyAnalyticsStrategy extends BaseStrategy {
public interface Callback {
void onAnalysisComplete(AnalysisResult result);
}
@Nullable
private final Callback mCallback;
// Support a parameterless constructor.
public MyAnalyticsStrategy() {
this(null);
}
// Support passing a callback.
public MyAnalyticsStrategy(@Nullable Callback callback) {
mCallback = callback;
}
@Override
public void onEvent(@NonNull PlayerEvent event) {
// ... business logic
// Notify the result through the callback.
if (mCallback != null) {
mCallback.onAnalysisComplete(result);
}
}
}
Utilisation :
strategyManager.register(new MyAnalyticsStrategy(result -> {
// Process the analysis result.
Log.d("Analytics", "Result: " + result);
}));
Exemple : pour plus d'informations, consultez strategies/FirstFrameStrategy.java.
Comment accéder au statut du lecteur
Les stratégies peuvent accéder au statut et aux données du lecteur via StrategyContext :
@Override
public void onEvent(@NonNull PlayerEvent event) {
if (!isCurrentPlayer(event)) return;
// Get the player ID.
String playerId = getPlayerId();
// Get the playback data model.
AliPlayerModel model = mContext.getModel();
if (model != null) {
String title = model.getVideoTitle();
VideoSource source = model.getVideoSource();
}
// Get the player state store.
IPlayerStateStore stateStore = mContext.getPlayerStateStore();
long position = stateStore.getCurrentPosition();
long duration = stateStore.getDuration();
PlayerState state = stateStore.getPlayState();
}
mContext est null dans le constructeur. Il n'est initialisé qu'après l'appel de onStart().
Comment enregistrer une stratégie globale
Vous pouvez utiliser StrategyRegistry pour enregistrer une stratégie globale. Elle sera automatiquement appliquée à toutes les nouvelles instances de lecteur créées :
// Register a global strategy.
StrategyRegistry.addGlobalStrategy(() -> new MyAnalyticsStrategy());
// All AliPlayerController instances created after this will automatically register this strategy.
AliPlayerController controller = new AliPlayerController(this);
Restaurer les configurations par défaut :
StrategyRegistry.resetToDefault();
Bonnes pratiques
Gestion du cycle de vie
public class MyStrategy extends BaseStrategy {
@Overridepublic void onStart(@NonNull StrategyContext context) {
super.onStart(context); // Must be called.
// Initialize resources.
}
@Overridepublic void onStop() {
// Clean up resources.
super.onStop(); // Must be called.
}
@Overridepublic void onReset() {
super.onReset();
// Reset the internal state to prepare for a new video.
}
}
Filtrage des événements
Dans les scénarios multi-lecteurs, filtrez toujours la source de l'événement :
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// Filter out events from other players to avoid interference.
if (!isCurrentPlayer(event)) return;
// Process events for the current player.
}
Nettoyage des ressources
Si une stratégie possède des ressources asynchrones telles qu'un Handler ou un Runnable, nettoyez-les dans onStop() :
public class MyStrategy extends BaseStrategy {
private final Handler mHandler = new Handler();
private Runnable mRunnable;
@Overridepublic void onStart(@NonNull StrategyContext context) {
super.onStart(context);
mRunnable = () -> doSomething();
mHandler.postDelayed(mRunnable, 1000);
}
@Overridepublic void onStop() {
// Clean up asynchronous tasks to avoid memory leaks.
mHandler.removeCallbacks(mRunnable);
super.onStop();
}
}
Remarques
|
Scénario |
Pratique recommandée |
Raison |
|
Scénarios multi-lecteurs |
Utilisez |
Éviter les interférences d'événements. |
|
Tâches de longue durée |
Annulez les tâches dans |
Éviter les fuites de mémoire. |
|
Changement de vidéo |
Réinitialisez l'état dans |
Assurer la justesse de l'état. |
|
Accès au contexte |
Obtenez-le via |
Assurer la disponibilité du contexte. |
Référence d'exemple
Le projet fournit un exemple complet situé dans playerkit-examples/example-strategy-system.
Fonctionnalités de l'exemple
|
Fonctionnalité |
Description |
|
Stratégie de reprise de lecture |
Enregistre automatiquement la progression de la lecture et reprend à partir de ce point la prochaine fois. |
|
Démonstration d'enregistrement de stratégie |
Démontre comment enregistrer une politique personnalisée. |
Exécuter l'exemple
Dans l'application de démonstration, sélectionnez l'exemple Strategy System pour le voir en action.
Référence API
Interfaces principales
|
Interface/Classe |
Description |
|
|
L'interface de stratégie. Elle définit les méthodes du cycle de vie. |
|
|
La classe de base pour les stratégies. Elle encapsule la gestion des abonnements aux événements. |
|
|
Le gestionnaire de stratégies. Il gère le cycle de vie des stratégies. |
|
|
Le contexte de stratégie. Il fournit des informations en lecture seule sur le lecteur. |
|
|
Le registre de service pour les stratégies. Il gère les stratégies globales. |
Méthodes BaseStrategy
|
Méthode |
Description |
|
|
Renvoie le nom de la politique pour la journalisation et le débogage. |
|
|
Renvoie une liste des types d'événements auxquels s'abonner. |
|
|
Le rappel d'événement. Il traite les événements abonnés. |
|
|
Vérifie si l'événement provient du lecteur actuel. |
|
|
Obtient l'ID du lecteur actuel. |
Méthodes StrategyManager
|
Méthode |
Description |
|
|
Enregistre une stratégie. |
|
|
Désenregistre une stratégie. |
|
|
Obtient une stratégie par son nom. |
|
|
Démarre toutes les stratégies. |
|
|
Arrête toutes les stratégies. |
|
|
Réinitialise toutes les stratégies. |
|
|
Détruit le gestionnaire. |
Méthodes StrategyContext
|
Méthode |
Description |
|
|
Obtient l'ID du lecteur. |
|
|
Obtient le modèle de données de lecture. |
|
|
Obtient le magasin d'état du lecteur (lecture seule). |
Principes techniques
Architecture globale
Le système de stratégies utilise un modèle architectural piloté par les événements et planifié par un gestionnaire.
Points de conception :
Les stratégies ne conservent pas de références au lecteur : un découplage complet est réalisé grâce à une conception pilotée par les événements.
Les stratégies se concentrent uniquement sur les événements : elles ont seulement besoin de déclarer ce à quoi s'abonner et comment le traiter.
Le gestionnaire gère la planification : le gestionnaire contrôle le cycle de vie et la distribution des événements.
Cycle de vie
Les stratégies utilisent un cycle de vie en trois étapes :
|
Cycle de vie |
Condition de déclenchement |
Description |
|
|
|
La stratégie démarre, s'abonne aux événements et initialise les ressources. |
|
|
Le contenu de lecture change |
Réinitialise l'état interne pour préparer une nouvelle vidéo. |
|
|
|
La stratégie s'arrête, se désabonne et nettoie les ressources. |
Mécanisme piloté par les événements
Les stratégies s'abonnent aux événements du lecteur via un bus d'événements, en utilisant un modèle publication-abonnement.
Flux d'abonnement aux événements :
La stratégie déclare les types d'événements auxquels elle doit s'abonner dans
observedEvents().BaseStrategys'abonne automatiquement à ces événements dansonStart().BaseStrategyse désabonne automatiquement dansonStop().La stratégie traite les événements reçus dans
onEvent().
Philosophie de conception : le mécanisme piloté par les événements découple complètement les stratégies du lecteur. Les stratégies n'ont pas besoin de conserver une référence au lecteur. Elles doivent seulement se concentrer sur l'abonnement aux événements et la logique de traitement. Cela permet une séparation des préoccupations.
Isolation multi-lecteur
Dans les scénarios multi-lecteurs, le système de stratégies assure l'isolation grâce aux mécanismes suivants :
StrategyManager indépendant : chaque
AliPlayerControllerpossède son propreStrategyManagerindépendant.StrategyContext indépendant : chaque contexte de stratégie est lié à un ID de lecteur spécifique.
Filtrage des événements : les stratégies filtrent les sources d'événements en utilisant
isCurrentPlayer(event).
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// Events from each player instance carry a playerId.
// isCurrentPlayer() checks if the event is from the current player.
if (!isCurrentPlayer(event)) return;
// Process only events from the current player.
}
FAQ
Quand une stratégie démarre-t-elle ?
Toutes les stratégies enregistrées démarrent automatiquement lorsque vous appelez playerView.attach().
Comment obtenir le statut actuel du lecteur ?
Utilisez la méthode getPlayerStateStore() de StrategyContext :
IPlayerStateStore stateStore = mContext.getPlayerStateStore();
PlayerState state = stateStore.getPlayState();
long position = stateStore.getCurrentPosition();
Comment déboguer une stratégie ?
Utilisez LogHub pour afficher les journaux. Le format du TAG est Nom de la politique.BaseStrategy ou StrategyManager.
Erreurs courantes provoquant des plantages
Voici les problèmes les plus couramment signalés par les clients qui entraînent des plantages. Évitez ces erreurs.
Erreur 1 : Accéder à mContext dans le constructeur
Code incorrect :
public class MyStrategy extends BaseStrategy {
public MyStrategy() {
super();
// mContext is null in the constructor.
String playerId = getPlayerId(); // Returns an empty string.
AliPlayerModel model = mContext.getModel(); // NullPointerException!
}
}
Code correct :
@Overridepublic void onStart(@NonNull StrategyContext context) {
super.onStart(context); // ✅ Must be called first.
// mContext can be accessed after onStart.
String playerId = getPlayerId(); // Gets the ID normally.
AliPlayerModel model = mContext.getModel(); // Gets the model normally.
}
Raison du plantage : mContext est assigné après l'appel de onStart(). La stratégie n'a pas encore démarré lorsque le constructeur s'exécute.
Erreur 2 : Oublier d'appeler super.onStart() entraîne l'échec de l'abonnement aux événements
Code incorrect :
public class MyStrategy extends BaseStrategy {
@Overridepublic void onStart(@NonNull StrategyContext context) {
// Forgot to call super.onStart(context).
// mContext will be null, and event subscription will not execute.
initMyResources();
}
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// This will never be called because event subscription did not execute.
}
}
Code correct :
@Overridepublic void onStart(@NonNull StrategyContext context) {
super.onStart(context); // ✅ Must be called first.
initMyResources();
}
Raison du plantage : super.onStart(context) initialise mContext et s'abonne aux événements renvoyés par observedEvents(). Ne pas l'appeler entraînera un mContext nul et des abonnements aux événements invalides.
Erreur 3 : Le fait de ne pas filtrer les événements provoque des interférences dans les scénarios multi-lecteurs
Code incorrect :
public class MyStrategy extends BaseStrategy {
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// The event source is not filtered, which causes crosstalk in multi-player scenarios.
if (event instanceof PlayerEvents.StateChanged) {
// State changes from all players will trigger this logic.
updateUI(); // This may update the UI of the wrong player.
}
}
}
Code correct :
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// First, filter out events that are not from the current player. if (!isCurrentPlayer(event)) return;
if (event instanceof PlayerEvents.StateChanged) {
updateUI(); // Only process events from the current player.
}
}
Cause du plantage : dans un scénario multi-lecteur, les événements de tous les lecteurs sont distribués à toutes les politiques. L'absence de filtrage amène les politiques à traiter les événements d'autres lecteurs.
Erreur 4 : Le fait de ne pas nettoyer les tâches asynchrones dans onStop() provoque une fuite de mémoire
Code incorrect :
public class MyStrategy extends BaseStrategy {
private Handler handler = new Handler();
@Overridepublic void onStart(@NonNull StrategyContext context) {
super.onStart(context);
handler.postDelayed(() -> updateProgress(), 1000); // Delayed task.
}
@Overridepublic void onStop() {
// Forgot to remove the delayed task.
super.onStop();
}
}
Code correct :
@Overridepublic void onStop() {
handler.removeCallbacksAndMessages(null); // Clear all delayed tasks
super.onStop();
}
Cause du plantage : une tâche différée conserve une référence à une instance de politique. Après l'arrêt de la politique, l'instance ne peut pas être récupérée par le garbage collector, ce qui provoque une fuite de mémoire. Lorsque la tâche s'exécute, elle peut accéder à des ressources déjà détruites.
Erreur 5 : observedEvents renvoie null, ce qui entraîne l'échec de l'abonnement aux événements
Code incorrect :
public class MyStrategy extends BaseStrategy {
@Nullable@Overrideprotected List<Class<? extends PlayerEvent>> observedEvents() {
// Returning null will not cause a crash, but the event subscription will not be executed.
return null;
}
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// Will never be called.
}
}
Code correct :
@Nullable@Overrideprotected List<Class<? extends PlayerEvent>> observedEvents() {
return Arrays.asList(
PlayerEvents.StateChanged.class, // Declare the events to subscribe to
PlayerEvents.Prepared.class
);
}
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
// You can now receive events.
}
Raison du plantage : BaseStrategy vérifie la valeur de retour de observedEvents() lors de l'abonnement aux événements. Si la valeur de retour est null ou une liste vide, elle ne s'abonne à aucun événement.
Erreur 6 : Le fait de ne pas réinitialiser l'état interne dans onReset() conduit à des données incorrectes
Code incorrect :
public class MyStrategy extends BaseStrategy {
private int mPlayCount = 0;
@Overridepublic void onEvent(@NonNull PlayerEvent event) {
if (!isCurrentPlayer(event)) return;
if (event instanceof PlayerEvents.Prepared) {
mPlayCount++; // The count is not reset when the video is switched, so it accumulates.
Log.d(TAG, "Play count: " + mPlayCount);
}
}
@Overridepublic void onReset() {
super.onReset();
// Forgot to reset mPlayCount.
}
}
Code correct :
@Overridepublic void onReset() {
super.onReset();
mPlayCount = 0; // Reset the internal state.
}
Cause du plantage : onReset() est appelé lors du changement de vidéo pour réinitialiser l'état interne de la politique. Si l'état n'est pas réinitialisé, il s'accumule et affecte la précision statistique.