Chuqur sho'ng'in: MediaPlayerning eng yaxshi amaliyotlari

Marsela Laskoski surati Unsplash-da

MediaPlayer-dan foydalanish juda aldamchi tuyuladi, ammo murakkablik shunchaki er ostida yashaydi. Masalan, quyidagini yozish vasvasaga tushishi mumkin:

MediaPlayer.create (kontekst, R.raw.cowbell) .start ()

Bu birinchi, ehtimol ikkinchi, uchinchi yoki undan ham ko'proq marta yaxshi ishlaydi. Biroq, har bir yangi MediaPlayer xotira va kodek kabi tizim resurslarini iste'mol qiladi. Bu sizning ilovangiz va, ehtimol, butun qurilmangizning ishlashini yomonlashtirishi mumkin.

Yaxshiyamki, bir nechta oddiy qoidalarga amal qilish orqali MediaPlayer-dan oddiy va xavfsiz tarzda foydalanish mumkin.

Oddiy ish

Eng asosiy masala shundaki, bizda o'ynashni xohlaydigan ovozli fayl, ehtimol xom-ashyo mavjud. Bu holda biz har safar ovoz chiqarishda takrorlanadigan yagona o'yinchi yaratamiz. O'yinchini quyidagicha yaratish kerak:

private val mediaPlayer = MediaPlayer (). apply {
    setOnPreparedListener {start ()}
    setOnCompletionListener {reset ()}
}

Pleyer ikkita tinglovchilar bilan yaratilgan:

  • OnPreparedListener, u pleer tayyorlangandan so'ng avtomatik ravishda o'qiy boshlaydi.
  • Ijro tugashi bilan resurslarni avtomatik ravishda tozalaydigan OnCompletionListener.

Yaratilgan pleer yordamida keyingi qadam resurs identifikatorini oladigan va uni o'ynash uchun MediaPlayer-dan foydalanadigan funktsiyani amalga oshirishdir.

qiziqarli playSound-ni bekor qilish (@RawRes rawResId: Int) {
    val aktivFileDescripttor = context.resources.openRawResourceFd (rawResId)?: qaytish
    mediaPlayer.run {
        qayta o'rnatish()
        to'siq ma'lumotlar
        tayyorgarlikAsync ()
    }
}

Ushbu qisqa usulda biroz sodir bo'lmoqda:

  • Resurs identifikatori AssetFileDescripttorga aylantirilishi kerak, chunki MediaPlayer xom resurslarni o'ynashda foydalanadi. Null tekshiruvi manbaning mavjudligini ta'minlaydi.
  • Qo'ng'iroqni qayta tiklash () qo'ng'iroqni boshlash holatiga o'tkazilishini ta'minlaydi. Bu o'yinchi qanday holatda bo'lishidan qat'i nazar ishlaydi.
  • Pleyer uchun ma'lumot manbasini o'rnating.
  • PreparAsync o'yinchini o'ynashga tayyorlaydi va UIga javoban darhol qaytib keladi. Bu ishlaydi, chunki biriktirilgan OnPreparedListener manba tayyorlangandan so'ng o'ynashni boshlaydi.

Shuni ta'kidlash kerakki, biz pleyerimizga (() chiqishni chaqirmaymiz yoki uni bekor qilamiz. Biz uni qayta ishlatishni xohlaymiz! Shunday qilib, biz o'rniga () chaqiramiz, bu xotira va ishlatilgan kodeklarni bo'shatadi.

Tovushni chaqirish qo'ng'iroq kabi oddiy:

playSound (R.raw.cowbell)

Oddiy!

Ko'proq kovaklar

Bir vaqtning o'zida bitta tovushni ijro etish juda oson, lekin agar siz birinchisini o'ynab turganida boshqa ovoz chiqarmoqchi bo'lsangiz nima bo'ladi? PlaySound () ga bir necha marta qo'ng'iroq qilish, ishlamaydi:

playSound (R.raw.big_cowbell)
playSound (R.raw.small_cowbell)

Bunday holda, R.raw.big_cowbell tayyorgarlikni boshlaydi, lekin ikkinchi qo'ng'iroq biron bir narsa yuz bermasidan oldin pleyerni tiklaydi, shuning uchun faqat R.raw.small_cowbellni eshitasiz.

Va agar biz bir vaqtning o'zida bir nechta tovushlarni birgalikda ijro etishni xohlasak nima bo'ladi? Har biri uchun MediaPlayer yaratishimiz kerak. Buning eng oddiy usuli - faol o'yinchilar ro'yxati. Ehtimol, shunga o'xshash narsa:

