Dokumen ini menjelaskan latar belakang desain, jenis strategi, dan penggunaan strategi siklus hidup pemutar AliPlayerKit.
Latar Belakang
Strategi siklus hidup pemutar adalah pola strategi yang diabstraksi dari pool pemutar multi-instans yang awalnya diimplementasikan untuk aplikasi drama pendek Alibaba Cloud.
Desain ini memungkinkan penggeseran video cepat dengan menggunakan pool instans pemutar yang dibagikan secara global untuk menyediakan hal-hal berikut:
Penggunaan ulang instans: Mengurangi beban pembuatan instans baru.
Konfigurasi dinamis: Secara fleksibel mengontrol jumlah instans.
Optimalisasi thread: Memberikan kontrol detail halus atas sumber daya thread.
Pendekatan ini memberikan pemuatan frame pertama yang cepat, peralihan lancar, dan jejak memori yang terkendali, sehingga dapat diadaptasi ke berbagai skenario bisnis.
Konsep Utama
Apa itu strategi siklus hidup pemain?
Strategi siklus hidup pemutar adalah arsitektur untuk mengelola siklus hidup instans pemutar. Strategi ini mendefinisikan operasi inti seperti acquire, recycle, dan clear, serta menguraikan keterkaitan antara manajemen sumber daya pemutar dan logika bisnis.
Skenario bisnis yang berbeda memiliki kebutuhan berbeda dalam mengelola instans pemutar:
Kasus penggunaan | Kebutuhan | Strategi yang direkomendasikan |
Standard video playback | Simple dan langsung, tidak perlu penggunaan ulang. | default strategy |
Short video list | Frequent switching, requires fast startup. | reuse pool strategy |
Video preloading | Instance is bound to an ID and supports preloading. | ID-scoped strategy |
Memory-sensitive scenarios | A single global instance for a minimal memory footprint. | singleton strategy |
Keunggulan pola strategi
Pola strategi menawarkan keunggulan inti berikut dalam mengelola siklus hidup pemutar:
Penguraian keterkaitan: Memisahkan logika bisnis dari detail pembuatan dan penghancuran instans pemutar.
Fleksibilitas: Memungkinkan Anda mengganti strategi saat runtime tanpa mengubah logika bisnis.
Ekstensibilitas: Anda dapat mengimplementasikan strategi kustom untuk memenuhi kebutuhan bisnis tertentu.
Kemudahan observasi: Event siklus hidup terpadu mempermudah pemantauan dan debugging.
Fitur
Masalah yang diatasi
Manajemen terdesentralisasi: Tidak ada mekanisme terpadu untuk mengontrol instans pemutar.
Beban performa: Pembuatan dan penghancuran instans pemutar yang sering membuang sumber daya.
Tantangan penggunaan ulang: Instans pemutar tidak dapat digunakan ulang di berbagai skenario.
Memori tidak terkendali: Kurangnya kontrol efektif atas jejak memori.
Nilai inti
Penggunaan | Deskripsi | Keunggulan |
default strategy | Creates a new instance for each use and destroys it afterward. | Simple dan langsung, tanpa residual state. |
reuse pool strategy | Maintains an object pool to reuse idle instances. | Mengurangi beban pembuatan dan meningkatkan kecepatan startup. |
ID-scoped strategy | Maintains a separate instance for each ID. | Mendukung preloading dan penggunaan ulang berbasis ID. |
singleton strategy | A single global instance. | Jejak memori minimal. |
Keunggulan arsitektural:
Penguraian keterkaitan strategi: Manajemen sumber daya pemutar dipisahkan dari logika bisnis untuk tanggung jawab yang jelas.
Pergantian saat runtime: Ganti strategi tanpa me-restart aplikasi.
Thread safety: Semua implementasi strategi mendukung akses aman dari beberapa thread.
Berbasis event: Menerbitkan event siklus hidup melalui event bus untuk mempermudah pemantauan.
Kemampuan inti
Kemampuan | Deskripsi |
Instance acquisition | acquire() Acquires a player instance. The strategy determines whether to create a new one or reuse an existing one. |
Instance recycling | recycle() Recycles a player instance. The strategy determines whether to destroy it or keep it. |
Resource cleanup | clear() Cleans up all resources, ensuring safe release. |
Preloading | preload() Creates instances in advance to reduce time-to-first-frame. |
Pool capacity control | setMaxPoolSize() Dynamically adjusts the pool capacity. |
Komponen bawaan
Jenis strategi
AliPlayerKit menyediakan empat strategi siklus hidup pemutar bawaan:
Strategi | Deskripsi | Kasus penggunaan | Jejak memori |
DefaultLifecycleStrategy | The default strategy. Creates a new instance for each use and destroys it immediately afterward. | Standard playback scenarios, simple dan langsung. | Rendah |
ReusePoolLifecycleStrategy | The reuse pool strategy. Maintains an idle pool and uses a LIFO (Last-In, First-Out) policy for reuse. | Short video lists dan information feeds. | Sedang |
IdScopedPoolLifecycleStrategy | The ID-scoped strategy. Maintains a separate instance for each ID and uses an LRU (Least Recently Used) policy for eviction. | Video preloading, multi-video switching. | Sedang |
SingletonLifecycleStrategy | The singleton strategy. A single global instance. | Memory-sensitive scenarios, single video playback. | Terendah |
Detail strategi
DefaultLifecycleStrategy (default strategy)
Ini adalah strategi paling sederhana. Setiap pemanggilan acquire() membuat instans baru, dan setiap pemanggilan recycle() segera menghancurkannya.
Poin utama:
Stateless, memberikan instans baru setiap kali.
Tidak ada penggunaan ulang, cocok untuk skenario sederhana.
Jejak memori terendah (sumber daya dilepas segera setelah digunakan).
ReusePoolLifecycleStrategy (reuse pool strategy)
Berdasarkan pola object pool, strategi ini memelihara pool idle dan daftar aktif. Strategi ini menggunakan kebijakan LIFO (Last-In, First-Out) untuk penggunaan ulang instans.
Poin utama:
Kebijakan LIFO untuk pool idle, di mana instans yang paling baru digunakan akan digunakan ulang terlebih dahulu.
Mendukung preloading untuk membuat instans lebih awal.
Kapasitas pool dapat dikonfigurasi. Instans akan dihancurkan jika kapasitas terlampaui.
Ideal untuk skenario short video list dan information feed.
Setiap instans pemutar mengonsumsi sekitar 35–40 MB memori. Kapasitas pool default adalah 3, yang dapat Anda sesuaikan berdasarkan performa perangkat.
IdScopedPoolLifecycleStrategy (ID-scoped strategy)
Memelihara instans pemutar terpisah untuk setiap uniqueId, selalu mengembalikan instans yang sama untuk ID tertentu. Strategi ini menggunakan mekanisme LRU (Least Recently Used) untuk mengontrol jumlah instans.
Poin utama:
Berbasis ID, selalu menggunakan ulang instans yang sama untuk ID yang sama.
Mekanisme eviction LRU secara otomatis membersihkan instans yang paling jarang digunakan.
Mendukung preloading untuk membuat instans unbound lebih awal.
Ideal untuk skenario video preloading dan multi-video switching.
SingletonLifecycleStrategy (singleton strategy)
Memelihara satu instans pemutar global. Strategi ini selalu mengembalikan instans yang sama, terlepas dari uniqueId-nya.
Poin utama:
Unik secara global, semua pemanggil berbagi instans yang sama.
Jejak memori terendah.
Tidak cocok untuk memutar beberapa video secara bersamaan.
Event siklus hidup
Strategi menerbitkan event siklus hidup yang dapat Anda dengarkan pada event bus:
Event | Deskripsi | Kapan dipicu |
PlayerCreated | A player is created. | When a new instance is created. |
PlayerDestroyed | A player is destroyed. | When an instance is destroyed. |
PlayerReused | A player is reused. | When an instance is retrieved from the pool for reuse. |
PlayerHit | A player instance is hit. | When an existing instance is hit for a given ID. |
PlayerEvicted | A player is evicted. | When an instance is evicted by the LRU policy or because the pool is full. |
Penggunaan Dasar
Gunakan strategi default
Ini adalah cara paling sederhana untuk menggunakan pemutar, tanpa konfigurasi tambahan:
// Buat controller (secara otomatis menggunakan DefaultLifecycleStrategy)
AliPlayerController controller = new AliPlayerController(context);
// Bind pemutar
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
controller.configure(model);
playerView.attach(controller); Gunakan strategi reuse pool
Strategi ini ideal untuk skenario short video list dan information feed:
// Dapatkan instans singleton dari strategi reuse pool
ReusePoolLifecycleStrategy strategy = ReusePoolLifecycleStrategy.getInstance();
// Atur kapasitas pool (opsional, default 3)
strategy.setMaxPoolSize(3);
// Suntikkan strategi saat membuat controller
AliPlayerController controller = new AliPlayerController(context, strategy);
// Bersihkan sumber daya saat selesai
strategy.clear(); Gunakan strategi ID-scoped
Strategi ini ideal untuk skenario yang memerlukan preloading dan peralihan antara beberapa video:
// Dapatkan instans singleton dari strategi ID-scoped
IdScopedPoolLifecycleStrategy strategy = IdScopedPoolLifecycleStrategy.getInstance();
// Atur kapasitas pool
strategy.setMaxPoolSize(3);
// Preload instans pemutar (opsional)
strategy.preload(context, 2);
// Suntikkan strategi saat membuat controller
AliPlayerController controller = new AliPlayerController(context, strategy);
// Bersihkan sumber daya saat selesai
strategy.clear(); Gunakan strategi singleton
Strategi ini ideal untuk skenario yang sensitif terhadap memori:
// Dapatkan instans strategi singleton
SingletonLifecycleStrategy strategy = SingletonLifecycleStrategy.getInstance();
// Preload instans (opsional, hanya membuat satu instans)
strategy.preload(context, 1);
// Suntikkan strategi saat membuat controller
AliPlayerController controller = new AliPlayerController(context, strategy);Penggunaan Lanjutan
Mendengarkan event siklus hidup
Dengarkan event siklus hidup internal strategi melalui event bus untuk debugging dan pemantauan:
PlayerEventBus eventBus = PlayerEventBus.getInstance();
// Dengarkan pembuatan pemutar
eventBus.subscribe(PlayerLifecycleEvents.PlayerCreated.class, event -> {
Log.d("Player", "Created: " + event.playerId);
});
// Dengarkan penggunaan ulang pemutar
eventBus.subscribe(PlayerLifecycleEvents.PlayerReused.class, event -> {
Log.d("Player", "Reused: " + event.playerId);
});
// Dengarkan eviction pemutar
eventBus.subscribe(PlayerLifecycleEvents.PlayerEvicted.class, event -> {
Log.d("Player", "Evicted: " + event.playerId);
});
// Dengarkan penghancuran pemutar
eventBus.subscribe(PlayerLifecycleEvents.PlayerDestroyed.class, event -> {
Log.d("Player", "Destroyed: " + event.playerId);
});
// Berhenti berlangganan saat tidak diperlukan lagi
eventBus.unsubscribe(PlayerLifecycleEvents.PlayerCreated.class, listener);Mengganti strategi secara dinamis
Anda dapat mengganti strategi saat runtime berdasarkan kebutuhan bisnis:
private IPlayerLifecycleStrategy mCurrentStrategy;
private void switchToReusePool() {
// 1. Bersihkan sumber daya lama
if (mCurrentStrategy != null) {
mCurrentStrategy.clear();
}
// 2. Ganti ke strategi reuse pool
mCurrentStrategy = ReusePoolLifecycleStrategy.getInstance();
mCurrentStrategy.setMaxPoolSize(3);
// 3. Preload
mCurrentStrategy.preload(this, 2);
}
private void switchToSingleton() {
// 1. Bersihkan sumber daya lama
if (mCurrentStrategy != null) {
mCurrentStrategy.clear();
}
// 2. Ganti ke strategi singleton
mCurrentStrategy = SingletonLifecycleStrategy.getInstance();
}Preloading instans pemutar
Preloading membuat instans pemutar lebih awal untuk mengurangi time-to-first-frame:
// Strategi reuse pool: Instans yang dipreloaded ditempatkan di pool idle
ReusePoolLifecycleStrategy strategy = ReusePoolLifecycleStrategy.getInstance();
strategy.preload(context, 2); // Pra-buat 2 instans
// Strategi ID-scoped: Melakukan preload instans unbound
IdScopedPoolLifecycleStrategy strategy = IdScopedPoolLifecycleStrategy.getInstance();
strategy.preload(context, 2); // Pra-buat 2 instans unbound
// Strategi singleton: Melakukan preload instans global
SingletonLifecycleStrategy strategy = SingletonLifecycleStrategy.getInstance();
strategy.preload(context, 1); // Membuat instans global (parameter jumlah diabaikan)Mengimplementasikan strategi kustom
Implementasikan strategi kustom dengan memperluas BaseLifecycleStrategy:
public class MyCustomStrategy extends BaseLifecycleStrategy {
private final Map<String, IMediaPlayer> playerMap = new HashMap<>();
@NonNull@Overridepublic IMediaPlayer acquire(@NonNull Context context, @NonNull String uniqueId) {
// Logika akuisisi kustom
IMediaPlayer player = playerMap.get(uniqueId);
if (player != null) {
// Hit instans yang sudah ada
PlayerEventBus.getInstance().post(
new PlayerLifecycleEvents.PlayerHit(player.getPlayerId()));
return player;
}
// Buat instans baru
player = createPlayer(context);
playerMap.put(uniqueId, player);
return player;
}
@Overridepublic void recycle(@Nullable IMediaPlayer player, @NonNull String uniqueId, boolean force) {
if (player == null) return;
if (force) {
// Hancurkan paksa
playerMap.remove(uniqueId);
destroyPlayer(player);
} else {
// Hanya hentikan pemutar, simpan instansnya
player.stop();
}
}
@Overridepublic void clear() {
// Bersihkan semua instans
for (IMediaPlayer player : playerMap.values()) {
destroyPlayer(player);
}
playerMap.clear();
}
}Praktik Terbaik
Pemilihan strategi
Kasus penggunaan | Strategi yang direkomendasikan | Deskripsi |
Standard video playback | default strategy | Simple dan langsung, tanpa overhead tambahan. |
Short video list (TikTok-style) | reuse pool strategy | Menggunakan ulang instans untuk peralihan cepat. |
Video preloading | ID-scoped strategy | Mengikat instans ke ID dan mendukung preloading. |
Perangkat low-end | singleton strategy | Jejak memori minimal. |
Educational videos (multi-video switching) | ID-scoped strategy | Preload video berikutnya. |
Optimalisasi memori
Setiap instans pemutar mengonsumsi sekitar 35–40 MB memori (berdasarkan hasil profiling memori dari pool pemutar multi-instans Alibaba Cloud untuk drama pendek). Kami merekomendasikan menyesuaikan kapasitas pool berdasarkan performa perangkat:
Jenis perangkat | Konfigurasi yang direkomendasikan | Kapasitas pool |
High-end device | ReusePool atau IdScopedPool | 3 |
Mid-range device | ReusePool atau IdScopedPool | 2 |
Low-end device | Singleton | 1 (default) |
Memory-sensitive scenarios | Singleton | 1 |
// Sesuaikan secara dinamis berdasarkan memori perangkat
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowMemory = am.isLowRamDevice();
if (isLowMemory) {
// Gunakan strategi singleton pada perangkat bermemori rendah
strategy = SingletonLifecycleStrategy.getInstance();
} else {
// Gunakan strategi reuse pool pada perangkat standar
strategy = ReusePoolLifecycleStrategy.getInstance();
strategy.setMaxPoolSize(isHighEndDevice ? 3 : 2);
}Peringatan
Item | Deskripsi |
Clean up promptly | Call clear() atau recycle(force=true) untuk melepas sumber daya saat halaman dihancurkan. |
Strategy singletons | Kelas ReusePoolLifecycleStrategy, IdScopedPoolLifecycleStrategy, dan SingletonLifecycleStrategy diimplementasikan sebagai singleton dengan status yang dibagikan secara global. |
Thread safety | Semua implementasi strategi bersifat thread-safe dan dapat digunakan di lingkungan multi-threaded. |
Unsubscribe from events | Untuk menghindari memory leak, selalu berhenti berlangganan dari event siklus hidup pada waktu yang tepat. |
Initialization order | Strategi diinisialisasi secara otomatis. Anda tidak perlu memanggil init() secara manual. |
Contoh
Contoh lengkap disediakan dalam proyek di playerkit-examples/example-lifecycle-strategy.
Fitur contoh
Feature | Description |
Strategy switching | Beralih secara dinamis antara keempat strategi bawaan. |
Status display | Menampilkan status real-time dari strategi (created/reused/hit/evicted). |
Event listening | Mendengarkan dan menampilkan event lifecycle. |
Multi-video playback | Menunjukkan efek dari peralihan antara multiple video dengan strategi yang berbeda. |
Jalankan contoh
Dalam aplikasi demo, pilih contoh Lifecycle Strategy untuk melihatnya beraksi.
API
Antarmuka inti
Interface/class | Deskripsi |
| Antarmuka strategi siklus hidup pemutar, yang mendefinisikan operasi inti. |
| Kelas dasar untuk strategi, yang mengenkapsulasi logika umum. |
| Antarmuka factory pemutar, yang bertanggung jawab untuk membuat dan menghancurkan instans. |
Metode IPlayerLifecycleStrategy
Metode | Deskripsi |
| Menginisialisasi strategi dan menginjeksi player factory. |
| Mengambil instans pemutar. |
| Mendaur ulang instans pemutar. |
| Membersihkan semua sumber daya. |
| Memuat instans pemutar lebih awal. |
Metode BaseLifecycleStrategy
Metode | Deskripsi |
| Mengatur kapasitas maksimum pool (hanya berlaku untuk strategi berbasis pool). |
| Membuat instans pemutar (dipanggil oleh subclass). |
| Menghancurkan instans pemutar (dipanggil oleh subclass). |
Cara Kerja
Thread safety
Strategi | Thread safety |
DefaultLifecycleStrategy | Stateless dan thread-safe. |
ReusePoolLifecycleStrategy | synchronized blocks. |
IdScopedPoolLifecycleStrategy | synchronized blocks. |
SingletonLifecycleStrategy | AtomicReference + double-checked locking. |
Perbandingan strategi pooling
Fitur | ReusePool | IdScopedPool |
Reuse mechanism | LIFO (Last-In, First-Out) | ID binding + LRU |
Preloading support | Yes (places instances in the idle pool) | Yes (places instances in an unbound queue) |
Eviction policy | Evicts when the pool is full | Automatic LRU eviction |
Use case | Short video list | Multi-video preloading |
Logika arsitektural dasar:
Mengapa strategi reuse pool menggunakan LIFO (Last-In, First-Out)?
Dalam skenario pengguliran naik-turun yang sering dalam feed, instans pemutar yang baru saja ditambahkan ke pool idle adalah yang paling "panas" dalam hal konteks decoder dan sumber daya perangkat kerasnya (cache-nya belum dipindahkan oleh sistem). Menggunakannya kembali terlebih dahulu secara signifikan mengurangi waktu yang diperlukan untuk reset konteks tingkat rendah, meningkatkan kecepatan startup berdasarkan prinsip lokalitas spasial.
Mengapa strategi ID-scoped menggunakan LRU (Least Recently Used)?
Pooling multi-instans mengikuti prinsip lokalitas temporal: saat pengguna menggulir feed, probabilitas memutar ulang video yang jauh lebih lama menurun. Kebijakan eviction LRU selaras dengan pola penurunan ini dalam riwayat penjelajahan pengguna, memungkinkan kontrol efektif atas jejak memori puncak aplikasi sekaligus mempertahankan ikatan ketat dengan ID.
FAQ
Memilih strategi yang tepat
Pilih berdasarkan skenario bisnis Anda:
Pemutaran sederhana: Gunakan strategi default untuk kompleksitas tambahan nol.
Feed short video/informasi: Gunakan strategi reuse pool untuk peralihan cepat.
Preloading video: Gunakan strategi ID-scoped untuk ikatan ID.
Skenario sensitif memori: Gunakan strategi singleton untuk jejak terkecil.
Mengatur kapasitas pool
Sesuaikan kapasitas berdasarkan performa perangkat dan kebutuhan bisnis:
Setiap instans pemutar mengonsumsi sekitar 35–40 MB memori.
Kapasitas pool default adalah 3, yang mengonsumsi sekitar 100–120 MB.
Untuk perangkat low-end, atur kapasitas menjadi 2 atau gunakan strategi singleton.
Menggunakan preloading
Gunakan preloading dalam skenario yang memerlukan pengurangan time-to-first-frame:
Short video list: Preload 1 hingga 2 instans.
Halaman detail video: Preload video rekomendasi berikutnya.
Hindari preloading berlebihan karena akan meningkatkan penggunaan memori.
Perangkap umum
Situasi berikut adalah penyebab paling umum masalah yang dilaporkan pengguna. Pastikan untuk menghindarinya:
Contoh 1: Lupa memanggil clear()
Kode salah:
@Overrideprotected void onDestroy() {
super.onDestroy();
// Hanya detach View, lupa membersihkan instans yang disimpan oleh strategi
mPlayerView.detach();
}
Kode benar:
@Overrideprotected void onDestroy() {
super.onDestroy();
// Detach View
mPlayerView.detach();
// Bersihkan sumber daya strategi
if (mStrategy != null) {
mStrategy.clear();
}
}
Penyebab: Strategi (seperti ReusePool) menyimpan referensi ke instans pemutar. Gagal membersihkannya akan menyebabkan memory leak.
Contoh 2: Gagal berhenti berlangganan dari pendengar event
Kode salah:
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Berlangganan ke event tetapi tidak pernah berhenti berlangganan
PlayerEventBus.getInstance().subscribe(
PlayerLifecycleEvents.PlayerCreated.class,
event -> updateUI()
);
}
Kode benar:
private PlayerEventBus.EventListener<PlayerLifecycleEvents.PlayerCreated> mListener;
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListener = event -> updateUI();
PlayerEventBus.getInstance().subscribe(
PlayerLifecycleEvents.PlayerCreated.class, mListener);
}
@Overrideprotected void onDestroy() {
super.onDestroy();
// Berhenti berlangganan if (mListener != null) {
PlayerEventBus.getInstance().unsubscribe(
PlayerLifecycleEvents.PlayerCreated.class, mListener);
}
}
Penyebab: Event bus menyimpan referensi ke Activity. Jika pendengar tidak dihapus, Activity tidak dapat dilepas setelah dihancurkan.
Contoh 3: Menggunakan instans pemutar setelah didaur ulang
Kode salah:
// Daur ulang pemutar
strategy.recycle(player, "video_1", false);
// Lanjutkan menggunakan pemutar setelah didaur ulang
player.start(); // Dapat menyebabkan exceptionKode benar:
// Daur ulang pemutar
strategy.recycle(player, "video_1", false);
player = null; // Hapus referensi untuk mencegah penyalahgunaan. Ambil kembali saat diperlukan.
player = strategy.acquire(context, "video_1");Penyebab: Setelah instans pemutar didaur ulang, pemutar tersebut mungkin dihentikan atau dihancurkan. Menggunakannya setelah itu akan menyebabkan exception.
Debugging
Periksa log: Filter Logcat dengan menggunakan
tag:AliPlayerKit.Dengarkan event siklus hidup: Amati event pembuatan, penggunaan ulang, dan eviction pada event bus.
Periksa status pool: Periksa ukuran pool saat ini dan jumlah instans aktif melalui log.
Analisis memori: Gunakan Android Profiler untuk memeriksa jumlah instans pemutar dan jejak memorinya.