All Products
Search
Document Center

ApsaraVideo VOD:Sistem Slot

Last Updated:Sep 11, 2026

Sistem Slot merupakan arsitektur inti AliPlayerKit yang membagi UI pemutar menjadi komponen-komponen independen melalui komponenisasi. Sistem terpadu mengelola komponen-komponen ini untuk mencapai penguraian keterkaitan UI, fleksibilitas pencampuran, serta kustomisasi yang dapat diperluas.

Pengenalan Konsep

Apa itu Sistem Slot?

Sistem Slot adalah salah satu arsitektur inti AliPlayerKit yang berfungsi sebagai mekanisme manajemen plugin untuk komponen UI.

Sistem ini memungkinkan developer menambah, menghapus, atau mengganti komponen UI secara dinamis di posisi mana pun pada antarmuka pemutar, sehingga menghasilkan UI pemutar yang sangat dapat dikustomisasi.

Apa itu Slot?

Slot adalah kontainer tampilan yang dicadangkan untuk menampung sekelompok komponen UI terkait.

Setiap slot memiliki siklus hidup, logika tampil/sembunyi, serta manajemen event sendiri. Slot dapat secara otomatis menampilkan atau menyembunyikan komponen internal berdasarkan kondisi seperti status pemutaran atau jenis skenario.

Fitur

Masalah yang Diselesaikan

  • Keterkaitan erat antara komponen UI dan logika pemutar.

  • Kustomisasi antarmuka memerlukan modifikasi kode sumber framework.

  • Fleksibilitas terbatas saat menggabungkan komponen di berbagai skenario.

  • Manajemen siklus hidup komponen yang kompleks.

Nilai Inti

Sistem Slot menguraikan komponen UI pemutar secara independen. Developer dapat memilih komponen mana yang akan digunakan dan cara mengonfigurasinya.

Penggunaan

Deskripsi

Keuntungan

Gunakan antarmuka default

Komponen pemutar menggunakan UI default resmi

Menyederhanakan integrasi, mengurangi biaya integrasi, dan memungkinkan integrasi low-code.

Penggunaan kustom

Ganti beberapa atau seluruh implementasi slot

Memenuhi berbagai kebutuhan UI yang kaya.

Jangan gunakan slot

Gunakan hanya kemampuan pemutaran

Skenario pemutaran murni tanpa ketergantungan UI.

Keunggulan Arsitektur:

  • Penguraian keterkaitan: Komponen UI terpisah dari logika inti pemutar dengan tanggung jawab yang jelas.

  • Fleksibilitas: Gabungkan dan ganti komponen UI secara dinamis saat runtime.

  • Dapat diperluas: Kustomisasi UI tanpa memodifikasi framework, memungkinkan ekstensibilitas kuat.

  • Pemisahan tanggung jawab: Gaya UI (layout XML) terpisah dari logika bisnis (penanganan kelas Slot).

Kemampuan Inti

Kemampuan

Deskripsi

Pencampuran dinamis

Gabungkan komponen UI secara bebas saat runtime.

Hot swapping

Ganti implementasi komponen tanpa perlu restart.

Adaptasi skenario

Otomatis beralih perilaku UI untuk skenario pemutaran berbeda.

Komponen Bawaan

AliPlayerKit menyediakan serangkaian komponen bawaan siap pakai yang mencakup kebutuhan umum UI pemutar.

Jenis Slot

Jenis Slot

Deskripsi

Implementasi Default

PLAYER_SURFACE

Tampilan video, mendukung berbagai tampilan rendering

DisplayViewSlot / SurfaceViewSlot / TextureViewSlot

FULLSCREEN

Manajemen layar penuh, menangani peralihan orientasi layar

FullscreenSlot

GESTURE_CONTROL

Kontrol gesture, menangani klik/klik ganda/tahan lama/geser

GestureControlSlot

LANDSCAPE_HINT

Petunjuk tampilan lanskap, memandu pengguna ke layar penuh

LandscapeHintSlot

COVER

Gambar sampul, menampilkan sampul video sebelum pemutaran

CoverSlot

CENTER_DISPLAY

Tampilan tengah, menunjukkan status seperti kecepatan pemutaran/kecerahan/volume

CenterDisplaySlot

PLAY_STATE

