So I was learning Manual Dependency Injection from the Android Developers documentation to implement a simple saving mechanism using the Room database library. However, it has been causing the NullPointerException in the Container class:
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)
Here are the classes where I figure there could be an error.
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()
}
}
Specifically, the error points to line no. 7 in the MyApplication.kt class
val appContainer = AppContainer(applicationContext)
stating
Attempt to invoke virtual method 'android.content.Contextandroid.content.Context.getApplicationContext()' on a null object reference
I can't figure out how the context can be null considering that I'm invoking the MyApplication class in the onCreate method of my MainActivity.kt class
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
appContainer = (application as MyApplication).appContainer
...
}
and in the onCreate method of my RegistrationFragment.kt class
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appContainer = (context as MyApplication).appContainer
userViewModel = ViewModelProvider(
this,
appContainer.userViewModelFactory
).get(UserViewModel::class.java)
}
I mean, shouldn't the activity and fragment been started and initialized with a context within these methods?