我有一个名为MusicPlayer的Activity类和一个名为MpPlayer的外部类。在后者中,我的目标是从MusicPlayer更改布局。但是,即使我将上下文从MusicPlayer传递给MpPlayer,只要MpPlayer类尝试访问布局,该应用程序仍将继续返回java.lang.NullPointerException。
我已经清洗了很多代码,因此很容易读取问题。
我的主要类(class)观点:

 open class MusicPlayer : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_musicplayer)

            mp = MpPlayer(this@MusicPlayer)
            mp.loadMusic //error here
    }
}
我的第二堂课:
class MpPlayer(private var playerReference: MusicPlayer){

    init{
        playerReference = MusicPlayer()
    }

    fun loadMusic(url: String){
            musicPlayer.apply {
                reset()
                setDataSource(url)
                prepareAsync()
                setOnPreparedListener { mp ->
                    totaltime = mp.duration
                    playerReference.seekBar.max = totaltime         //error here
                    playerReference.txt_musicName.text = playerReference.musicNameList[musicCounter]
                    playerReference.txt_musicArtist.text = playerReference.musicArtistList[musicCounter]
                    playerReference.btn_musicplayPause.setImageResource(R.drawable.ic_play)

                    if (wanting2Play) {
                        mp.start()
                        playerReference.btn_musicplayPause.setImageResource(R.drawable.ic_pause)
                    }

                }
                setOnCompletionListener { skipNext() }
            }
        }
}
Logcat:
2020-07-13 17:27:11.278 21742-21742/com.android.slowfy E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.slowfy, PID: 21742
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
        at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:159)
        at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:157)
        at android.content.Context.obtainStyledAttributes(Context.java:675)
        at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:692)
        at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:659)
        at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:479)
        at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:214)
        at com.android.slowfy.MpPlayer$loadMusic$$inlined$apply$lambda$1.onPrepared(MpPlayer.kt:41)
        at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:3367)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-07-13 17:32:11.329 21742-21775/com.android.slowfy E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19
2020-07-13 17:32:11.330 21742-21775/com.android.slowfy E/EGL_emulation: tid 21775: swapBuffers(570): error 0x300d (EGL_BAD_SURFACE)
任何对此的想法将不胜感激:)

最佳答案

init块中,您已使用新实例化的 Activity 覆盖了传入的Activity:

class MpPlayer(private var playerReference: MusicPlayer){

    init{
        playerReference = MusicPlayer()
    }
首先,删除此init块。请注意,永远不要直接实例化 Activity ,因为 Activity 没有正确设置为能够使用其上下文。另外,您需要允许Activity在关闭时将其自身作为侦听器删除,因此使该属性可为空,并在该Activity被销毁时将其设置为null。否则,您的MPPlayer类可能会在Activity已经消失时尝试更新 View ,并且还会泄漏Activity。
class MpPlayer(var playerReference: MusicPlayer?){
open class MusicPlayer : AppCompatActivity() {
    //...
    override fun onDestroy() {
        super.onDestroy()
        mp.playerReference = null
    }
}
我还将注意到,将Activity传递给音乐播放器对封装的使用很差。当音乐播放器应该能够在不了解View类的任何知识的情况下独立运行时,您就像意大利面条一样紧密地结合了这两个类。我建议为各种MusicPlayer事件创建一个侦听器类,并让Activity实现它们,如下所示:
class MpPlayer(var eventListener: EventListener?){

    interface EventListener {
        fun onPlayerPrepared(mediaPlayer: MediaPlayer)
        fun onPlaybackCompleted(mediaPlayer: MediaPlayer)
        //etc. anything views in Activity might want to respond to
    }

    fun loadMusic(url: String){
            musicPlayer.apply {
                reset()
                setDataSource(url)
                prepareAsync()
                setOnPreparedListener { mp ->
                    eventListener?.onPlayerPrepared(mp)
                }
                setOnCompletionListener {
                    eventListener?.onPlaybackCompleted(mp)
                    skipNext()
                }
            }
        }
}
我还建议在任何Activity类的名称上都加上“Activity”一词,以免造成混淆。
class MusicPlayerActivity : AppCompatActivity(), MpPlayer.EventListener {

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_musicplayer)

            mp = MpPlayer(this)
            mp.loadMusic()
    }

    override fun onPlayerPrepared(mediaPlayer: MediaPlayer){
        totaltime = mp.duration
        seekBar.max = totaltime
        txt_musicName.text = playerReference.musicNameList[musicCounter]
        txt_musicArtist.text = playerReference.musicArtistList[musicCounter]
        btn_musicplayPause.setImageResource(R.drawable.ic_play)
        //...
    }

    override fun onPlaybackCompleted(mediaPlayer: MediaPlayer) {
        //...
    }

    override fun onDestroy() {
        super.onDestroy()
        mp.eventListener = null
    }
}

10-04 22:11
查看更多