当我尝试在 Android 中实现手动依赖注入时收到空指针异常



所以我从Android开发者文档中学习了手动依赖注入,以使用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)
}

主活动.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()
}
}

具体来说,错误指向MyApplication.kt类中的第7行

val appContainer = AppContainer(applicationContext)

说明

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

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

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

以及在myRegistrationFragment.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)

}

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

class MyApplication : Application() {
val appContainer = AppContainer(applicationContext)
}

您试图过早地将Application用作Context,在初始化阶段,它还没有完全设置好。

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

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

applicationContext将为空,直到应用程序onCreate();

Fisrt,你可以试试下面的代码:

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)
}
} 

相关内容

  • 没有找到相关文章

最新更新