The Slot System is a core architectural design of AliPlayerKit. It decomposes the player UI into independent, pluggable components through componentization. A unified system manages these components to achieve UI decoupling, flexible composition, and extensible customization.
Introduction to Concepts
What is the Slot System?
The Slot System is one of AliPlayerKit’s core architectural designs. It is a plug-in management mechanism for UI components.
It enables developers to dynamically add, remove, or replace UI components at any position in the player interface, resulting in a highly customizable player UI.
What is a Slot?
A Slot is a reserved view container that holds a group of related UI components.
Each slot has its own lifecycle, display and hide logic, and event management. It can automatically show or hide internal components based on conditions such as playback status or scenario type.
Features
Problems Solved
Tight coupling between UI components and player logic.
Customizing the interface requires modifying framework source code.
Limited flexibility when combining components across different scenarios.
Complex component lifecycle management.
Core Value
The Slot System independently decomposes player UI components. Developers can choose which ones to use and how to configure them.
Usage | Description | Advantages |
Use default interface | The player component uses the official default UI | Simplifies integration, reduces integration costs, and enables low-code integration. |
Custom use | Replace some or all slot implementations | Meets various rich UI requirements. |
Do not use slots | Use only playback capabilities | Pure playback scenarios with no UI dependencies. |
Architectural Advantages:
Decoupling: UI components are separate from core player logic, with clearly defined responsibilities.
Flexibility: Dynamically combine and replace UI components at runtime.
Extensibility: Customize the UI without modifying the framework, enabling strong extensibility.
Separation of concerns: UI styles (XML layouts) are separate from business logic (Slot class handling).
Core Capabilities
Capability | Description |
Dynamic combination | Freely combine UI components at runtime. |
Hot swapping | Replace component implementations without restarting. |
Scenario adaptation | Automatically switch UI behavior for different playback scenarios. |
Built-in Components
AliPlayerKit provides a set of out-of-the-box built-in components that cover common player UI requirements.
Slot Types
Slot Type | Description | Default Implementation |
PLAYER_SURFACE | Video display, supports various rendering views | DisplayViewSlot / SurfaceViewSlot / TextureViewSlot |
FULLSCREEN | Full screen management, handles screen orientation switching | FullscreenSlot |
GESTURE_CONTROL | Gesture control, handles click/double-click/long press/drag | GestureControlSlot |
LANDSCAPE_HINT | Landscape viewing hint, guides users to full screen | LandscapeHintSlot |
COVER | Cover image, displays video cover before playback | CoverSlot |
CENTER_DISPLAY | Center display, shows status such as playback speed/brightness/volume | CenterDisplaySlot |
PLAY_STATE | Playback status, shows loading/error prompts, etc. | PlayStateSlot |
LOG_PANEL | Log panel, displays player logs during debugging | LogPanelSlot |
TOP_BAR | Top control bar, shows back/title/settings, etc. | TopBarSlot |
BOTTOM_BAR | Bottom control bar, shows playback controls/progress bar, etc. | BottomBarSlot |
SETTING_MENU | Settings menu, shows settings such as playback speed/definition/mirroring | SettingMenuSlot |
Scenario Adaptation
AliPlayerKit defines five playback scenarios. Slot behavior automatically adapts to each scenario:
Scenario | Description | Typical Applications |
VOD | Video-on-demand scenario, supports all features. | Regular video playback. |
LIVE | Live streaming scenario, timeline operations disabled. | Real-time live streams. |
VIDEO_LIST | List playback scenario, vertical gestures disabled. | Information feeds, short video lists. |
RESTRICTED | Restricted playback scenario, limits seeking. | Education and training, exam monitoring. |
MINIMAL | Minimal playback scenario, only displays the video screen. | Background videos, decorative videos. |
Slot Visibility Rules:
Slot | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
PLAYER_SURFACE | ✓ | ✓ | ✓ | ✓ | ✓ |
FULLSCREEN | ✓ | ✓ | ✓ | ✓ | ✓ |
GESTURE_CONTROL | ✓ | ✓ | ✓ | ✓ | ✗ |
LANDSCAPE_HINT | ✓ | ✓ | ✓ | ✓ | ✗ |
COVER | ✓ | ✗ | ✓ | ✗ | ✗ |
CENTER_DISPLAY | ✓ | ✓ | ✓ | ✓ | ✗ |
PLAY_STATE | ✓ | ✓ | ✓ | ✓ | ✓ |
LOG_PANEL | Configurable | Configurable | Configurable | Configurable | ✗ |
TOP_BAR | ✓ | ✓ | ✓ | ✓ | ✗ |
BOTTOM_BAR | ✓ | ✓ | ✓ | ✓ | ✗ |
SETTING_MENU | ✓ | ✓ | ✓ | ✓ | ✗ |
Gesture Behavior Differences:
Gesture | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
Click | Show/hide control bar | Show/hide control bar | Show/hide control bar | Show/hide control bar | Disabled |
Double-click | Toggle play/pause | Toggle play/pause | Toggle play/pause | Toggle play/pause | Disabled |
Long press | 2× speed playback | Disabled | 2× speed playback | Disabled | Disabled |
Horizontal drag | Progress Navigation | Disabled | Progress navigation | Disabled | Disabled |
Left vertical drag | Adjust brightness | Adjust brightness | Disabled | Adjust brightness | Disabled |
Right vertical drag | Adjust volume | Adjust volume | Disabled | Adjust volume | Disabled |
Bottom Control Bar Differences:
Control | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
Play/pause button | ✓ | ✓ | ✓ | ✓ | ✗ |
Progress bar | Draggable | Not draggable | Draggable | Not draggable | ✗ |
Time display | ✓ | ✓ | ✓ | ✓ | ✗ |
Refresh button | ✗ | ✓ | ✗ | ✗ | ✗ |
Full screen button | ✓ | ✓ | ✓ | ✓ | ✗ |
Basic Usage
The Slot System offers three usage strategies. Developers can select the appropriate method based on their needs:
Strategy | Description | Applicable Scenarios |
Use default interface | The simplest method; the player component uses the default interface. | Quick integration, standard playback scenarios. |
Customize some slots | Customize only specific slots; others use the default interface. | Partial customization, retaining default interactions. |
Fully customize interface | Customize all slots to create a fully personalized player interface. | Deep customization, brand-specific UI. |
Strategy One: Use Default Configurations
The simplest approach. Directly use the built-in slot configurations:
// 1. Get the player view
AliPlayerView playerView = findViewById(R.id.player_view);
// 2. Create the controller and playback data
AliPlayerController controller = new AliPlayerController(this);
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
// 3. Bind to the view (automatically uses default slots)
playerView.attach(controller, model);Strategy Two: Customize Some Slots
Customize only specific slots; others retain the default interface. For example, customize only the top bar:
// 1. Create a registry
SlotRegistry registry = new SlotRegistry();
// 2. Register only the slots to customize
registry.register(SlotType.TOP_BAR, parent -> new MyTopBarSlot(parent.getContext()));
// 3. Pass the registry during binding (unregistered slots use default implementation)
playerView.attach(controller, model, registry);Strategy Three: Fully Customize Interface
Customize all slots to create a fully personalized player interface:
// 1. Create a registry
SlotRegistry registry = new SlotRegistry();
// 2. Register all slots
registry.register(SlotType.PLAYER_SURFACE, parent -> new MySurfaceSlot(parent.getContext()));
registry.register(SlotType.COVER, parent -> new MyCoverSlot(parent.getContext()));
registry.register(SlotType.TOP_BAR, parent -> new MyTopBarSlot(parent.getContext()));
registry.register(SlotType.BOTTOM_BAR, parent -> new MyBottomBarSlot(parent.getContext()));
// ... Register other slots
// 3. Pass the registry during binding
playerView.attach(controller, model, registry); Advanced Usage
How to Implement Dynamic Slot Switching?
Dynamically switch slot implementations at runtime:
// Get the manager
ISlotManager slotManager = playerView.getSlotManager();
// Switch Surface type
registry.register(SlotType.PLAYER_SURFACE,
parent -> new TextureViewSlot(parent.getContext()));
slotManager.rebuildSlots(); How to Implement Custom Slots?
AliPlayerKit provides two ways to implement custom slots. Developers can select the appropriate method based on their requirements.
Method One: Inherit BaseSlot (Recommended)
Inheriting BaseSlot is the simplest method. The framework encapsulates common logic such as lifecycle management and event subscription.
Applicable Scenarios: Most UI slots, such as covers, control bars, and status displays.
Create the layout file.
Create the layout file in the
res/layout/directory:<!-- res/layout/my_cover_layout.xml --> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/cover_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> </FrameLayout>Create the Slot class.
Inherit
BaseSlotand override the necessary methods:public class MyCoverSlot extends BaseSlot { public MyCoverSlot(@NonNull Context context) { super(context); } @Override protected int getLayoutId() { return R.layout.my_cover_layout; // Return the layout ID } @Override public void onBindData(@NonNull AliPlayerModel model) { // Bind data ImageView coverImage = findViewByIdCompat(R.id.cover_image); Glide.with(getContext()).load(model.getCoverUrl()).into(coverImage); } @Override public void onUnbindData() { // Clean up resources ImageView coverImage = findViewByIdCompat(R.id.cover_image); Glide.with(getContext()).clear(coverImage); } }Register and use.
SlotRegistry registry = new SlotRegistry(); registry.register(SlotType.COVER, parent -> new MyCoverSlot(parent.getContext())); playerView.attach(controller, model, registry);
Method Two: Implement the ISlot Interface
Directly implementing the ISlot interface provides greater flexibility. However, you must manage the lifecycle manually.
Applicable Scenarios: Requires full control over the view hierarchy or needs to inherit specific View types (such as SurfaceView or TextureView).
Create the layout file.
Create the layout file in the
res/layout/directory:<!-- res/layout/my_cover_layout.xml --> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/cover_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> </FrameLayout>Create the Slot class.
Implement the
ISlotinterface and gain slot capabilities by composingSlotBehavior:public class MySurfaceSlot extends FrameLayout implements ISlot, ISurfaceProvider { // Gain core slot capabilities through composition private final SlotBehavior slotBehavior = new SlotBehavior(); private SlotHost host; public MySurfaceSlot(@NonNull Context context) { super(context); View.inflate(context, R.layout.my_surface_layout, this); } @Override public void onAttach(@NonNull SlotHost host) { // 1. Delegate lifecycle handling to SlotBehavior slotBehavior.attach(host); // 2. Save host reference this.host = host; // 3. Set up Surface setupSurfaceProvider(host); } @Override public void onDetach() { if (host != null) { onSurfaceDestroyed(host); } slotBehavior.detach(); host = null; } @Override public void onBindData(@NonNull AliPlayerModel model) { // Data binding logic } @Override public void onUnbindData() { // Data cleanup logic } @Override public void setupSurfaceProvider(@Nullable SlotHost host) { // Surface setup logic } }Register and use.
SlotRegistry registry = new SlotRegistry(); registry.register(SlotType.COVER, parent -> new MyCoverSlot(parent.getContext())); playerView.attach(controller, model, registry);
Method Comparison
Feature | Inherit BaseSlot | Implement ISlot Interface |
Code volume | Less, focus only on business logic | More, manual lifecycle handling required |
Flexibility | Medium, inherits FrameLayout | High, can inherit any View type |
Lifecycle | Framework automatically manages | Must manually delegate to SlotBehavior |
Recommended scenarios | Most UI slots | Requires special view types |
How to Restore the UI?
When restoring the player UI in a project, handle different scenarios as follows:
Scenario One: Official Slots Not Provided
If the official player component does not provide the slot type you need, implement it yourself:
Create custom slots.
Place custom slots in the
ui/slots/customdirectory to distinguish them from official implementations:your-module/src/main/java/com/yourpackage/ └── ui/ └── slots/ └── custom/ ├── MyDanmakuSlot.java // Live comment slot ├── MySubtitleSlot.java // Caption slot └── MyWatermarkSlot.java // Watermark slot
Scenario Two: Official Slots Provided but Not Needed
If the official player component provides slots but your scenario does not require them, handle this in one of two ways:
Method One: Disable via configuration (recommended).
Remove the corresponding configuration item in the
createDefaultConfigs()method ofSlotConstants:// Before modification: SlotRegistry injects default components configs.add(new SlotConfig.Builder() .type(SlotType.LOG_PANEL) .excludeScenes(createSet(SceneType.MINIMAL)) .condition(AliPlayerKit::isLogPanelEnabled) .build()); // After modification: Comment out or delete this configuration; SlotRegistry will no longer inject default components // configs.add(new SlotConfig.Builder() // .type(SlotType.LOG_PANEL) // ... // .build());Method Two: Physical deletion (not recommended).
Directly delete the corresponding Slot class file and its related resources. This is not recommended.
Scenario Three: Official Slot UI Style Does Not Meet Requirements
If the official slot’s UI style does not meet your requirements, modify the XML layout file directly to restore the UI. Thanks to the separation of style and logic, layout modifications do not affect business logic.
Locate the corresponding layout file.
Layout files are in the
playerkit/src/main/res/layout/directory, with a naming convention oflayout_{slot_type}.xml:Slot Type
Layout File
TOP_BAR
layout_top_bar_slot.xmlBOTTOM_BAR
layout_bottom_bar_slot.xmlCOVER
layout_cover_slot.xmlPLAY_STATE
layout_play_state_slot.xml…
…
Modify the layout file.
Directly modify the style attributes in the XML file. Ensure control IDs remain unchanged:
<!-- Before modification: Official default style --> <LinearLayout android:background="#80000000" android:padding="8dp"> <ImageView android:id="@+id/iv_back" android:layout_width="40dp" android:layout_height="40dp" /> </LinearLayout> <!-- After modification: Custom style (keep ID unchanged) --> <LinearLayout android:background="@drawable/custom_top_bar_bg" android:padding="12dp"> <ImageView android:id="@+id/iv_back" android:layout_width="48dp" android:layout_height="48dp" android:src="@drawable/ic_custom_back" /> </LinearLayout>Architectural constraints.
The following constraints ensure that custom code does not conflict with official code:
Control IDs must be fixed. The Slot class finds controls and binds events using their IDs.
Layout structure is adjustable, but core controls must be retained.
Business logic does not require modification; it automatically adapts to the new layout.
Upgrade compatibility recommendations.
To make extensive UI modifications, we recommend using the Custom Slot strategy to avoid conflicts during future AliPlayerKit upgrades:
Do not directly modify official layout files or Slot classes.
Create V2 versions (such as
TopBarSlotV2.javaandlayout_top_bar_slot_v2.xml).Register through
SlotRegistryor modifySlotConstantsto replace the default implementation.
public class TopBarSlotV2 extends BaseSlot { @Override protected int getLayoutId() { return R.layout.layout_top_bar_slot_v2; } }
How to Implement Headless Slots?
The Slot System supports both UI componentization and business logic componentization. In addition to UI slots, you can extend functionality through headless slots, which implement business logic without rendering any UI.
Example: Fullscreen Management Slot
The official FullscreenSlot is a typical headless slot. It manages fullscreen switching logic only and has no UI rendering capability.
public class FullscreenSlot extends BaseSlot {
@Override
protected int getLayoutId() {
return 0; // Return 0 for no layout, no UI rendering
}
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
// Listen for fullscreen toggle events
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
if (event instanceof FullscreenEvents.Toggle) {
// Handle fullscreen toggle logic
toggleFullscreen();
}
}
private void toggleFullscreen() {
// Pure logic handling: modify Activity orientation, adjust system UI, etc.
}
}Implementation Methods:
Method | Description | Applicable Scenarios |
Inherit | Simple, inherits FrameLayout but does not render. | Most headless slots. |
Implement | Flexible, does not inherit View at all. | Pure logic components. |
Design Value:
Headless slots deliver the same advantages of the slot system to business logic:
Unified lifecycle management.
Automatic synchronization with player status.
Pluggable replacement.
Equal collaboration with UI slots.
Best practices
Lifecycle Management
public class MySlot extends BaseSlot {
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
// Initialize view
}
@Override
public void onDetach() {
// Clean up resources
super.onDetach();
}
}Event Subscription
@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
return Arrays.asList(PlayerEvents.StateChanged.class);
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
if (event instanceof PlayerEvents.StateChanged) {
// Handle state changes
}
}Surface Selection
Scenario | Recommended Type | Reason |
Normal playback | SurfaceView | Better performance |
Requires animation | TextureView | Supports transformations |
Background playback | No Surface | Saves resources |
Example Reference
The project provides a complete example located at playerkit-examples/example-slot-system.
Example Features
Feature | Description |
SurfaceView switching | Suitable for normal playback scenarios |
TextureView switching | Suitable for scenarios requiring animation |
Empty slot switching | Suitable for audio-only playback |
Run the Example
In the Demo App, select the Slot System example to see the effect.
API Reference
Core Interfaces
Interface/Class | Description |
| Slot interface, defines lifecycle |
| Slot base class, encapsulates common logic |
| Service registry, manages builders |
| Manager, provides rebuild and query functions |
BaseSlot Methods
Method | Description |
| Returns the layout resource ID |
| Gets the slot host |
| Controls visibility |
| Publishes events |
Technical Principles
Lifecycle
Slots use a dual lifecycle system:
Lifecycle | Description |
View lifecycle |
|
Data lifecycle |
|
Relationship with Android Lifecycle:
The slot lifecycle is independent of the Android Activity or Fragment lifecycle. To perform specific operations (such as pausing animations or stopping polling) when the app switches between foreground and background, handle it as follows:
Method One: Listen for Playback State Events
Listen for playback state changes using observedEvents(). Stop animations when the player pauses:
@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
return Arrays.asList(PlayerEvents.StateChanged.class);
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
if (event instanceof PlayerEvents.StateChanged) {
PlayerState state = ((PlayerEvents.StateChanged) event).newState;
if (state == PlayerState.PAUSED || state == PlayerState.STOPPED) {
stopAnimation(); // Pause animation
} else if (state == PlayerState.PLAYING) {
startAnimation(); // Resume animation
}
}
}Method Two: Control in Activity/Fragment
In the Activity’s onPause() or onResume() methods, retrieve the slot through the slot manager and call its methods:
@Override
protected void onPause() {
super.onPause();
MyAnimationSlot slot = playerView.getSlotManager().getSlot(SlotType.CUSTOM);
if (slot != null) {
slot.pauseAnimation();
}
}
@Override
protected void onResume() {
super.onResume();
MyAnimationSlot slot = playerView.getSlotManager().getSlot(SlotType.CUSTOM);
if (slot != null) {
slot.resumeAnimation();
}
}Unidirectional Data Flow
The Slot System uses a unidirectional data flow architecture. Slots do not hold direct references to controllers. Instead, they interact in a decoupled manner through interfaces provided by the host:
Controller → State / Event → Slot (State flows down, read-only)
Slot → Command → Controller (Commands flow up, send-only)State flows down: Two ways to obtain the state.
Active query: Query the current state using
getPlayerStateStore().Passive listening: Receive state change notifications by subscribing to events.
Commands flow up: Slots send commands using
postEvent(). The controller executes them, and the slot does not depend on execution details.
Actively query state: Get read-only access to the player state using host.getPlayerStateStore():
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
// Query playback state
PlayerState state = host.getPlayerStateStore().getPlayState();
// Query current playback position and total duration
long position = host.getPlayerStateStore().getCurrentPosition();
long duration = host.getPlayerStateStore().getDuration();
}Passively listen for state: Receive state changes by subscribing to events:
@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
return Arrays.asList(
PlayerEvents.StateChanged.class, // Playback state changes
PlayerEvents.Info.class // Playback progress updates
);
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
if (event instanceof PlayerEvents.StateChanged) {
// Handle playback state changes
updatePlayPauseIcon(((PlayerEvents.StateChanged) event).newState);
} else if (event instanceof PlayerEvents.Info) {
// Handle playback progress updates
PlayerEvents.Info info = (PlayerEvents.Info) event;
updateProgress(info.currentPosition, info.bufferedPosition, info.duration);
}
}Send commands: Send command events using postEvent() to trigger player behavior:
// Toggle play/pause
postEvent(new PlayerCommand.Toggle(mPlayerId));
// Seek to a specific position
postEvent(new PlayerCommand.Seek(mPlayerId, 30000));
// Set playback speed
postEvent(new PlayerCommand.SetSpeed(mPlayerId, 1.5f));
// Take a screenshot
postEvent(new PlayerCommand.Snapshot(mPlayerId));The unidirectional data flow architecture completely decouples slots from controllers. Slots do not need to hold controller references. They only need to focus on querying state and sending commands, achieving clear separation of concerns.
Horizontal Isolation and Event Specification Between Slots
To ensure architectural stability, slots must not directly obtain instances of other slots or engage in point-to-point communication.
When Slot A (such as a settings overlay slot) triggers a state change that affects Slot B (such as a bottom control bar slot):
Event bubbling: Slot A uses
postEvent()to pass the event to theController.State sinking: The
ControllerorStateStorecentrally handles state changes and broadcasts the new state to all listening slots.
This U-shaped link (**event bubbling, state sinking**) avoids mesh coupling and deadlock risks.
Event interception tip: Slot containers covering the page’s top layer must handle gesture event dispatch to avoid blocking gesture detection by the underlying GestureControlSlot.
FAQ
When is onAttach called?
It triggers when you call playerView.attach() or slotManager.rebuildSlots().
Custom Slot Considerations?
Call
super.onAttach(host)inonAttach.Clean up resources before
onDetach.
How to Debug?
Use LogHub to view logs. The TAG format is ClassName.BaseSlot.
Common Crash Examples
The following are the most common crash-inducing issues reported by customers. Avoid them strictly:
Error 1: Not Calling super.onAttach() Leads to Lifecycle Disruption
Incorrect code:
public class MySlot extends BaseSlot {
@Override
public void onAttach(@NonNull SlotHost host) {
// Forgot to call super.onAttach(host)
// Directly initialize the view
ImageView iv = findViewByIdCompat(R.id.iv_icon);
iv.setOnClickListener(v -> postEvent(...)); // NullPointerException!
}
} Correct code:
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host); // Must be called first
// Then initialize the view
ImageView iv = findViewByIdCompat(R.id.iv_icon);
iv.setOnClickListener(v -> postEvent(...));
} Cause of crash: If super.onAttach(host) is not called, slotBehavior cannot initialize, events cannot be subscribed, and Surface cannot be set. This causes getHost() to return null, postEvent() to fail, and event subscriptions to become invalid.
Error 2: ID Overwrite During Custom XML Leads to NullPointerException
Incorrect code:
<!-- When customizing the layout, the ID conflicts with the official default ID -->
<LinearLayout ...>
<!-- The official uses @+id/iv_back, and you overwrote it -->
<ImageView
android:id="@+id/iv_back"
android:src="@drawable/my_icon" /> <!-- The official expectation is a back button -->
</LinearLayout> Correct practice:
Keep core control IDs unchanged: Control IDs used by the Slot class must be retained in the layout.
Control type matching: The control type corresponding to the ID must match the type in the code.
<!-- Keep core ID unchanged -->
<LinearLayout ...>
<ImageView
android:id="@+id/iv_back" <!-- Keep the ID, but you can change the style -->
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/custom_back" /> <!-- You can change the icon -->
</LinearLayout> Cause of crash: If the layout lacks the control ID required by the Slot class (R.id.iv_back) or the control type is incorrect, findViewByIdCompat() causes a failed cast or incorrect click event binding.
Error 3: Calling getHost() in the Constructor
Incorrect code:
public class MySlot extends BaseSlot {
public MySlot(@NonNull Context context) {
super(context);
// getHost() returns null in the constructor
SlotHost host = getHost(); // null!
}
}Correct code:
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
// Host can only be accessed after onAttach
SlotHost host = getHost(); // Obtained normally
}Cause of crash: The slot instance is not yet attached to the host during construction. This causes getHost() to return null.
Error 4: Not Cleaning Up Resources in onDetach Leads to Memory Leak
Incorrect code:
public class MySlot extends BaseSlot {
private Handler handler = new Handler();
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
handler.postDelayed(() -> updateUI(), 1000); // Delayed task
}
@Override
public void onDetach() {
// Forgot to remove the delayed task
super.onDetach();
}
}Correct code:
@Override
public void onDetach() {
handler.removeCallbacksAndMessages(null); // Clean up all delayed tasks
super.onDetach();
}Cause of crash: If a delayed task holds a Slot reference, memory cannot be reclaimed (memory leak). Also, the Slot might already be in a detached state when the task executes.
Error 5: Event Subscription Not Declared in observedEvents()
Incorrect code:
public class MySlot extends BaseSlot {
@Override
public void onAttach(@NonNull SlotHost host) {
super.onAttach(host);
// Expected to receive events, but not declared in observedEvents()
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
// Never called!
}
}Correct code:
@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
return Arrays.asList(
PlayerEvents.StateChanged.class, // Declare events to subscribe to
PlayerEvents.Info.class
);
}
@Override
protected void onEvent(@NonNull PlayerEvent event) {
// Now events can be received normally
}Cause of crash: BaseSlot declares events to subscribe to via observedEvents(). If not declared, it will not subscribe.