Какой контекст я использую для загрузки ресурсов в синглтоне? - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть SoundPool, который я хочу играть в разных фрагментах.Таким образом, я загружаю это в пределах одного.Какой контекст мне нужно использовать?

object PingSoundPool {

fun loadpings(note: Int) {

    val context = Application()

    val mAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_GAME)
        .build()

    val mSoundPool = SoundPool.Builder()
        .setMaxStreams(9)
        .setAudioAttributes(mAttributes)
        .build()

    val cping = mSoundPool.load(context, R.raw.cping, 1)
    val dbping = mSoundPool.load(context, R.raw.dbping, 1)
    [...]

    if (note == 0) {}
    if(note == 1)
        mSoundPool.play(cping, 1f, 1f, 1, -1, 1f)
    if(note == 2)
    mSoundPool.play(dbping, 1f, 1f, 1, -1, 1f)
    [...]
    }
}

Если я использую его таким образом, загрузка его в onCreate моей деятельности, например, PingSoundPool.loadPings(0) и доступ к нему в onClickListener с PingSoundPool.loadPings(1) должна работать, не так ли?Во время выполнения я получаю NullPointerExeption, например:

java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.soulfetch2/com.example.soulfetch2.FullscreenActivity}:
 java.lang.NullPointerException: Attempt to invoke virtual method 
'android.content.res.Resources android.content.Context.getResources()' 
on a null object reference

Исключение указывает на строку val cping = mSoundPool.load(context, R.raw.cping, 1) R.raw.файл существует, но как-то не доступенЯ думаю, что я могу использовать неправильный контекст.Или я реализую правильный контекст неправильно.В любом случае, помощь очень ценится.


РЕДАКТИРОВАТЬ:

Первоначальный вопрос был решен, но все еще что-то не так: код, как он перезагружает SoundPool каждый раз, когда он пытается воспроизвестизвук.У кого-нибудь есть хорошая идея, как загрузить его отдельно, чтобы вызовы PingSoundPool(this).loadPings(Int) просто воспроизводили звуки и не перезагружали все?

Другое дело: когда я выполняю PingSoundPool(this).loadPings(Int) из действия, все работаетЧто ж.Однако из фрагмента я получаю TypeMismatch "Required: Context, Found: MainFragment".Я могу обойти это с PingSoundPool(this.requireContext()).loadPings(2) или PingSoundPool(this.context!!).loadPings(2), но, похоже, это не лучшая вещь.Любые предложения?

Вот класс, который я использую вместо объекта сейчас:

class PingSoundPool(context: Context) {

    val mAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_GAME)
        .build()

    val mSoundPool = SoundPool.Builder()
        .setMaxStreams(9)
        .setAudioAttributes(mAttributes)
        .build()

    val cping = mSoundPool.load(context, R.raw.cping, 1)
    val dbping = mSoundPool.load(context, R.raw.dbping, 1)

fun loadPings(note: Int) {

    if(note == 1)
        mSoundPool.play(cping, 1f, 1f, 1, -1, 1f)
    if(note == 2)
    mSoundPool.play(dbping, 1f, 1f, 1, -1, 1f)
[...]
}

}

Ответы [ 3 ]

0 голосов
/ 09 февраля 2019

Проблема заключается в этой строке:

val context = Application()

Здесь вы создаете новый Application объект, который вы не должны делать.У вас есть две опции:

  1. Вместо Context сделать PingSoundPool a class вместо параметра конструктора и использовать его вместо:
class PingSoundPool(private val context: Context) {
  ...

  fun loadpings(note: Int) {
    [...]
    val cping = mSoundPool.load(context, R.raw.cping, 1)
    val dbping = mSoundPool.load(context, R.raw.dbping, 1)
    [...]
  }
Сделать этот метод loadPings принять Context в качестве аргумента:
fun loadPings(note: Int, context: Context) {
   [...]
   val cping = mSoundPool.load(context, R.raw.cping, 1)
   val dbping = mSoundPool.load(context, R.raw.dbping, 1)
   [...]
}

Затем для обоих методов 1. и 2. передать Application / Activity / Fragmentвы используете в качестве Context.Лично я предпочитаю метод 1., поскольку он масштабируется лучше, если в будущем вы добавите больше методов, опираясь на Context.

Надеюсь, это поможет!

0 голосов
/ 09 февраля 2019

Если вы вызываете его из onCreate вашей деятельности, почему бы вам не передать также Context в качестве параметра?

Функция будет выглядеть следующим образом:

fun loadPings(context: Context, note: Int) {

    //val context = Application()   //Remove this line

    val cping = mSoundPool.load(context, R.raw.cping, 1)    //Here it's used the parameter context
    val dbping = mSoundPool.load(context, R.raw.dbping, 1)
    [...]
}

И вonCreate вашей деятельности вы называете так:

PingSoundPool.loadPings (this, 0)

EDIT:

Если вы создаете объект PingSoundPool, он не будет перезагружаться каждый раз, когда файлы:так что вы можете сделать это в своей деятельности:

class YourActivity ... {

    companion object {   //So it is accesible from other classes with YourActivity.pingSoundPool
        lateinit var pingSoundPool: PingSoundPool;
    }

    @Override
    ... onCreate(...) {
        pingSoundPool = PingSoundPool(this)
        ...
    }
}

Тогда, если вам нужно воспроизвести звук (я использую, чтобы изменить название функции), вы можете сделать это с помощью

pingSoundPool.load(1)                 // Inside of YourActivity
YourActivity.pingSoundPool.load(1)    // Outside of YourActivity

В этомЯ также решил ваш последний вопрос, но, возможно, вы хотите знать, как передать правильный Context объект из Fragment: в вашем Fragment вы можете объявить Context объект (или YourActivity объект,он является потомком Context) и присваивает ему значение из метода onAttach(..) следующим образом:

class YourFragment ... {

    private lateinit var mContext : Context
    private lateinit var mActivity : YourActivity   // You don't need both

    override fun onAttach(context: Context?) {
        super.onAttach(context)

        mContext = context!!

        if (context is YourActivity)
            mActivity = context
    }
}

Затем внутри своего фрагмента вы можете вызвать PingSoundPool(mContext) (или PingSoundPool(mActivity)).

Обратите внимание, что onAttach вызывается перед любым другим методом обратного вызова, а также перед onCreateView.

Вы также можете получить фрагментконтекст с getContext() (просто context!! в Kotlin), но я думаю, что вышеупомянутое решение лучше и безопаснее.

0 голосов
/ 09 февраля 2019

Вы не должны создавать экземпляр Application как новый Context, вам нужно передать существующий Context в ваш метод:

fun loadpings(note: Int, ctx: Context) {
    ...
    val cping = mSoundPool.load(ctx, R.raw.cping, 1)
}

Если вы вызываете этот метод из Activityпросто передайте this@YourActivity:

PingSoundPool.loadpings(0, this@YourActivity)

или просто

PingSoundPool.loadpings(0, this)
...