Status pemutaran, menampilkan prompt loading/error, dll.

PlayStateSlot

LOG_PANEL

Panel log, menampilkan log pemutar saat debugging

LogPanelSlot

TOP_BAR

Bilah kontrol atas, menampilkan tombol kembali/judul/pengaturan, dll.

TopBarSlot

BOTTOM_BAR

Bilah kontrol bawah, menampilkan kontrol pemutaran/bilah progres, dll.

BottomBarSlot

SETTING_MENU

Menu pengaturan, menampilkan pengaturan seperti kecepatan pemutaran/definisi/pencerminan

SettingMenuSlot

Adaptasi Skenario

AliPlayerKit mendefinisikan lima skenario pemutaran. Perilaku slot secara otomatis menyesuaikan setiap skenario:

Skenario

Deskripsi

Aplikasi Khas

VOD

Skenario video-on-demand yang mendukung semua fitur.

Pemutaran video reguler.

LIVE

Skenario streaming langsung dengan operasi garis waktu dinonaktifkan.

Streaming langsung real-time.

VIDEO_LIST

Skenario pemutaran daftar dengan gesture vertikal dinonaktifkan.

Feed informasi dan daftar video pendek.

RESTRICTED

Skenario pemutaran terbatas yang membatasi pencarian (seeking).

Edukasi dan pelatihan, pemantauan ujian.

MINIMAL

Skenario pemutaran minimal yang hanya menampilkan layar video.

Video latar belakang dan video dekoratif.

Aturan Visibilitas Slot:

Slot

VOD

LIVE

VIDEO_LIST

RESTRICTED

MINIMAL

PLAYER_SURFACE

✓

✓

✓

✓

✓

FULLSCREEN

✓

✓

✓

✓

✓

GESTURE_CONTROL

✓

✓

✓

✓

✗

LANDSCAPE_HINT

✓

✓

✓

✓

✗

COVER

✓

✗

✓

✗

✗

CENTER_DISPLAY

✓

✓

✓

✓

✗

PLAY_STATE

✓

✓

✓

✓

✓

LOG_PANEL

Dapat dikonfigurasi

Dapat dikonfigurasi

Dapat dikonfigurasi

Dapat dikonfigurasi

✗

TOP_BAR

✓

✓

✓

✓

✗

BOTTOM_BAR

✓

✓

✓

✓

✗

SETTING_MENU

✓

✓

✓

✓

✗

Perbedaan Perilaku Gesture:

Gesture

VOD

LIVE

VIDEO_LIST

RESTRICTED

MINIMAL

Click

Tampilkan/sembunyikan bilah kontrol

Tampilkan/sembunyikan bilah kontrol

Tampilkan/sembunyikan bilah kontrol

Tampilkan/sembunyikan bilah kontrol

Disabled

Double-click

Alihkan putar/jeda

Alihkan putar/jeda

Alihkan putar/jeda

Alihkan putar/jeda

Disabled

Long press

Pemutaran kecepatan 2×

Disabled

Pemutaran kecepatan 2×

Disabled

Disabled

Horizontal drag

Navigasi progres

Disabled

Navigasi progres

Disabled

Disabled

Left vertical drag

Atur kecerahan

Atur kecerahan

Disabled

Atur kecerahan

Disabled

Right vertical drag

Atur volume

Atur volume

Disabled

Atur volume

Disabled

Perbedaan Bilah Kontrol Bawah:

Control

VOD

LIVE

VIDEO_LIST

RESTRICTED

MINIMAL

Tombol play/pause

✓

✓

✓

✓

✗

Progress bar

Draggable

Not draggable

Draggable

Not draggable

✗

Tampilan waktu

✓

✓

✓

✓

✗

Tombol refresh

✗

✓

✗

✗

✗

Tombol full screen

✓

✓

✓

✓

✗

Penggunaan Dasar

Sistem Slot menawarkan tiga strategi penggunaan. Developer dapat memilih metode yang sesuai berdasarkan kebutuhan mereka:

Strategi

Deskripsi

Skenario Penerapan

Gunakan antarmuka default

Metode paling sederhana; komponen pemutar menggunakan antarmuka default.

Integrasi cepat, skenario pemutaran standar.

Kustomisasi beberapa slot

