01 ตุลาคม 2556

[Android Code] เกร็ดความรู้กับการจัดการกับไฟล์เสียง [Sound Effect]


        บทความนี้จะต่อเนื่องจากบทความ BGM & Effect นะครับ [Android Code] การเพิ่มเสียงประกอบลงใน Application [BGM & Effect] เมื่อเล่นไฟล์เสียงไปหลายครั้งแล้วอยู่ๆเสียงมันก็ดันไม่เล่นซะงั้นนี้

        ปัญหานี้เกิดมาจากการที่คลาส Media Player ที่ใช้เล่นเสียงถูกเรียกใช้ซ้ำเรื่อยๆ โดยไม่ได้มีการเคลียร์ของเก่าทิ้งจึงทำให้เกิดการล้นของบัฟเฟอร์ขึ้น เสียงจึงไม่เล่นและขึ้นข้อความเออเรอร์ที่ LogCat แบบนี้แทน


        ดังนั้นจึงต้องเคลียร์บัฟเฟอร์ทิ้งทุกครั้งเมื่อเล่นไฟล์เสียงเสร็จ

        ทีนี้มาดูโค๊ดที่ใช้กันทั่วไปก่อน เวลาเล่นไฟล์เสียงมักจะใช้คำสั่งดังนี้

MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start();

        ซึ่งแน่นอนว่า คำสั่งเพียงเท่านี้ก็จะทำให้เกิดปัญหาบัฟเฟอร์ล้นได้ เพราะว่าไม่มีการเคลียร์บัฟเฟอร์เมื่อเล่นไฟล์เสียงจบแต่อย่างใด จะใช้คำสั่งเคลียร์บัฟเฟอร์ แต่จะรู้ได้ไงล่ะว่าไฟล์เสียงนั้นๆเล่นจบเมื่อไร? สำหรับ Media Player จะมี Listener สำหรับจับ Event เมื่อเล่นเพลงจบให้แล้ว
MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { } });

        จะเห็นว่ามีฟังก์ชัน onCompletion ที่จะทำงานเมื่อเสียงเล่นจบแล้ว ดังนั้นในฟังก์ชันดังกล่าวนี้ก็สามารถใส่คำสั่งเคลียร์บัฟเฟอร์ได้เลย
MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } });

        จะสังเกตุเห็นว่าคลาส Media Player ที่เจ้าของบล็อกเรียกใช้จะเป็น mp แทนที่จะเรียก mpEffect ทั้งนี้ก็เพราะว่า มันคือตัวเดียวกัน! จึงไม่จำเป็นต้องไปเรียกใช้งานผ่าน mpEffect เลย ผลลัพธ์ก็คือ mpEffect จะทำการ Release นั่นแหละ เพราะมันคือตัวเดียวกัน

        ดังนั้นก็จะสรุปคร่าวๆได้ว่า เมื่อเล่นไฟล์เสียงเพลงแล้ว จะสร้าง Listener คอยเช็คว่าเล่นไฟล์เสียงจบแล้วหรือยัง ถ้าเล่นจนจบแล้วก็จะทำการ Release เพื่อเคลียร์บัฟเฟอร์ จึงทำให้หมดปัญหาเรื่องกดเล่นเสียงหลายๆครั้งแล้วเสียงหาย

        ดังนั้นโค๊ดโดยรวมก็จะประมาณนี้
public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } }
        โค๊ดอันนี้ก็เข้าใจได้ไม่ยากเลย ก็คือกดปุ่มแล้วมีเสียงสุดคลาสสิค แต่ทีนี้ก็ยังมีปัญหาหนึ่งคือ ถ้าไฟล์เสียงเป็นเสียงสั้นๆก็ไม่เป็นไรหรอก ถ้าเป็นเสียงที่มีความยาวในระดับนึงล่ะ? ลองเอาไปทดสอบดูได้ ระหว่างนั้นถ้ากดย่อแอพหรือปิดแอพเสียงก็จะยังไม่หยุดเล่น ดังนั้นจึงควรแก้ไขโค๊ดให้เหมาะสมกว่านี้กันหน่อยละกันเนอะ

        อย่างแรกเลยก็คือ ให้เสียงหยุดเล่นเมื่อเกิด onStop ขึ้น นั่นก็คือ เมื่อแอปพลิเคชันหยุดทำงานหรือถูกปิดนั่นเอง
