Android (Kotlin) - 无法从另一个 class 访问视图
Android (Kotlin) - Cannot access view from another class
我有一个名为 MusicPlayer 的 Activity Class 和一个名为 MpPlayer 的外部 class。在后者中,我的目标是更改 MusicPlayer 的布局。但是,即使我将上下文从 MusicPlayer 传递到 MpPlayer,只要 MpPlayer class 尝试访问布局,应用程序就会继续 return a 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:
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.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:
class MpPlayer(private var playerReference: MusicPlayer){
init{
playerReference = MusicPlayer()
}
首先,删除这个 init
块。请注意,永远不要直接实例化活动,因为这样它们就无法正确设置,无法使用它们的上下文。此外,您需要允许 Activity 在关闭时将自身作为侦听器移除,因此使 属性 可为空并在 Activity 被销毁时将其设置为空。否则,您的 MPPlayer class 可能会在 Activity 已经消失时尝试更新视图,并且它也会泄漏 Activity.
class MpPlayer(var playerReference: MusicPlayer?){
open class MusicPlayer : AppCompatActivity() {
//...
override fun onDestroy() {
super.onDestroy()
mp.playerReference = null
}
}
我还会注意到,将 Activity 传递给音乐播放器是对封装的不良使用。您将这两个 class 紧密结合在一起,就像意大利面条一样,而音乐播放器应该能够在不了解 View class 的情况下独立运行。我建议为各种 MusicPlayer 事件创建一个侦听器 class 并让 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 class 的名称中加上“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
}
}
我有一个名为 MusicPlayer 的 Activity Class 和一个名为 MpPlayer 的外部 class。在后者中,我的目标是更改 MusicPlayer 的布局。但是,即使我将上下文从 MusicPlayer 传递到 MpPlayer,只要 MpPlayer class 尝试访问布局,应用程序就会继续 return a 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:
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.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:
class MpPlayer(private var playerReference: MusicPlayer){
init{
playerReference = MusicPlayer()
}
首先,删除这个 init
块。请注意,永远不要直接实例化活动,因为这样它们就无法正确设置,无法使用它们的上下文。此外,您需要允许 Activity 在关闭时将自身作为侦听器移除,因此使 属性 可为空并在 Activity 被销毁时将其设置为空。否则,您的 MPPlayer class 可能会在 Activity 已经消失时尝试更新视图,并且它也会泄漏 Activity.
class MpPlayer(var playerReference: MusicPlayer?){
open class MusicPlayer : AppCompatActivity() {
//...
override fun onDestroy() {
super.onDestroy()
mp.playerReference = null
}
}
我还会注意到,将 Activity 传递给音乐播放器是对封装的不良使用。您将这两个 class 紧密结合在一起,就像意大利面条一样,而音乐播放器应该能够在不了解 View class 的情况下独立运行。我建议为各种 MusicPlayer 事件创建一个侦听器 class 并让 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 class 的名称中加上“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
}
}