Hanya kustomisasi slot tertentu; sisanya menggunakan antarmuka default.

Kustomisasi parsial, mempertahankan interaksi default.

Kustomisasi antarmuka sepenuhnya

Kustomisasi semua slot untuk membuat antarmuka pemutar yang sepenuhnya personal.

Kustomisasi mendalam, UI khusus merek.

Strategi Satu: Gunakan Konfigurasi Default

Pendekatan paling sederhana. Langsung gunakan konfigurasi slot bawaan:

// 1. Dapatkan tampilan pemutar
AliPlayerView playerView = findViewById(R.id.player_view);

// 2. Buat controller dan data pemutaran
AliPlayerController controller = new AliPlayerController(this);
AliPlayerModel model = new AliPlayerModel.Builder()
        .videoSource(videoSource)
        .build();

// 3. Ikat ke tampilan (otomatis menggunakan slot default)
playerView.attach(controller, model);

Strategi Dua: Kustomisasi Beberapa Slot

Hanya kustomisasi slot tertentu; sisanya mempertahankan antarmuka default. Misalnya, hanya kustomisasi bilah atas:

// 1. Buat registri
SlotRegistry registry = new SlotRegistry();

// 2. Daftarkan hanya slot yang akan dikustomisasi
registry.register(SlotType.TOP_BAR, parent -> new MyTopBarSlot(parent.getContext()));

// 3. Teruskan registri saat mengikat (slot yang tidak terdaftar menggunakan implementasi default)
playerView.attach(controller, model, registry);

Strategi Tiga: Kustomisasi Antarmuka Secara Penuh

Kustomisasi semua slot untuk membuat antarmuka pemutar yang sepenuhnya personal:

// 1. Buat registri
SlotRegistry registry = new SlotRegistry();

// 2. Daftarkan semua slot
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()));
// ... Daftarkan slot lainnya

// 3. Teruskan registri saat mengikat
playerView.attach(controller, model, registry);     

Penggunaan Lanjutan

Cara Mengimplementasikan Switching Slot Dinamis?

Alihkan implementasi slot secara dinamis saat runtime:

// Dapatkan manajer
ISlotManager slotManager = playerView.getSlotManager();

// Alihkan jenis Surface
registry.register(SlotType.PLAYER_SURFACE,
    parent -> new TextureViewSlot(parent.getContext()));
slotManager.rebuildSlots();        

Cara Mengimplementasikan Slot Kustom?

AliPlayerKit menyediakan dua cara untuk mengimplementasikan slot kustom. Developer dapat memilih metode yang sesuai berdasarkan kebutuhan mereka.

Metode Satu: Warisi BaseSlot (Direkomendasikan)

Mewarisi BaseSlot adalah metode paling sederhana. Framework mengenkapsulasi logika umum seperti manajemen siklus hidup dan langganan event.

Skenario Penerapan: Sebagian besar slot UI, seperti sampul, bilah kontrol, dan tampilan status.

  1. Buat file layout.

    Buat file layout di direktori res/layout/:

    <!-- 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>
  2. Buat kelas Slot.

    Warisi BaseSlot dan override metode yang diperlukan:

    public class MyCoverSlot extends BaseSlot {
    
        public MyCoverSlot(@NonNull Context context) {
            super(context);
        }
    
        @Override
        protected int getLayoutId() {
            return R.layout.my_cover_layout;  // Kembalikan ID layout
        }
    
        @Override
        public void onBindData(@NonNull AliPlayerModel model) {
            // Ikat data
            ImageView coverImage = findViewByIdCompat(R.id.cover_image);
            Glide.with(getContext()).load(model.getCoverUrl()).into(coverImage);
        }
    
        @Override
        public void onUnbindData() {
            // Bersihkan resource
            ImageView coverImage = findViewByIdCompat(R.id.cover_image);
            Glide.with(getContext()).clear(coverImage);
        }
    }
  3. Daftarkan dan gunakan.

    SlotRegistry registry = new SlotRegistry();
    registry.register(SlotType.COVER, parent -> new MyCoverSlot(parent.getContext()));
    playerView.attach(controller, model, registry);

Metode Dua: Implementasikan Antarmuka ISlot

Langsung mengimplementasikan antarmuka ISlot memberikan fleksibilitas lebih besar. Namun, Anda harus mengelola siklus hidup secara manual.