sinf MediaPlayers (kontekst: kontekst) {
    private val context: Kontekst = context.applicationContext
    xususiy val-pleerlarInUse = mutableListOf  ()

    shaxsiy qiziqarli buildPlayer () = MediaPlayer (). apply {
        setOnPreparedListener {start ()}
        setOnCompletionListener {
            it.release ()
            gamesInUse - = bu
        }
    }

    qiziqarli playSound-ni bekor qilish (@RawRes rawResId: Int) {
        val aktivFileDescripttor = context.resources.openRawResourceFd (rawResId)?: qaytish
        val mediaPlayer = buildPlayer ()

        mediaPlayer.run {
            gamesInUse + = bu
            setDataSource (propertyFileDescripttor.fileDescripttor, propertyFileDescripttor.startOffset,
                    propertyFileDescripttor.declaredLength)
            tayyorgarlikAsync ()
        }
    }
}

Endi har bir tovushning o'z pleyeri mavjud bo'lib, R.raw.big_cowbell va R.raw.small_cowbellni birgalikda ijro etish mumkin! Perfect!

... Yaxshi, deyarli mukammal. Bizning kodimizda bir vaqtning o'zida ijro etiladigan tovushlar sonini cheklaydigan hech narsa yo'q va MediaPlayerda ishlash uchun xotira va kodeklar bo'lishi kerak. Ular tugagach, MediaPlayer jimgina ishlamaydi, faqat logcat-dagi "E / MediaPlayer: Xato (1, -19)" ni belgilaydi.

MediaPlayerPool-ga kiring

Biz bir vaqtning o'zida bir nechta tovushlarni ijro etishni qo'llab-quvvatlamoqchimiz, ammo xotiradan yoki kodeklardan foydalanishni xohlamaymiz. Bu narsalarni boshqarishning eng yaxshi usuli - bu pleyerlar to'plash va keyin biz ovoz o'ynashni xohlaganimizda ulardan birini tanlash. Bizning kodimizni quyidagicha yangilashimiz mumkin:

class MediaPlayerPool (kontekst: Kontekst, maxStreams: Int) {
    private val context: Kontekst = context.applicationContext

    xususiy val mediaPlayerPool = mutableListOf  () .also {
        uchun (i 0..maxStreams ichida) u + = buildPlayer ()
    }
    xususiy val-pleerlarInUse = mutableListOf  ()

    shaxsiy qiziqarli buildPlayer () = MediaPlayer (). apply {
        setOnPreparedListener {start ()}
        setOnCompletionListener {recyclePlayer (it)}
    }

    / **
     * Agar mavjud bo'lsa, [MediaPlayer] ni qaytaradi,
     * aks holda null.
     * /
    shaxsiy qiziqarli requestPlayer (): MediaPlayer? {
        return if (! mediaPlayerPool.isEmpty ()) {
            mediaPlayerPool.removeAt (0) .also {
                gamesInUse + = bu
            }
        } yana null
    }

    shaxsiy qiziqarli recyclePlayer (mediaPlayer: MediaPlayer) {
        mediaPlayer.reset ()
        gamesInUse - = mediaPlayer
        mediaPlayerPool + = mediaPlayer
    }

    qiziqarli playSound (@ RawRes rawResId: Int) {
        val aktivFileDescripttor = context.resources.openRawResourceFd (rawResId)?: qaytish
        val mediaPlayer = requestPlayer ()?: qaytish

        mediaPlayer.run {
            setDataSource (propertyFileDescripttor.fileDescripttor, propertyFileDescripttor.startOffset,
                    propertyFileDescripttor.declaredLength)
            tayyorgarlikAsync ()
        }
    }
}

Endi bir nechta tovushlar bir vaqtning o'zida o'ynashi mumkin va biz juda ko'p xotirani yoki juda ko'p kodekni ishlatmaslik uchun bir vaqtning o'zida eng ko'p pleyerni nazorat qila olamiz. Biz misollarni qayta ko'rib chiqayotganimiz sababli, axlat yig'ish vositasi o'ynashni tugatgan barcha eski narsalarni tozalashga majbur bo'lmaydi.

Ushbu yondashuvning bir nechta kamchiliklari bor:

  • MaxStreams tovushlari yangragandan so'ng, pleer bo'shatilgunga qadar playSound-ga qo'shimcha qo'ng'iroqlarga e'tibor berilmaydi. Siz shu bilan yangi ovoz chiqarishda ishlatiladigan o'yinchini "o'g'irlash" orqali ishlashingiz mumkin.
  • PlaySound-ni chaqirish va ovozni ijro etish o'rtasida sezilarli kechikish bo'lishi mumkin. MediaPlayer qayta ishlatilayotgan bo'lsa ham, aslida u CN + orqali mahalliy ob'ektni boshqaradigan ingichka o'rash vositasi. MediaPlayer.reset () ni har safar chaqirganingizda, mahalliy pleer yo'q qilinadi va u MediaPlayer tayyor bo'lganda yangilanishi kerak.

O'yinchilarni qayta ishlatish qobiliyatini saqlab qolish bilan kechikishni yaxshilash qiyinroq. Yaxshiyamki, kutish vaqti talab qilinadigan ba'zi turdagi tovushlar va ilovalar uchun keyingi safar yana bir variantni ko'rib chiqamiz: SoundPool.