У меня есть интерфейс, который передается в модель представления из действия, в котором он реализован. Затем в модели представления он вызывает реализованный метод интерфейса. Внутри этого метода все, что я хочу сделать, это отобразить Toast, но я получаю нулевую ошибку при попытке получить доступ к контексту активности. Я не понимаю почему. Для ясности: внутри всех переопределенных методов, таких как onCreate, onStop, OnResume и т. д. Если я показываю всплывающее уведомление, оно работает нормально. Только когда внутри этого метода интерфейса контекст активности кажется нулевым. Я понятия не имею.
Это модуль рукояти
@Module
@InstallIn(SingletonComponent::class)
abstract class ToastListenerModule {
@Binds
abstract fun bindToastMessageListener(activity: SplashActivity): ToastMessageListener
}
Мой SplashActivity.kt
@AndroidEntryPoint
class SplashActivity @Inject constructor() : AppCompatActivity(), ToastMessageListener {
private lateinit var binding: ActivitySplashBinding
@Inject
lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun showToast(message: String) {
//THIS IS WHERE THE PROBLEM IS WITH THE CONTEXT
Toast.makeText(this, "MESSAGE", Toast.LENGTH_SHORT)
}
}
Ниже у меня есть ViewModel:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
private val auth: FirebaseAuth,
private val toastListner: ToastMessageListener,
) : ViewModel() {
val emailText = ObservableField<String>("")
val passwordText = ObservableField<String>("")
val nameText = ObservableField<String>("")
fun onSubmitClicked(isLogin: Boolean) {
if (isLogin) {
val email = emailText.get()
val password = passwordText.get()
if (email.isNullOrEmpty() || password.isNullOrEmpty()) {
println("Email or password is empty")
return
}
auth.signInWithEmailAndPassword(emailText.get() ?: "", passwordText.get() ?: "")
.addOnCompleteListener() { task ->
if (task.isSuccessful) {
toastListner.showToast("Login Successful")
} else {
val exception = task.exception
//THIS IS WHERE I CALL showToast
//AND YES IT IS GETTING CALLED AND EVERYTHING
toastListner.showToast("Login Failed: ${exception?.message}")
}
}
} else {
println("SIGN Up BUTTON CLICKED ${emailText.get()} ${passwordText.get()}")
}
}
fun isValidInput(): Boolean {
val inputEm = emailText.get() ?: return false
val inputPass = passwordText.get() ?: return false
return inputEm.isNotEmpty() && inputPass.isNotEmpty()
}
}
Моя ОШИБКА
FATAL EXCEPTION: main
Process: com.example.findroomies, PID: 27661
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object referenceat android.content.ContextWrapper.getPackageName(ContextWrapper.java:173)
at android.widget.Toast.<init>(Toast.java:174)
at android.widget.Toast.makeText(Toast.java:498)
at android.widget.Toast.makeText(Toast.java:487)
at com.example.findroomies.ui.activities.SplashActivity.showToast(SplashActivity.kt:52)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel.onSubmitClicked$lambda$0(AuthenticationViewModel.kt:35)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel.$r8$lambda$b1GN3_M-m0Ij9SZCrkTUOa2z6PY(Unknown Source:0)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel$$ExternalSyntheticLambda0.onComplete(Unknown Source:2)
at com.google.android.gms.tasks.zzi.run(com.google.android.gms:play-services-tasks@@18.0.2:1)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
@Laser Я добавил для вас журнал ошибок, если это поможет
Прежде всего, вы сказали, что у вас проблема в контексте. Таким образом, вы можете решить эту проблему, внедрив контекст приложения в класс viewModel, а затем передав контекст функции в качестве аргумента.
Вот пример для справки. Но все же нужно знать точную причину nullPointerException. Я думаю, вам стоит поделиться журналами ошибок.
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
private val auth: FirebaseAuth,
private val toastListner: ToastMessageListener,
@ApplicationContext private val context: Context
) : ViewModel() {
Затем в своей деятельности вы можете добавить контекст в качестве аргумента.
override fun showToast(context : Context, message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
Точно так же вы можете передать этот контекст в класс viewModel.
toastListner.showToast(context,"Login Failed: ${exception?.message}")
Когда я перемещаю Toast в свой фрагмент, он говорит, что фрагмент не прикреплен к действию. Может ли у меня возникнуть более серьезная проблема? Как можно фрагмент не прикрепить к действию? Возможно, я мог бы решить эту проблему с помощью этого, но можете ли вы помочь мне найти проблему с моим кодом?
На самом деле вы передали контекст по умолчанию функции всплывающего уведомления в splashActivity
, но там нет экземпляра viewModel. Итак, я предполагаю, что вы подключили viewModel к другому фрагменту или действию. Теперь, когда вы вызываете из viewModel, всплывающее сообщение не получает контекст фрагмента или действия, в котором вы находитесь, поскольку вы передали туда контекст по умолчанию.
более того, почему вам нужен интерфейс для отображения тостов? вы можете просто создать функцию расширения и использовать ее где угодно.
Спасибо за объяснение и предложение, думаю, теперь я понимаю лучше.
не нужно благодарить, просто примите ответ, если он помог. чтобы в будущем это мог прочитать любой желающий.
можешь поделиться логами ошибок? и синтаксис тоста неверен