Skenario Penerapan: Memerlukan kontrol penuh atas hierarki tampilan atau perlu mewarisi jenis View tertentu (seperti SurfaceView atau TextureView).

  1. Buat file layout.

    Buat file layout di direktori res/layout/:

    <!-- 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>
  2. Buat kelas Slot.

    Implementasikan antarmuka ISlot dan dapatkan kemampuan slot dengan menyusun SlotBehavior:

    public class MySurfaceSlot extends FrameLayout implements ISlot, ISurfaceProvider {
    
        // Dapatkan kemampuan inti slot melalui penyusunan
        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. Delegasikan penanganan siklus hidup ke SlotBehavior
            slotBehavior.attach(host);
            // 2. Simpan referensi host
            this.host = host;
            // 3. Siapkan Surface
            setupSurfaceProvider(host);
        }
    
        @Override
        public void onDetach() {
            if (host != null) {
                onSurfaceDestroyed(host);
            }
            slotBehavior.detach();
            host = null;
        }
    
        @Override
        public void onBindData(@NonNull AliPlayerModel model) {
            // Logika pengikatan data
        }
    
        @Override
        public void onUnbindData() {
            // Logika pembersihan data
        }
    
        @Override
        public void setupSurfaceProvider(@Nullable SlotHost host) {
            // Logika penyiapan Surface
        }
    }
  3. Daftarkan dan gunakan.

    SlotRegistry registry = new SlotRegistry();
    registry.register(SlotType.COVER, parent -> new MyCoverSlot(parent.getContext()));
    playerView.attach(controller, model, registry);

Perbandingan Metode

Fitur

Warisi BaseSlot

Implementasikan Antarmuka ISlot

Volume kode

Lebih sedikit, fokus hanya pada logika bisnis

Lebih banyak, perlu penanganan siklus hidup manual

Fleksibilitas

Medium, mewarisi FrameLayout

Tinggi, dapat mewarisi jenis View apa pun

Siklus hidup

Framework mengelola secara otomatis

Harus mendelegasikan secara manual ke SlotBehavior

Skenario yang direkomendasikan

Sebagian besar slot UI

Memerlukan jenis tampilan khusus

Cara Memulihkan UI?

Saat memulihkan UI pemutar dalam proyek, tangani skenario berbeda sebagai berikut:

Skenario Satu: Slot Resmi Tidak Tersedia

Jika komponen pemutar resmi tidak menyediakan jenis slot yang Anda butuhkan, implementasikan sendiri:

  1. Buat slot kustom.

    Tempatkan slot kustom di direktori ui/slots/custom untuk membedakannya dari implementasi resmi:

    your-module/src/main/java/com/yourpackage/
    └── ui/
        └── slots/
            └── custom/
                ├── MyDanmakuSlot.java      // Slot komentar langsung
                ├── MySubtitleSlot.java     // Slot takarir
                └── MyWatermarkSlot.java    // Slot watermark

Skenario Dua: Slot Resmi Tersedia Tetapi Tidak Diperlukan

Jika komponen pemutar resmi menyediakan slot tetapi skenario Anda tidak memerlukannya, tangani dengan salah satu dari dua cara berikut:

  • Metode Satu: Nonaktifkan melalui konfigurasi (direkomendasikan).

    Hapus item konfigurasi terkait dalam metode createDefaultConfigs() dari SlotConstants:

    // Sebelum modifikasi: SlotRegistry menyuntikkan komponen default
    configs.add(new SlotConfig.Builder()
            .type(SlotType.LOG_PANEL)
            .excludeScenes(createSet(SceneType.MINIMAL))
            .condition(AliPlayerKit::isLogPanelEnabled)
            .build());
    
    // Setelah modifikasi: Beri komentar atau hapus konfigurasi ini; SlotRegistry tidak akan lagi menyuntikkan komponen default
    // configs.add(new SlotConfig.Builder()
    //         .type(SlotType.LOG_PANEL)
    //         ...
    //         .build());
  • Metode Dua: Penghapusan fisik (tidak direkomendasikan).

    Langsung hapus file kelas Slot terkait beserta resource-nya. Hal ini tidak direkomendasikan.

