我在哪个环境中加载单个资源?

时间:2019-02-09 09:42:15

标签: android kotlin singleton android-context soundpool

我有一个想要在不同片段中播放的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)
    [...]
    }
}

如果我这样使用它,可以像这样PingSoundPool.loadPings(0)将其加载到活动的onCreate中,并使用PingSoundPool.loadPings(1)在onClickListener中对其进行访问,不是吗? 在运行时,我得到了一个N​​ullPointerExeption:

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。 Hay有人知道如何分别加载它,这样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 :(得分:1)

您不应将SESSION的实例创建为新的USER_ID,而需要将现有的Application传递给您的方法:

Context

如果您从Activity中调用该方法,只需传递Context

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

或者只是

this@YourActivity

答案 1 :(得分:1)

问题出在这一行:

val context = Application()

在这里,您正在创建一个不应使用的新Application对象。您在这里有两个选择:

  1. PingSoundPool设为class,而将Context作为构造函数参数,并改为使用它:
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)
    [...]
  }
  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的方法。

希望有帮助!

答案 2 :(得分:1)

如果您是从活动的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)
    [...]
}

在Activity的onCreate中,您可以通过以下方式调用它:

PingSoundPool.loadPings (this, 0)

编辑:

如果创建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中,您可以声明一个{ {1}}对象(或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(在Kotlin中仅为getContext())获得Fragment上下文,但是我认为上述解决方案更好,更安全。