【问题标题】:Null Pointer Exception received when I'm trying to implement Manual Dependency Injection in Android [duplicate]当我尝试在 Android 中实现手动依赖注入时收到空指针异常 [重复]
【发布时间】:2021-03-15 02:06:24
【问题描述】:

所以我从 Android 开发者documentation 那里学习手动依赖注入,以使用 Room 数据库库实现一个简单的保存机制。但是,它一直在导致 Container 类中的 NullPointerException:

2021-03-15 07:12:08.370 23721-23721/com.arpansircar.Practice.dependencyinjectionpractice E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.arpansircar.Practice.dependencyinjectionpractice, PID: 23721
    java.lang.RuntimeException: Unable to instantiate application com.arpansircar.practice.dependencyinjectionpractice.di.MyApplication: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
        at android.app.LoadedApk.makeApplication(LoadedApk.java:1230)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6583)
        at android.app.ActivityThread.access$1400(ActivityThread.java:227)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1890)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7592)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
            at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:128)
            at com.arpansircar.practice.dependencyinjectionpractice.di.MyApplication.<init>(MyApplication.kt:7)
            at java.lang.Class.newInstance(Native Method)
            at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)
            at androidx.core.app.CoreComponentFactory.instantiateApplication(CoreComponentFactory.java:52)
            at android.app.Instrumentation.newApplication(Instrumentation.java:1156)
            at android.app.LoadedApk.makeApplication(LoadedApk.java:1222)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6583) 
            at android.app.ActivityThread.access$1400(ActivityThread.java:227) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1890) 
            at android.os.Handler.dispatchMessage(Handler.java:107) 
            at android.os.Looper.loop(Looper.java:224) 
            at android.app.ActivityThread.main(ActivityThread.java:7592) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 

以下是我认为可能出现错误的类。

AppContainer.kt

package com.arpansircar.practice.dependencyinjectionpractice.di

import android.content.Context
import com.arpansircar.practice.dependencyinjectionpractice.repository.UserRepository
import com.arpansircar.practice.dependencyinjectionpractice.room.UserDao
import com.arpansircar.practice.dependencyinjectionpractice.room.UserDatabase
import com.arpansircar.practice.dependencyinjectionpractice.viewmodel.UserViewModelFactory

class AppContainer(context: Context) {

    private val userDao: UserDao = UserDatabase.getInstance(context).userDao()

    private val userRepository: UserRepository = UserRepository(userDao)

    val userViewModelFactory: UserViewModelFactory = UserViewModelFactory(userRepository)

}

MyApplication.kt

package com.arpansircar.practice.dependencyinjectionpractice.di

import android.app.Application

class MyApplication : Application() {

    val appContainer = AppContainer(applicationContext)

}

MainActivity.kt

package com.arpansircar.practice.dependencyinjectionpractice.view

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import com.arpansircar.practice.dependencyinjectionpractice.di.AppContainer
import com.arpansircar.practice.dependencyinjectionpractice.di.MyApplication
import com.arpansircar.practice.dependencyinjectionpractice.R

class MainActivity : AppCompatActivity() {

    private var appContainer: AppContainer? = null
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        appContainer = (application as MyApplication).appContainer

        val navHostFragment: NavHostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment_container) as NavHostFragment
        navController = navHostFragment.findNavController()
    }

    override fun onDestroy() {
        super.onDestroy()
        appContainer = null
    }

}

RegistrationFragment.kt

package com.arpansircar.practice.dependencyinjectionpractice.view

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.arpansircar.practice.dependencyinjectionpractice.databinding.FragmentRegistrationBinding
import com.arpansircar.practice.dependencyinjectionpractice.di.MyApplication
import com.arpansircar.practice.dependencyinjectionpractice.room.UserEntity
import com.arpansircar.practice.dependencyinjectionpractice.viewmodel.UserViewModel

class RegistrationFragment : Fragment(), View.OnClickListener {

    private lateinit var binding: FragmentRegistrationBinding
    private lateinit var userViewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val appContainer = (context as MyApplication).appContainer

        userViewModel = ViewModelProvider(
            this,
            appContainer.userViewModelFactory
        ).get(UserViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentRegistrationBinding.inflate(
            inflater,
            container,
            false
        )

        return binding.root
    }