Skenario Tiga: Gaya UI Slot Resmi Tidak Memenuhi Persyaratan

Jika gaya UI slot resmi tidak memenuhi persyaratan Anda, modifikasi langsung file layout XML untuk memulihkan UI. Berkat pemisahan gaya dan logika, modifikasi layout tidak memengaruhi logika bisnis.

  1. Temukan file layout terkait.

    File layout berada di direktori playerkit/src/main/res/layout/, dengan konvensi penamaan layout_{slot_type}.xml:

    Jenis Slot

    File Tata Letak

    TOP_BAR

    layout_top_bar_slot.xml

    BOTTOM_BAR

    layout_bottom_bar_slot.xml

    COVER

    layout_cover_slot.xml

    PLAY_STATE

    layout_play_state_slot.xml

    …

    …

  2. Modifikasi file layout.

    Modifikasi langsung atribut gaya dalam file XML. Pastikan ID kontrol tetap tidak berubah:

    <!-- Sebelum modifikasi: Gaya default resmi -->
    <LinearLayout
        android:background="#80000000"
        android:padding="8dp">
    
        <ImageView
            android:id="@+id/iv_back"
            android:layout_width="40dp"
            android:layout_height="40dp" />
    </LinearLayout>
    
    <!-- Setelah modifikasi: Gaya kustom (pertahankan ID) -->
    <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>
  3. Batasan arsitektur.

    Batasan berikut memastikan kode kustom tidak bertentangan dengan kode resmi:

    1. ID kontrol harus tetap. Kelas Slot menemukan kontrol dan mengikat event menggunakan ID-nya.

    2. Struktur layout dapat disesuaikan, tetapi kontrol inti harus dipertahankan.

    3. Logika bisnis tidak perlu dimodifikasi; secara otomatis menyesuaikan dengan layout baru.

  4. Rekomendasi kompatibilitas upgrade.

    Untuk melakukan modifikasi UI secara ekstensif, kami merekomendasikan menggunakan strategi Slot Kustom untuk menghindari konflik selama upgrade AliPlayerKit di masa depan:

    • Jangan langsung memodifikasi file layout resmi atau kelas Slot.

    • Buat versi V2 (seperti TopBarSlotV2.java dan layout_top_bar_slot_v2.xml).

    • Daftarkan melalui SlotRegistry atau modifikasi SlotConstants untuk mengganti implementasi default.

    public class TopBarSlotV2 extends BaseSlot {
    
        @Override
        protected int getLayoutId() {
            return R.layout.layout_top_bar_slot_v2;
        }
    }

Cara Mengimplementasikan Slot Headless?

Sistem Slot mendukung baik komponenisasi komponen UI maupun komponenisasi logika bisnis. Selain slot UI, Anda dapat memperluas fungsionalitas melalui slot headless, yang mengimplementasikan logika bisnis tanpa merender UI apa pun.

Contoh: Slot Manajemen Layar Penuh

FullscreenSlot resmi adalah contoh khas slot headless. Slot ini hanya mengelola logika alih layar penuh dan tidak memiliki kemampuan rendering UI.

public class FullscreenSlot extends BaseSlot {

    @Override
    protected int getLayoutId() {
        return 0;  // Kembalikan 0 untuk tanpa layout, tanpa rendering UI
    }

    @Override
    public void onAttach(@NonNull SlotHost host) {
        super.onAttach(host);
        // Dengarkan event alih layar penuh
    }

    @Override
    protected void onEvent(@NonNull PlayerEvent event) {
        if (event instanceof FullscreenEvents.Toggle) {
            // Tangani logika alih layar penuh
            toggleFullscreen();
        }
    }

    private void toggleFullscreen() {
        // Penanganan logika murni: ubah orientasi Activity, sesuaikan UI sistem, dll.
    }
}

Metode Implementasi:

Metode

Deskripsi

Skenario Penerapan

Warisi BaseSlot, getLayoutId() mengembalikan 0

Sederhana, mewarisi FrameLayout tetapi tidak merender.

Sebagian besar slot headless.

Implementasikan antarmuka ISlot + susun SlotBehavior

Fleksibel, sama sekali tidak mewarisi View.

Komponen logika murni.

Nilai Desain:

