我用哪个上下文来加载单例中的资源?

问题描述 投票:0回答:3

我有一个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)一样,并在带有PingSoundPool.loadPings(1)的onClickListener中访问它应该可以工作,不是吗?在运行时,我得到一个像这样的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。 Hay有人知道如何单独加载它,所以PingSoundPool(this).loadPings(Int)的调用只是播放声音而不是重新加载一切?

另一件事:当我从一个活动中做PingSoundPool(this).loadPings(Int)时,一切运作良好。但是,从片段中,我得到一个TypeMismatch“必需: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)
[...]
}

}

android kotlin singleton android-context soundpool
3个回答
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你可以声明一个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()获取Fragment上下文(仅在Kotlin中使用context!!),但我认为上述解决方案更好,更安全。


1
投票

您不应该将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)

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的方法。

希望有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.