คุณสามารถเล่นสื่อในเบื้องหลังได้แม้ว่าแอปพลิเคชันจะไม่ได้อยู่บนหน้าจอ เช่น ขณะที่ผู้ใช้โต้ตอบกับแอปพลิเคชันอื่นๆ
โดยคุณจะฝัง MediaPlayer ในMediaBrowserServiceCompat
บริการและให้โต้ตอบกับ MediaBrowserCompat ในกิจกรรมอื่น
โปรดระมัดระวังในการติดตั้งใช้งานการตั้งค่าไคลเอ็นต์และเซิร์ฟเวอร์นี้ มีข้อกำหนดเกี่ยวกับวิธีที่ผู้เล่นที่ทำงานในบริการที่ทำงานอยู่เบื้องหลังโต้ตอบกับส่วนอื่นๆ ของระบบ หากแอปพลิเคชันไม่เป็นไปตามความคาดหวังเหล่านั้น ผู้ใช้อาจได้รับประสบการณ์การใช้งานที่ไม่ดี ดูรายละเอียดได้ที่การสร้างแอปเสียง
หน้านี้จะอธิบายวิธีการพิเศษในการจัดการ MediaPlayer เมื่อคุณ ติดตั้งใช้งานภายในบริการ
เรียกใช้แบบไม่พร้อมกัน
เช่นเดียวกับActivity งานทั้งหมดในServiceจะดำเนินการในชุดข้อความเดียวโดยค่าเริ่มต้น
ในความเป็นจริง เมื่อคุณเรียกใช้กิจกรรมและบริการจากแอปพลิเคชันเดียวกัน
โดยค่าเริ่มต้น กิจกรรมและบริการจะใช้เธรดเดียวกัน ("เธรดหลัก")
บริการต้องประมวลผล 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
เมื่อเล่นหรือสตรีมเพลงในเบื้องหลัง คุณต้องใช้การล็อกเพื่อป้องกันไม่ให้ระบบรบกวนการเล่น เช่น โดยการทำให้อุปกรณ์เข้าสู่โหมดสลีป
Wake Lock เป็นสัญญาณที่ส่งไปยังระบบว่าแอปพลิเคชันของคุณกำลังใช้ฟีเจอร์ที่ควรพร้อมใช้งานแม้ว่าโทรศัพท์จะไม่มีการใช้งานอยู่ก็ตาม
หากต้องการให้ CPU ทำงานต่อไปขณะที่ MediaPlayer กำลังเล่น ให้เรียกใช้เมธอด setWakeMode() เมื่อเริ่มต้น 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 เป็นโซลูชันที่แนะนำสำหรับการเล่นสื่อในแอป อ่านข้อมูลเพิ่มเติมเกี่ยวกับโซลูชันนี้
หน้าเหล่านี้ครอบคลุมหัวข้อที่เกี่ยวข้องกับการบันทึก จัดเก็บ และเล่นเสียง และวิดีโอ