Slot headless memberikan keunggulan yang sama dari sistem slot kepada logika bisnis:

  • Manajemen siklus hidup terpadu.

  • Sinkronisasi otomatis dengan status pemutar.

  • Penggantian yang dapat dipasang.

  • Kolaborasi setara dengan slot UI.

Praktik Terbaik

Manajemen Siklus Hidup

public class MySlot extends BaseSlot {

    @Override
    public void onAttach(@NonNull SlotHost host) {
        super.onAttach(host);
        // Inisialisasi tampilan
    }

    @Override
    public void onDetach() {
        // Bersihkan resource
        super.onDetach();
    }
}

Langganan Event

@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) {
        // Tangani perubahan status
    }
}

Seleksi Permukaan

Skenario

Jenis yang Direkomendasikan

Alasan

Pemutaran normal

SurfaceView

Kinerja lebih baik

Memerlukan animasi

TextureView

Mendukung transformasi

Pemutaran latar belakang

No Surface

Menghemat resource

Referensi Contoh

Proyek menyediakan contoh lengkap yang terletak di playerkit-examples/example-slot-system.

Fitur Contoh

Feature

Description

SurfaceView switching

Cocok untuk skenario pemutaran normal

TextureView switching

Cocok untuk skenario yang memerlukan animasi

Empty slot switching

Cocok untuk pemutaran hanya audio

Jalankan Contoh

Pada Aplikasi Demo, pilih contoh Sistem Slot untuk melihat efeknya.

Referensi API

Antarmuka Inti

Antarmuka/Kelas

Deskripsi

ISlot

Antarmuka slot, mendefinisikan siklus hidup

BaseSlot

Kelas dasar slot, mengenkapsulasi logika umum

SlotRegistry

Registri layanan, mengelola builder

ISlotManager

Manajer, menyediakan fungsi rebuild dan kueri

Metode BaseSlot

Metode

Deskripsi

getLayoutId()

Mengembalikan ID resource layout

getHost()

Mendapatkan host slot

show() / hide() / gone()

Mengontrol visibilitas

postEvent(event)

Menerbitkan event

Prinsip Teknis

Siklus Hidup

Slot menggunakan sistem siklus hidup ganda:

Siklus Hidup

Deskripsi

Siklus hidup tampilan

onAttach → onDetach

Siklus hidup data

onBindData → onUnbindData

Hubungan dengan Siklus Hidup Android:

Siklus hidup slot bersifat independen dari siklus hidup Activity atau Fragment Android. Untuk melakukan operasi tertentu (seperti menjeda animasi atau menghentikan polling) saat aplikasi beralih antara latar depan dan latar belakang, tangani sebagai berikut:

Metode Satu: Dengarkan Event Status Pemutaran

Dengarkan perubahan status pemutaran menggunakan observedEvents(). Hentikan animasi saat pemutar dijeda:

@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();  // Jeda animasi
        } else if (state == PlayerState.PLAYING) {
            startAnimation();  // Lanjutkan animasi
        }
    }
}

Metode Dua: Kontrol di Activity/Fragment

Pada metode onPause() atau onResume() Activity, ambil slot melalui manajer slot dan panggil metodenya:

@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();
    }
}

Aliran Data Unidireksional

Sistem Slot menggunakan arsitektur aliran data unidireksional. Slot tidak menyimpan referensi langsung ke controller. Sebaliknya, mereka berinteraksi secara terurai melalui antarmuka yang disediakan oleh host:

Controller → State / Event → Slot (Status mengalir ke bawah, read-only)
Slot → Command → Controller (Perintah mengalir ke atas, hanya kirim)
  • Status mengalir ke bawah: Dua cara untuk mendapatkan status.

    • Kueri aktif: Kueri status saat ini menggunakan getPlayerStateStore().

    • Dengarkan pasif: Terima notifikasi perubahan status dengan berlangganan event.

  • Perintah mengalir ke atas: Slot mengirim perintah menggunakan postEvent(). Controller mengeksekusinya, dan slot tidak bergantung pada detail eksekusi.

Kueri status aktif: Dapatkan akses read-only ke status pemutar menggunakan host.getPlayerStateStore():