public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); // หยุดเล่นไฟล์เสียงตรงนี้ } }

ก็จะทำให้ไฟล์เสียงหยุดเล่นเมื่อปิดหรือย่อแอพลง
แต่ปัญหาคือจะเรียกใช้ mpEffect ได้ยังไงล่ะ ?
เพราะมันถูกประกาศและเรียกใช้ใน onClick
ซึ่งไม่สามารถเข้าถึงจาก onStop ได้เลยนะ

คำตอบง่ายๆเลยก็คือ ก็ประกาศมันเป็น Global สิ
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); // หยุดเล่นไฟล์เสียงตรงนี้ } }

        วิธีแก้ปัญหาสุดแสนจะธรรมดา ประกาศไว้ข้างนอกสุดเลย เพื่อให้ทุกฟังก์ชันสามารถเรียกใช้งาน mpEffect ได้ ทีนี้ใน onStop ก็สามารถเรียกใช้คำสั่งหยุดเล่นเสียงได้
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); mpEffect.stop(); mpEffect.release(); } }

        แค่นี้ก็สามารถใช้คำสั่งหยุดเล่นเพลงและเคลียร์บัฟเฟอร์ทิ้งได้แล้ว จากโค๊ดนี้อยากให้เอาไปลองกันดู เพราะมันจะเกิดเออเรอร์ได้อีกกรณี นั่นก็คือยังไม่ได้กดเล่นเสียงแล้วกดออกทันที ทำให้เกิด NullPointerException ซึ่งเกิดมาจากการที่ใช้คำสั่งหยุดเล่นเสียงและเคลียร์บัฟเฟอร์ ทั้งๆที่ยังไม่ได้กำหนดค่าให้กับ mpEffect เลย (กำหนดค่าก็ต่อเมื่อกดปุ่ม) ดังนั้นถ้ายังไม่ได้กดปุ่มเล่นเสียงแล้วกดออกทันที ก็จะทำให้เออเรอร์

        จะแก้ปัญหานี้ยังไงดีล่ะ? อันนี้ก็ขึ้นอยู่กับวิธีของแต่ละคน ในกรณีนี้เจ้าของบล็อกใช้เสียงแค่อันเดียว ไม่ได้ใช้งานกับเสียงอื่นๆ ก็ใช้วิธีกำหนดค่าให้กับ mpEffect ตั้งแต่ onCreate กันไปเลย
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); mpEffect.stop(); mpEffect.release(); } }

        เท่านี้ก็เรียบร้อย เวลากดปุ่มเสียงก็ทำงานแค่คำสั่ง Start ไปเลย ไม่ต้องมานั่งกำหนดค่า mpEffect ซ้ำซ้อนให้เสียเวลา เพราะยังไงก็ใช้เสียงเดิม และนอกจากนี้วิธีนี้ยังแก้ปัญหาเสียงเพลงเล่นซ้อนกันเมื่อกดเล่นรัวๆได้ด้วยนะ โดยกดซ้ำก็จะไม่เล่นซ้อนกับเสียงเดิม จนกว่าเสียงเดิมจะเล่นจนจบ

        แต่ถึงกระนั้น ปัญหาก็ยังไม่หมด นั่นก็คือ คำสั่งใน onStop นั่นเอง จะเห็นว่าใช้คำสั่งให้หยุดเล่นเสียงและเคลียร์บัฟเฟอร์ แต่ลองคิดตามดู คำสั่งดังกล่าวนี้จะใช้เพื่ออะไร? ใช้เพื่อหยุดเล่นเสียงเพลง เพื่อไม่ให้เสียงเพลงเล่นค้างไว้ ดังนั้นคำสั่งนี้ ควรจะทำงานเมื่อเสียงเพลงเล่นอยู่เท่านั้น เพราะคำสั่งจะทำงานตลอดเวลา ถึงแม้ว่าเสียงจะเล่นอยู่หรือไม่ก็ตาม ดังนั้นในการใช้คำสั่งนี้ใน onStop ควรมีการเช็คด้วยว่าเสียงเล่นอยู่หรือไม่
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); if(mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } }

        วิธีแก้ปัญหาง่ายๆเลยก็คือ ก็ใช้ If เช็คไปสิว่าเสียงเล่นอยู่หรือไม่ ถ้าเล่นอยู่ก็ค่อยให้ใช้คำสั่งดังกล่าวแทน แต่ก็ยังคงมีเออเรอร์อยู่อีกนะ คือคำสั่ง isPlaying() ไม่สามารถทำงานได้เมื่อ Media Player ถูก Release แล้ว ดังนั้นเพื่อแก้ปัญหาดังกล่าวเจ้าของบล็อกจึงใช้ Try-Catch ซะเลย
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); Button buttonSound = (Button)findViewById(R.id.buttonSound); buttonSound.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); try { if(mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } }

        เพื่อป้องกันกรณีดังกล่าว ซึ่งจะเป็นเออเรอร์แบบ IllegalStateException เพียงเท่านั้นก็เป็นอันเสร็จเรียบร้อยสำหรับการจัดการปัญหาต่างๆ

        แล้วทีนี้สำหรับกรณีที่มีเสียงเพลงมากกว่าหนึ่งล่ะ? จะสร้างตัวแปรแยกหลายๆตัวก็ได้ แต่ก็ดูไม่ค่อยเหมาะซักเท่าไร นึกเล่นๆว่าถ้ามีซัก 10 เสียง ก็ต้องสร้างมา 10 ตัว ซึ่งเปลืองไม่ใช่น้อย ดังนั้นถ้าเอามาใช้ร่วมกันในตัวแปรตัวเดียวไปเลยก็น่าจะเวิร์กกว่า

        ซึ่งจากโค๊ดเดิมนั้นก็ไม่ยากอะไรแล้ว ต่างจากเดิมแค่ว่า กำหนดค่าให้กับ mpEffect ใน onClick ของ Button แต่ละตัวแทน
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_01); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_02); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_03); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_04); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); try { if(mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } }

        ดังนั้น mpEffect ก็จะใช้ร่วมกัน โดยสามารถกดให้เล่นเสียงซ้อนกันได้ แต่ทีนี้ให้มาโฟกัสกันที่ onStop กันต่อ สำหรับกรณีเสียงเพลงที่มากกว่าหนึ่ง