    override fun onStart() {
        super.onStart()
        binding.createAccountButton.setOnClickListener(this)
        binding.getAllAccountsButton.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        when (view) {

            binding.createAccountButton -> {
                val name: String = binding.userNameTextInputEditText.text.toString()
                val userName: String = binding.userUsernameTextInputEditText.text.toString()
                val password: String = binding.userPasswordTextInputEditText.text.toString()

                val userEntity = UserEntity(0, name, userName, password)
                userViewModel.insertUsers(userEntity)
            }

            binding.getAllAccountsButton -> {

            }
        }
    }
}

UserViewModelFactory.kt

package com.arpansircar.practice.dependencyinjectionpractice.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.arpansircar.practice.dependencyinjectionpractice.repository.UserRepository

class UserViewModelFactory(private val userRepository: UserRepository) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            return UserViewModel(userRepository) as T
        }

        throw IllegalArgumentException("Unknown ViewModel Class")
    }

}

UserViewModel.kt

package com.arpansircar.practice.dependencyinjectionpractice.viewmodel

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.arpansircar.practice.dependencyinjectionpractice.repository.UserRepository
import com.arpansircar.practice.dependencyinjectionpractice.room.UserEntity
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch

class UserViewModel(private val userRepository: UserRepository) : ViewModel() {

    fun insertUsers(userEntity: UserEntity) {
        viewModelScope.launch(IO) {
            userRepository.insertUser(userEntity)
        }
    }

    fun getUsers(): LiveData<List<UserEntity>> {
        return userRepository.selectUsers()
    }

}

具体来说,错误指向行号。 7 在 MyApplication.kt 类

val appContainer = AppContainer(applicationContext)

陈述

尝试调用虚方法 'android.content.Contextandroid.content.Context.getApplicationContext()' 在空对象引用上

考虑到我在 MainActivity.kt 类的 onCreate 方法中调用 MyApplication 类,我无法弄清楚上下文如何为空

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        appContainer = (application as MyApplication).appContainer
...
}

在我的 RegistrationFragment.kt 类的 onCreate 方法中

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val appContainer = (context as MyApplication).appContainer

userViewModel = ViewModelProvider(
    this,
    appContainer.userViewModelFactory
).get(UserViewModel::class.java)

}

我的意思是,不应该在这些方法中使用上下文启动和初始化活动和片段吗?

【问题讨论】:

    标签: android kotlin dependency-injection nullpointerexception android-room


    【解决方案1】:
    class MyApplication : Application() {
        val appContainer = AppContainer(applicationContext)
    }
    

    您正试图将您的Application 用作Context 太早,在它完全设置之前的初始化阶段。

    理论上 Application 这里是应用程序上下文,因此您可以将 applicationContext 替换为 this 但这只会将相同的问题转移到您尝试使用 Context 的 AppContainer在初始化阶段。

    考虑推迟Context 的使用。例如,您可以将您的 appContainer 包装在一个 lazy 委托中,该委托在首次访问时而不是在初始化阶段初始化 AppContainer。

    【讨论】:

    • 工作就像一个魅力。虽然我不得不用val appContainer = (requireActivity.applicationcontext as MyApplication).appContainer替换val appContainer = (context as MyApplication).appContainer
    【解决方案2】:

    applicationContext 在应用程序onCreate(); 之前为空

    首先,你可以试试下面的代码:

        class MyApplication : Application() {
        var appContainer: Context? = null
        override fun attachBaseContext(base: Context?) {
            super.attachBaseContext(base)
            Log.d("TAG", "attachBaseContext: " + applicationContext)
        }
    
        override fun onCreate() {
            super.onCreate()
            Log.d("TAG", "onCreate: " + applicationContext)
        }
        }  
    

    可以获得日志:

         D/TAG: attachBaseContext: null
         D/TAG: onCreate: siger.kotest0.MyApplication@6d46e8e   
    

    所以你可以在onCreate():中使用applicationContext

        var appContainer: Context? = null
        override fun onCreate() {
            super.onCreate()
            appContainer = AppContainer(base)
            Log.d("TAG", "onCreate: " + applicationContext)
        }
        } 
    

    【讨论】:

    • 我明白了。考虑到我们自定义的 Application 类继承了 Application,我们也可以在这里轻松地使用生命周期回调方法。当我在 MyApplication 类中为 AppContainer 创建对象时,我是在触发 onCreate() 方法之前这样做的。结果,有一个NullPointerException。明白了。
    猜你喜欢
    • 2018-08-20
    • 1970-01-01
    • 2016-07-14
    • 2014-11-14
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    相关资源
    最近更新 更多