@Override
public void onAttach(@NonNull SlotHost host) {
    super.onAttach(host);

    // Kueri status pemutaran
    PlayerState state = host.getPlayerStateStore().getPlayState();

    // Kueri posisi pemutaran saat ini dan durasi total
    long position = host.getPlayerStateStore().getCurrentPosition();
    long duration = host.getPlayerStateStore().getDuration();
}

Dengarkan status secara pasif: Terima perubahan status dengan berlangganan event:

@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
    return Arrays.asList(
        PlayerEvents.StateChanged.class,    // Perubahan status pemutaran
        PlayerEvents.Info.class             // Pembaruan progres pemutaran
    );
}

@Override
protected void onEvent(@NonNull PlayerEvent event) {
    if (event instanceof PlayerEvents.StateChanged) {
        // Tangani perubahan status pemutaran
        updatePlayPauseIcon(((PlayerEvents.StateChanged) event).newState);
    } else if (event instanceof PlayerEvents.Info) {
        // Tangani pembaruan progres pemutaran
        PlayerEvents.Info info = (PlayerEvents.Info) event;
        updateProgress(info.currentPosition, info.bufferedPosition, info.duration);
    }
}

Kirim perintah: Kirim event perintah menggunakan postEvent() untuk memicu perilaku pemutar:

// Alihkan putar/jeda
postEvent(new PlayerCommand.Toggle(mPlayerId));

// Cari ke posisi tertentu
postEvent(new PlayerCommand.Seek(mPlayerId, 30000));

// Atur kecepatan pemutaran
postEvent(new PlayerCommand.SetSpeed(mPlayerId, 1.5f));

// Ambil tangkapan layar
postEvent(new PlayerCommand.Snapshot(mPlayerId));

Arsitektur aliran data unidireksional sepenuhnya menguraikan keterkaitan antara slot dan controller. Slot tidak perlu menyimpan referensi controller. Mereka hanya perlu fokus pada kueri status dan pengiriman perintah, mencapai pemisahan tanggung jawab yang jelas.

Isolasi Horizontal dan Spesifikasi Event Antar Slot

Untuk memastikan stabilitas arsitektur, slot tidak boleh langsung mendapatkan instance slot lain atau melakukan komunikasi titik-ke-titik.

Saat Slot A (seperti slot overlay pengaturan) memicu perubahan status yang memengaruhi Slot B (seperti slot bilah kontrol bawah):

  1. Event bubbling: Slot A menggunakan postEvent() untuk meneruskan event ke Controller.

  2. State sinking: Controller atau StateStore menangani perubahan status secara terpusat dan menyiarkan status baru ke semua slot yang berlangganan.

Tautan berbentuk U ini (**event bubbling, state sinking**) menghindari coupling mesh dan risiko deadlock.

Petunjuk intersepsi event: Kontainer slot yang menutupi lapisan atas halaman harus menangani pengiriman event gesture untuk menghindari pemblokiran deteksi gesture oleh GestureControlSlot yang berada di bawahnya.

FAQ

Kapan onAttach dipanggil?

Terpicu saat Anda memanggil playerView.attach() atau slotManager.rebuildSlots().

Pertimbangan Slot Kustom?

  1. Panggil super.onAttach(host) di onAttach.

  2. Bersihkan resource sebelum onDetach.

Cara Men-debug?

Gunakan LogHub untuk melihat log. Format TAG adalah ClassName.BaseSlot.

Contoh Crash Umum

Berikut adalah masalah paling umum yang menyebabkan crash menurut laporan pelanggan. Hindari secara ketat:

Error 1: Tidak Memanggil super.onAttach() Menyebabkan Gangguan Siklus Hidup

Kode salah:

public class MySlot extends BaseSlot {

    @Override
    public void onAttach(@NonNull SlotHost host) {
        // Lupa memanggil super.onAttach(host)
        // Langsung inisialisasi tampilan
        ImageView iv = findViewByIdCompat(R.id.iv_icon);
        iv.setOnClickListener(v -> postEvent(...));  // NullPointerException!
    }
}    

Kode benar:

@Override
public void onAttach(@NonNull SlotHost host) {
    super.onAttach(host);  // Harus dipanggil terlebih dahulu
    // Kemudian inisialisasi tampilan
    ImageView iv = findViewByIdCompat(R.id.iv_icon);
    iv.setOnClickListener(v -> postEvent(...));
}          