public void onStop() { super.onStop(); try { if(mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } }
        เนื่องจากกรณีไฟล์เสียงมากกว่าหนึ่ง จะกำหนดค่าตัวแปร mpEffect ก็ต่อเมื่อกดปุ่ม Button ใดๆแล้วเท่านั้น ดังนั้นถ้ายังไม่กดล่ะ? mpEffect ก็จะมีค่าเป็น Null อยู่ คือยังไม่มีการกำหนดค่า แล้วถ้าผู้ใช้ยังไม่ได้กดเล่นเสียงใดๆแล้วกดออกทันทีล่ะ? ให้สังเกตุตรงคำสั่ง mpEffect.isPlaying() ซึ่งตรงนี้ล่ะจะเออเรอร์ เพราะว่า mpEffect เป็น Null อยู่ จึงไม่สามารถเรียกใช้คำสั่งได้ ดังนั้นจึงต้องมีการเช็คก่อนว่า mpEffect เป็น Null หรือไม่ ถ้าเป็น Null คำสั่ง isPlaying ก็จะไม่ทำงาน ทำให้ไม่เกิดเออเรอร์
public void onStop() { super.onStop(); try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } }

        สรุปคำสั่งทั้งหมดสำหรับการเล่นไฟล์เสียงหลายไฟล์ จะได้ดังนี้
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_01); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_02); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_03); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect_04); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }); } public void onStop() { super.onStop(); try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } }

        แต่โค๊ดมันยาวไปหน่อย แถมคำสั่งใน onClick ก็คล้ายๆกัน ถ้างั้นทำเป็นฟังก์ชันไปเลยดีกว่าจะได้สั้นลง ดูง่ายขึ้น
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_01); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_02); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_03); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_04); } }); } public void onStop() { super.onStop(); try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } public void playSound(Context context, int resId) { mpEffect = MediaPlayer.create(context, resId); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }

        เท่านี้ก็สั้นลงแล้ว แต่ทว่าโค๊ดตัวนี้เวลากดเล่นหลายๆครั้งรัวๆ เสียงมันจะเล่นซ้อนกัน ถ้าไม่ต้องการให้เสียงเล่นซ้อนกันก็ต้องทำการเช็คก่อน เมื่อผู้ใช้ onClick ที่ปุ่มใดๆ เช็คก่อนว่า MediaPlayer เล่นอยู่หรือไม่ ถ้ากำลังเล่นอยู่ก็ให้หยุดเสียงที่เล่นอยู่ก่อนแล้วจึงเล่นเสียงที่ผู้ใช้กด
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } playSound(Main.this, R.raw.sound_effect_01); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } playSound(Main.this, R.raw.sound_effect_02); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } playSound(Main.this, R.raw.sound_effect_03); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } playSound(Main.this, R.raw.sound_effect_04); } }); } public void onStop() { super.onStop(); try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } public void playSound(Context context, int resId) { mpEffect = MediaPlayer.create(context, resId); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }
        จะเห็นว่าโค๊ดเช็คว่าไฟล์เพลงเล่นอยู่หรือไม่ ถ้าเล่นอยู่ก็หยุดก่อน นี่มันคือโค๊ดเช็คเหมือนกันใน onStop เป๊ะๆเลย (ก็แหงดิ เช็คเหมือนกัน)

        แต่โค๊ดยาวขึ้นอีกแล้ว ต้องทำให้สั้นลงแล้วล่ะ
