คุณสามารถเล่นสื่อในเบื้องหลังได้แม้ว่าแอปพลิเคชันจะไม่แสดงบนหน้าจอ เช่น ขณะที่ผู้ใช้โต้ตอบกับแอปพลิเคชันอื่นๆ
โดยคุณฝัง MediaPlayer ในบริการ MediaBrowserServiceCompat
และทําให้โต้ตอบกับ MediaBrowserCompat
ในกิจกรรมอื่น
โปรดระมัดระวังในการใช้การตั้งค่าไคลเอ็นต์และเซิร์ฟเวอร์นี้ ผู้ใช้คาดหวังให้โปรแกรมเล่นที่ทำงานในบริการเบื้องหลังโต้ตอบกับระบบส่วนอื่นๆ อย่างไร หากแอปพลิเคชันของคุณไม่เป็นไปตามความคาดหวังดังกล่าว ผู้ใช้อาจได้รับประสบการณ์การใช้งานที่ไม่ดี โปรดดูรายละเอียดที่หัวข้อการสร้างแอปเสียง
หน้านี้จะอธิบายวิธีการพิเศษในการจัดการ MediaPlayer เมื่อคุณติดตั้งใช้งานภายในบริการ
ทำงานแบบไม่พร้อมกัน
เช่นเดียวกับ Activity
งานทั้งหมดใน Service
จะทําในชุดข้อความเดียวโดยค่าเริ่มต้น อันที่จริงแล้ว เมื่อคุณเรียกใช้กิจกรรมและบริการจากแอปพลิเคชันเดียวกัน กิจกรรมและบริการเหล่านั้นจะใช้เธรดเดียวกัน ("เธรดหลัก") โดยค่าเริ่มต้น
บริการต้องประมวลผล Intent ที่เข้ามาอย่างรวดเร็วและไม่ควรทำการคำนวณที่ใช้เวลานานเมื่อตอบสนองต่อ Intent คุณต้องทํางานหนักหรือบล็อกการเรียกใช้แบบไม่พร้อมกันจากเธรดอื่นที่คุณติดตั้งใช้งานเอง หรือใช้สิ่งอํานวยความสะดวกมากมายของเฟรมเวิร์กสําหรับการประมวลผลแบบไม่พร้อมกัน
เช่น เมื่อใช้ MediaPlayer
จากชุดข้อความหลัก คุณควรทำดังนี้
- เรียก
prepareAsync()
แทนprepare()
และ - ใช้
MediaPlayer.OnPreparedListener
เพื่อรับการแจ้งเตือนเมื่อการเตรียมพร้อมเสร็จสมบูรณ์และคุณเริ่มเล่นได้
เช่น
Kotlin
private const val ACTION_PLAY: String = "com.example.action.PLAY"
class MyService: Service(), MediaPlayer.OnPreparedListener {
private var mMediaPlayer: MediaPlayer? = null
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
...
val action: String = intent.action
when(action) {
ACTION_PLAY -> {
mMediaPlayer = ... // initialize it here
mMediaPlayer?.apply {
setOnPreparedListener(this@MyService)
prepareAsync() // prepare async to not block main thread
}
}
}
...
}
/** Called when MediaPlayer is ready */
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
}
Java
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
จัดการข้อผิดพลาดแบบไม่พร้อมกัน
ในการดำเนินการแบบซิงโครนัส ระบบจะส่งสัญญาณข้อผิดพลาดด้วยข้อยกเว้นหรือรหัสข้อผิดพลาด อย่างไรก็ตาม เมื่อใช้ทรัพยากรแบบไม่พร้อมกัน คุณควรแจ้งข้อผิดพลาดให้แอปพลิเคชันทราบอย่างเหมาะสม ในกรณีของ MediaPlayer
ให้ติดตั้งใช้งาน MediaPlayer.OnErrorListener
และตั้งค่าในอินสแตนซ์ MediaPlayer
ดังนี้
Kotlin
class MyService : Service(), MediaPlayer.OnErrorListener {
private var mediaPlayer: MediaPlayer? = null
fun initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer?.setOnErrorListener(this)
}
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
Java
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mediaPlayer;
public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer.setOnErrorListener(this);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
เมื่อเกิดข้อผิดพลาด MediaPlayer
จะเปลี่ยนเป็นสถานะข้อผิดพลาด คุณต้องรีเซ็ตก่อนจึงจะใช้ได้อีกครั้ง โปรดดูรายละเอียดในแผนภาพสถานะแบบเต็มสำหรับคลาส MediaPlayer
ใช้ Wake Lock
เมื่อเล่นหรือสตรีมเพลงในเบื้องหลัง คุณต้องทำงานขณะล็อกเพื่อป้องกันไม่ให้ระบบรบกวนการเล่น เช่น โดยการทำให้อุปกรณ์เข้าสู่โหมดสลีป
การล็อกให้ตื่นคือสัญญาณที่บอกระบบว่าแอปพลิเคชันของคุณกำลังใช้ฟีเจอร์ที่ควรพร้อมใช้งานแม้ในขณะที่โทรศัพท์ไม่ได้ทำงาน
เรียกใช้เมธอด setWakeMode()
เมื่อเริ่มต้นMediaPlayer
เพื่อให้ CPU ทำงานต่อไปขณะที่ MediaPlayer
เล่นอยู่ MediaPlayer
จะยึดล็อกที่ระบุไว้ขณะเล่นและปล่อยล็อกเมื่อหยุดชั่วคราวหรือหยุดเล่น
Kotlin
mediaPlayer = MediaPlayer().apply {
// ... other initialization here ...
setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}
Java
mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
อย่างไรก็ตาม Wake Lock ที่ได้ในตัวอย่างนี้จะตรวจสอบเฉพาะว่า CPU ยังคงทำงานอยู่ หากคุณสตรีมสื่อผ่านเครือข่ายและใช้ Wi-Fi คุณอาจต้องถือครอง WifiLock
ด้วย ซึ่งจะต้องรับและปล่อยด้วยตนเอง ดังนั้น เมื่อเริ่มเตรียม MediaPlayer
ด้วย URL ระยะไกล คุณควรสร้างและรับล็อก Wi-Fi
เช่น
Kotlin
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")
wifiLock.acquire()
Java
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
เมื่อหยุดสื่อชั่วคราวหรือหยุดเล่น หรือเมื่อคุณไม่จำเป็นต้องใช้เครือข่ายอีกต่อไป คุณควรปลดล็อกโดยทำดังนี้
Kotlin
wifiLock.release()
Java
wifiLock.release();
ดำเนินการล้างข้อมูล
ดังที่ได้กล่าวไว้ก่อนหน้านี้ ออบเจ็กต์ MediaPlayer
อาจใช้ทรัพยากรของระบบเป็นจำนวนมาก คุณจึงควรเก็บไว้เฉพาะเท่าที่จําเป็นเท่านั้น และเรียกใช้ release()
เมื่อเสร็จสิ้น คุณควรเรียกใช้เมธอดล้างข้อมูลนี้อย่างชัดเจนแทนที่จะใช้การเก็บขยะของระบบ เนื่องจากอาจใช้เวลาสักครู่ก่อนที่เครื่องมือเก็บขยะจะเรียกใช้MediaPlayer
อีกครั้ง เนื่องจากเครื่องมือนี้คำนึงถึงความต้องการหน่วยความจำเท่านั้น และไม่คำนึงถึงทรัพยากรอื่นๆ ที่เกี่ยวข้องกับสื่อ ดังนั้นในกรณีที่คุณใช้บริการ คุณควรลบล้างเมธอด onDestroy()
เสมอเพื่อให้แน่ใจว่าคุณกำลังเผยแพร่ MediaPlayer
Kotlin
class MyService : Service() {
private var mediaPlayer: MediaPlayer? = null
// ...
override fun onDestroy() {
super.onDestroy()
mediaPlayer?.release()
}
}
Java
public class MyService extends Service {
MediaPlayer mediaPlayer;
// ...
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) mediaPlayer.release();
}
}
คุณควรมองหาโอกาสอื่นๆ ในการเผยแพร่MediaPlayer
เสมอ นอกเหนือจากการเผยแพร่เมื่อมีการปิด ตัวอย่างเช่น หากคาดว่าจะเล่นสื่อไม่ได้เป็นเวลานาน (เช่น หลังจากเสียโฟกัสเสียง) คุณควรปล่อย MediaPlayer
ที่มีอยู่และสร้างใหม่ในภายหลัง ในทางกลับกัน หากคุณคาดว่าจะหยุดเล่นเพียงระยะเวลาสั้นๆ ก็ควรเก็บMediaPlayer
ไว้เพื่อหลีกเลี่ยงการสร้างและเตรียมMediaPlayer
อีกครั้ง
ดูข้อมูลเพิ่มเติม
Jetpack Media3 เป็นโซลูชันที่แนะนำสำหรับการเล่นสื่อในแอป อ่านข้อมูลเพิ่มเติมเกี่ยวกับโซลูชันนี้
หน้าเหล่านี้ครอบคลุมหัวข้อเกี่ยวกับการบันทึก การจัดเก็บ และการเล่นเสียงและวิดีโอ