Penyebab crash: Jika super.onAttach(host) tidak dipanggil, slotBehavior tidak dapat diinisialisasi, event tidak dapat berlangganan, dan Surface tidak dapat diatur. Hal ini menyebabkan getHost() mengembalikan null, postEvent() gagal, dan langganan event menjadi tidak valid.

Error 2: Timpa ID Saat XML Kustom Menyebabkan NullPointerException

Kode salah:

<!-- Saat menyesuaikan layout, ID bertentangan dengan ID default resmi -->
<LinearLayout ...>
    <!-- Resmi menggunakan @+id/iv_back, dan Anda menimpanya -->
    <ImageView
        android:id="@+id/iv_back"
        android:src="@drawable/my_icon" />  <!-- Ekspektasi resmi adalah tombol kembali -->
</LinearLayout>      

Praktik benar:

  1. Pertahankan ID kontrol inti tidak berubah: ID kontrol yang digunakan oleh kelas Slot harus dipertahankan dalam layout.

  2. Pencocokan jenis kontrol: Jenis kontrol yang sesuai dengan ID harus cocok dengan jenis dalam kode.

<!-- Pertahankan ID inti tidak berubah -->
<LinearLayout ...>
    <ImageView
        android:id="@+id/iv_back"  <!-- Pertahankan ID, tetapi Anda dapat mengubah gaya -->
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/custom_back" />  <!-- Anda dapat mengubah ikon -->
</LinearLayout>   

Penyebab crash: Jika layout tidak memiliki ID kontrol yang dibutuhkan oleh kelas Slot (R.id.iv_back) atau jenis kontrol salah, findViewByIdCompat() menyebabkan cast gagal atau pengikatan event klik salah.

Error 3: Memanggil getHost() di Konstruktor

Kode salah:

public class MySlot extends BaseSlot {

    public MySlot(@NonNull Context context) {
        super(context);
        // getHost() mengembalikan null di konstruktor
        SlotHost host = getHost();  // null!
    }
}

Kode benar:

@Override
public void onAttach(@NonNull SlotHost host) {
    super.onAttach(host);
    // Host hanya dapat diakses setelah onAttach
    SlotHost host = getHost();  // Diperoleh secara normal
}

Penyebab crash: Instance slot belum terikat ke host selama konstruksi. Hal ini menyebabkan getHost() mengembalikan null.

Error 4: Tidak Membersihkan Resource di onDetach Menyebabkan Kebocoran Memori

Kode salah:

public class MySlot extends BaseSlot {

    private Handler handler = new Handler();

    @Override
    public void onAttach(@NonNull SlotHost host) {
        super.onAttach(host);
        handler.postDelayed(() -> updateUI(), 1000);  // Tugas tertunda
    }

    @Override
    public void onDetach() {
        // Lupa menghapus tugas tertunda
        super.onDetach();
    }
}

Kode benar:

@Override
public void onDetach() {
    handler.removeCallbacksAndMessages(null);  // Bersihkan semua tugas tertunda
    super.onDetach();
}

Penyebab crash: Jika tugas tertunda menyimpan referensi Slot, memori tidak dapat dikembalikan (kebocoran memori). Selain itu, Slot mungkin sudah berada dalam status detached saat tugas dieksekusi.

Error 5: Langganan Event Tidak Dideklarasikan di observedEvents()

Kode salah:

public class MySlot extends BaseSlot {

    @Override
    public void onAttach(@NonNull SlotHost host) {
        super.onAttach(host);
        // Diharapkan menerima event, tetapi tidak dideklarasikan di observedEvents()
    }

    @Override
    protected void onEvent(@NonNull PlayerEvent event) {
        // Tidak pernah dipanggil!
    }
}

Kode benar:

@Override
protected List<Class<? extends PlayerEvent>> observedEvents() {
    return Arrays.asList(
        PlayerEvents.StateChanged.class,  // Deklarasikan event yang akan berlangganan
        PlayerEvents.Info.class
    );
}

@Override
protected void onEvent(@NonNull PlayerEvent event) {
    // Sekarang event dapat diterima secara normal
}

Penyebab crash: BaseSlot mendeklarasikan event yang akan berlangganan melalui observedEvents(). Jika tidak dideklarasikan, tidak akan berlangganan.