public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_01); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_02); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_03); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_04); } }); } public void onStop() { super.onStop(); try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } public void playSound(Context context, int resId) { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } mpEffect = MediaPlayer.create(context, resId); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } }
        กรณีแรกคือ ก็เอาคำสั่งที่ว่ามาใส่ในฟังก์ชัน playSound เลยสิ เพราะเล่นเพลงทุกครั้งต้องมีการเช็คอยู่แล้วว่ามีไฟล์อื่นเล่นอยู่หรือป่าว

        แต่ยังไม่พอ อยากได้สั้นกว่านี้อีกนิดนึง จะเห็นว่า การเช็คว่าไฟล์เสียงอื่นๆเล่นอยู่หรือป่าว เหมือนกับใน onStop เพราะงั้นจึงรวบเฉพาะโค๊ดเช็คเงื่อนไขดังกล่าวเป็นอีกฟังก์ชันได้
package app.akexorcist.soundmanagement; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.app.Activity; import android.content.Context; public class Main extends Activity { MediaPlayer mpEffect; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button buttonSound1 = (Button)findViewById(R.id.buttonSound1); buttonSound1.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_01); } }); Button buttonSound2 = (Button)findViewById(R.id.buttonSound2); buttonSound2.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_02); } }); Button buttonSound3 = (Button)findViewById(R.id.buttonSound3); buttonSound3.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_03); } }); Button buttonSound4 = (Button)findViewById(R.id.buttonSound4); buttonSound4.setOnClickListener(new OnClickListener() { public void onClick(View v) { playSound(Main.this, R.raw.sound_effect_04); } }); } public void onStop() { super.onStop(); stopIfPlating(); } public void playSound(Context context, int resId) { stopIfPlating(); mpEffect = MediaPlayer.create(context, resId); mpEffect.start(); mpEffect.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mp.release(); } }); } public void stopIfPlating() { try { if(mpEffect != null && mpEffect.isPlaying()) { mpEffect.stop(); mpEffect.release(); } } catch (IllegalStateException e) { e.printStackTrace(); } } }

        ก็เรียบร้อยแล้ว สำหรับเล่นเสียงแบบไม่ให้ซ้ำซ้อนกัน

        ก็จะเห็นว่า การใช้งานไฟล์เสียง ใช่ว่าเรียกใช้งานแล้วเสียงเล่นได้ แล้วจะปล่อยทิ้งไว้แบบนั้นเฉยๆเลย ควรจะมีการจัดการที่เหมาะสมด้วย ซึ่งวิธีก็จะแตกต่างกันไปตามกรณีการใช้งาน เพื่อป้องกันเออเรอร์ใดๆ

        สำหรับผู้ที่หลงเข้ามาอ่านคนไหนที่ต้องการไฟล์ตัวอย่าง สามารถดาวน์โหลดได้จาก Sound Management [Google Drive]



เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium