: kotlin.UninitializedPropertyAccessException: lateinit property manager has not been initialized - android

I am developing news and I am getting following nullpointexception
java.lang.RuntimeException: Unable to start activity ComponentInfo{yodgorbek.komilov.musobaqayangiliklari/yodgorbek.komilov.musobaqayangiliklari.ui.WelcomeActivity}: kotlin.UninitializedPropertyAccessException: lateinit property manager has not been initialized
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2976)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3113)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1858)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6820)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property manager has not been initialized
at yodgorbek.komilov.musobaqayangiliklari.ui.WelcomeActivity.onCreate(WelcomeActivity.kt:26)
at android.app.Activity.performCreate(Activity.java:7224)
at android.app.Activity.performCreate(Activity.java:7213)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2956)
... 11 more
below my WelcomeActivity.kt class
class WelcomeActivity : AppIntro() {
private lateinit var manager: PreferencesManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Make sure you don't call setContentView!
if (manager.isFirstRun()) {
showIntroSlides()
} else {
goToMain()
}
}
// Call addSlide passing your Fragments.
// You can use AppIntroFragment to use a pre-built fragment
private fun showIntroSlides() {
manager.setFirstRun()
addSlide(
AppIntroFragment.newInstance(
title = "Welcome to the NewsApp",
description = "NewsApp give your information about life news around the world",
imageDrawable = R.drawable.news,
backgroundDrawable = R.drawable.news_slider,
titleColor = Color.YELLOW,
descriptionColor = Color.RED,
backgroundColor = Color.BLUE,
titleTypefaceFontRes = R.font.opensans_light,
descriptionTypefaceFontRes = R.font.opensans_regular
)
)
addSlide(
AppIntroFragment.newInstance(
title = "...Let's get started!",
description = "This is the last slide, I won't annoy you more :)"
)
)
}
private fun goToMain() {
startActivity(Intent(this, MainActivity::class.java))
}
override fun onSkipPressed(currentFragment: Fragment?) {
super.onSkipPressed(currentFragment)
goToMain()
}
override fun onDonePressed(currentFragment: Fragment?) {
super.onDonePressed(currentFragment)
goToMain()
}
override fun onSlideChanged(oldFragment: Fragment?, newFragment: Fragment?) {
super.onSlideChanged(oldFragment, newFragment)
Log.d("Hello", "Changed")
}
}
I don't understand what is the causing null pointer exception even I have tried initialize manager following way
private var manager: PreferencesManager? = null
but it did not solve my problem
I want to know where I am making mistake what I have to do avoid nullpointer exception

It's simple by looking into logs. Your manager is not initialized before using. Look at onCreate
private lateinit var manager: PreferencesManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Make sure you don't call setContentView!
// <<<<<<<<<<<<<<< INITIALISE manager HERE >>>>>>>>>>>>>>
if (manager.isFirstRun()) { <<<<<<<< HERE you are using an uninitilised manager
showIntroSlides()
} else {
goToMain()
}
}
Initialise manager before if (manager.isFirstRun()) { in onCreate()

Related

Android - kotlin.UninitializedPropertyAccessException: lateinit property homeActivityBinding has not been initialized

I have a BaseFragment class that is extended by all fragments in my app. And i have a HomeActivity class is a starting activity and also has some generic functionality in it.
Here is my HomeActivity code:
class HomeActivity : HomeActivityContract.View {
private val presenter: HomeActivityContract.Presenter by inject {
parametersOf(Schedulers.computation())
}
override val selectedWatchList: WatchlistItem
get() = homeActivityBinding.watchlistSpinner.selectedWatchList
override val watchlistItems: MutableList<WatchlistItem>
get() = homeActivityBinding.watchlistSpinner.watchlistItems
val bag = CompositeDisposable()
lateinit var homeActivityBinding: ActivityHomeBinding
override
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (presenter.hasAccount) {
homeActivityBinding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(homeActivityBinding.root)
Observables.combineLatest(
realtimeDatabaseService.enableMaintenancePageForPhone,
realtimeDatabaseService.maintenanceDate
) { isEnabled, _mMsg ->
if (isEnabled) {
homeActivityBinding.homeMaintenanceView.root.visibility = View.VISIBLE
homeActivityBinding.homeParentView.visibility = View.GONE
homeActivityBinding.homeMaintenanceView.maintenanceTV.text = getString(R.string.maintenance_msg, _mMsg)
} else {
homeActivityBinding.homeMaintenanceView.root.visibility = View.GONE
homeActivityBinding.homeParentView.visibility = View.VISIBLE
presenter.attachView(this)
presenter.dataProvider.getAccountUpdate()
.subscribeBy(onError = { it.printStackTrace() }, onNext = {
presenter.sendDeviceIdForCurrentAccount(isNotificationsEnabledInSettings)
}).disposeBy(lifecycle.disposers.onDestroy)
setSupportActionBar(homeActivityBinding.homeToolbar)
presenter.start()
}
}
.subscribe()
.disposeBy(lifecycle.disposers.onDestroy)
} else {
val intent = Intent(this, LoginActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
startActivity(intent)
finish()
}
}
fun hideNavigationMenu() {
homeActivityBinding.homeNavigationView.visibility = View.GONE /// This is where it says that homeActivityBinding is uninitialised.
}
BaseFragment code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isAdded)
parentFragmentManager.let { fragmentManager ->
takeIf { fragmentManager.backStackEntryCount >= 1 }?.let {
(activity as? HomeActivity)?.hideNavigationMenu()
}
}
}
So BaseFragment is calling hideNavigationMenu in HomeActivity.
Here is the crash report:
Caused by kotlin.UninitializedPropertyAccessException: lateinit property homeActivityBinding has not been initialized
at com.app.android.traderpro.etx.activities.homeActivity.HomeActivity.G5(HomeActivity.kt:7)
at com.app.android.traderpro.etx.activities.homeActivity.HomeActivity.hideNavigationMenu(HomeActivity.kt:404)
at com.app.android.traderpro.etx.fragments.BaseFragment.hideNavigationMenu(BaseFragment.kt:138)
at com.app.android.traderpro.etx.fragments.BaseFragment.onCreate(BaseFragment.kt:153)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:3090)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:257)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2875)
at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:252)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:220)
at com.app.android.traderpro.etx.activities.BaseActivity.onCreate(BaseActivity.kt:61)
at com.app.android.traderpro.etx.activities.homeActivity.HomeActivity.onCreate(HomeActivity.kt:134)
at android.app.Activity.performCreate(Activity.java:8207)
at android.app.Activity.performCreate(Activity.java:8191)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3819)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4022)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8653)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
The first thing is I cannot replicate this crash and there are around 200 crashes reported in Firebase Crashlytics.
And
HomeActivity is the first activity that launches all other fragments. This crash is not happening as soon as this activity is started, it is happening after some time. So I don't understand how homeActivityBinding can be uninitialized.
I'd appreciate it if anyone can tell me how a lateinit property that is initialised can be uninitialized again
Fragment.onCreate() is too early to be trying to access members of the Activity, even though it is already attached. From the onCreate() documentation:
Note that this can be called while the fragment's activity is still in the process of being created. As such, you can not rely on things like the activity's content view hierarchy being initialized at this point.
You should move your code from onCreate to onViewCreated.

Cannot create an instance of class ViewModel unable to find cause

whenever I click on the title, the app Crashes shows this logcat
I am new to Android
using
dagger hilt,
exoplayer,
this is where i touch got error:
Songfragment
#AndroidEntryPoint
class SongFragments : Fragment(R.layout.fragment_song) {
#Inject
lateinit var glide: RequestManager
private lateinit var mainViewModel: MainViewModel
private val songViewModel: SongViewModel by viewModels()
private var curplayingSong: sound? = null
private var playbackState: PlaybackStateCompat? = null
private var shouldUpdateSeekbar = true
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
subscribeToObservers()
ivPlayPauseDetail.setOnClickListener {
curplayingSong?.let {
mainViewModel.playOrToggleSound(it, true)
}
}
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
setCurPlayerTimeToTextView(progress.toLong())
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
shouldUpdateSeekbar = false
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
seekBar?.let {
mainViewModel.seekTo(it.progress.toLong())
shouldUpdateSeekbar = true
}
}
})
ivSkipPrevious.setOnClickListener {
mainViewModel.skipToPreviousSound()
}
ivSkip.setOnClickListener{
mainViewModel.skipToNextSound()
}
}
private fun updateTitleAndSongImage(sound: sound) {
val title = "${sound.title} - ${sound.subtitle}"
tvSongName.text = title
glide.load(sound.imageUrl).into(ivSongImage)
}
private fun subscribeToObservers() {
mainViewModel.mediaItems.observe(viewLifecycleOwner){
it?.let { result ->
when(result.status){
Status.SUCCESS -> {
result.data?.let { sounds ->
if (curplayingSong == null && sounds.isNotEmpty()) {
curplayingSong = sounds[0]
updateTitleAndSongImage(sounds[0])
}
}
}
else -> Unit
}
}
}
mainViewModel.curPlayingSound.observe(viewLifecycleOwner) {
if(it == null) return#observe
curplayingSong = it.toSong()
updateTitleAndSongImage(curplayingSong!!)
}
mainViewModel.playbackState.observe(viewLifecycleOwner) {
playbackState = it
ivPlayPauseDetail.setImageResource(
if (playbackState?.isPlaying == true) R.drawable.ic_pause else R.drawable.ic_play
)
seekBar.progress = it?.position?.toInt() ?: 0
}
songViewModel.curPlayerPosition.observe(viewLifecycleOwner) {
if (shouldUpdateSeekbar) {
seekBar.progress = it.toInt()
setCurPlayerTimeToTextView(it)
}
}
songViewModel.curSongDuration.observe(viewLifecycleOwner) {
seekBar.max = it.toInt()
val dateFormat = SimpleDateFormat("mm:ss", Locale.getDefault())
tvSongDuration.text = dateFormat.format(it)
}
}
private fun setCurPlayerTimeToTextView(ms: Long) {
val dateFormat = SimpleDateFormat("mm:ss", Locale.getDefault())
tvCurTime.text = dateFormat.format(ms)
}
}
SongviewModel
class SongViewModel #ViewModelInject constructor(
musicServiceConnection: MusicServiceConnection
) : ViewModel() {
private val playbackState = musicServiceConnection.playBackState
private val _curSongDuration = MutableLiveData<Long>()
val curSongDuration: LiveData<Long> = _curSongDuration
private val _curPlayerPosition = MutableLiveData<Long>()
val curPlayerPosition: LiveData<Long> = _curPlayerPosition
init {
updateCurrentplayerPostion()
}
#SuppressLint("NullSafeMutableLiveData")
private fun updateCurrentplayerPostion() {
viewModelScope.launch {
while(true) {
val pos = playbackState.value?.currentPlaybackPosition
if(curPlayerPosition.value != pos){
_curPlayerPosition.postValue(pos)
_curSongDuration.postValue(MusicService.curSoundDuration)
}
delay(UPDATE_PLAYER_POSITION_INTERVAL)
}
}
}
}
logcat:
2022-08-12 19:11:39.122 10888-10983/com.fridayhouse.snoozz E/ion: ioctl c0044901 failed with code -1: Invalid argument
2022-08-12 19:11:39.318 10888-10888/com.fridayhouse.snoozz E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.fridayhouse.snoozz, PID: 10888
java.lang.RuntimeException: Cannot create an instance of class com.fridayhouse.snoozz.ui.viewmodels.SongViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:175)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:203)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111)
at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35)
at com.fridayhouse.snoozz.ui.fragments.SongFragments.getSongViewModel(SongFragments.kt:33)
at com.fridayhouse.snoozz.ui.fragments.SongFragments.subscribeToObservers(SongFragments.kt:121)
at com.fridayhouse.snoozz.ui.fragments.SongFragments.onViewCreated(SongFragments.kt:45)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1890)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1814)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:233)
at android.os.Looper.loop(Looper.java:334)
at android.app.ActivityThread.main(ActivityThread.java:8396)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:582)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068)
Caused by: java.lang.InstantiationException: java.lang.Class<com.fridayhouse.snoozz.ui.viewmodels.SongViewModel> has no zero argument constructor
at java.lang.Class.newInstance(Native Method)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322) 
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304) 
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:175) 
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:203) 
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111) 
at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:83) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) 
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53) 
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35) 
at com.fridayhouse.snoozz.ui.fragments.SongFragments.getSongViewModel(SongFragments.kt:33) 
at com.fridayhouse.snoozz.ui.fragments.SongFragments.subscribeToObservers(SongFragments.kt:121) 
at com.fridayhouse.snoozz.ui.fragments.SongFragments.onViewCreated(SongFragments.kt:45) 
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128) 
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552) 
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) 
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1890) 
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1814) 
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751) 
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538) 
at android.os.Handler.handleCallback(Handler.java:938) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loopOnce(Looper.java:233) 
at android.os.Looper.loop(Looper.java:334) 
at android.app.ActivityThread.main(ActivityThread.java:8396) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:582) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068) 
#ViewModelInject has been deprecated. You can check here for more info.
In newer versions, You need to annotate viewmodel class with #AndroidViewModel and constructor with #Inject.
#AndroidViewModel
class SongViewModel #Inject constructor(
musicServiceConnection: MusicServiceConnection
) : ViewModel()
Hope it would solve this issue.
Since you are using Hilt, you shouldn't be using by viewModels(). You should set up your MusicServiceConnection in your Provider, and instead of using private val songViewModel: SongViewModel by viewModels(), use #Inject private lateinit var songViewModel: SongViewModel.
I'm not very familiar with Hilt, so I can't provide more guidance. The below is an explanation of what's going wrong with by viewModels(), and how you would resolve this if you were not using a dependency injection framework.
When you create a ViewModel in your Activity or Fragment, you can supply a factory that is responsible for creating instances of the ViewModel. If you don't supply a factory, then the default factory is used.
The default factory is only capable of creating instances of your ViewModel if your ViewModel constructor's arguments are one of the following:
constructor() Empty constructor (no arguments)
constructor(savedStateHandle: SavedStateHandle)
constructor(application: Application)
constructor(application: Application, savedStateHandle: SavedStateHandle)
I don't think this is documented very well. I had to look at the Jetpack source code to learn it.
Since your ViewModel needs a MusicServiceConnection parameter, you cannot use the default factory. You will need to supply an explicit ViewModelProvider.Factory to the by viewModels() as an argument.
This training has an example of creating a ViewModelProvider.Factory class for a ViewModel that needs a special argument for the constructor. In your case, the solution might look like this:
class SongViewModelFactory(private val musicServiceConnection: MusicServiceConnection) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
require(modelClass.isAssignableFrom(SongViewModel::class.java)) { "Unknown ViewModel class" }
#Suppress("UNCHECKED_CAST")
return SongViewModel(musicServiceConnection) as T
}
}
I don't know where your MusicServiceConnection class comes from, but let's pretend you can simply instantiate it one time for your Activity with an empty constructor. Then you would replace
private val songViewModel: SongViewModel by viewModels()
with
private val songViewModel: SongViewModel by viewModels {
SongViewModelFactory(MusicServiceConnection())
}
Notice that you are passing a lambda function to viewModels instead of directly passing an instance of your factory. This is because the factory is lazily created only one time, even if your Activity is recreated multiple times (like if the user rotates the screen back and forth). The code inside this lambda is only called once when the app navigates to this screen, even if the Activity is recreated for screen rotations or other configuration changes.

"Intent must not be null" when sending strings from one activity to another

I'm new to Kotlin and I'm trying to send two strings from two textViews in one activity to another using intents. However, I'm getting this error when I run the function load.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.pdm_2021_i_p1_project1, PID: 18672
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.pdm_2021_i_p1_project1/com.example.pdm_2021_i_p1_project1.PlayActivity}: java.lang.IllegalStateException: intent must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3365)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.IllegalStateException: intent must not be null
at com.example.pdm_2021_i_p1_project1.PlayActivity.getWord(PlayActivity.kt:119)
at com.example.pdm_2021_i_p1_project1.PlayActivity.<init>(PlayActivity.kt:21)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1253)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3353)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:223) 
at android.app.ActivityThread.main(ActivityThread.java:7656) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
Here's my code from the activity receiving the strings
class PlayActivity : AppCompatActivity() {
//Variable Declarations
private val lives : Int = 4
private var fails : Int = 0
private var correctGuesses = mutableSetOf<Char>()
private var guesses = mutableSetOf<Char>()
private val word = getWord().toLowerCase(Locale.ROOT)
private val clue = getClue().toLowerCase(Locale.ROOT)
private val letters = word.toLowerCase(Locale.ROOT).toCharArray().toHashSet()
private val txtArray = arrayOfNulls<TextView>(word.length)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play)
btnCheck.setOnClickListener{(checkWord())}
createTxtViews()
txvClue.text = clue.toUpperCase(Locale.ROOT)
txvLives.text = "❤❤❤❤"
}
private fun checkWord(){
val character = txtPlayAddLetter.text.toString().toLowerCase(Locale.ROOT)
if (txtPlayAddLetter.text.isNotEmpty()){
if (word.contains(txtPlayAddLetter.text.toString().toLowerCase(Locale.ROOT))) {
correctGuesses.add(character[0])
guesses.add(character[0])
txvGuessed.text = guesses.toString().toUpperCase(Locale.ROOT)
txvTest.text = letters.toString()
showLetters()
txtPlayAddLetter.text.clear()
checkGameState()
}
else
{
fails++
guesses.add(character[0])
txvGuessed.text = guesses.toString().toUpperCase(Locale.ROOT)
txtPlayAddLetter.text.clear()
val hearts = txvLives.text.dropLast(1)
txvLives.text = hearts
when (fails) {
1 -> {frame1.isVisible = false
frame2.isVisible = true}
2 -> {frame2.isVisible = false
frame3.isVisible = true}
3 -> {frame3.isVisible = false
frame4.isVisible = true}
4 -> {frame4.isVisible = false
frame5.isVisible = true}}
if (fails == lives)
{
showDefeat()
}
}
}
else
{
Toast.makeText(this#PlayActivity, "Please enter a letter :)", Toast.LENGTH_LONG).show()
}
}
private fun showDefeat(){
val intent = Intent(this, DefeatActivity::class.java)
finish()
startActivity(intent)
}
private fun showVictory(){
val intent = Intent(this, VictoryActivity::class.java)
finish()
startActivity(intent)
}
private fun createTxtViews(){
val lLayout = findViewById<View>(R.id.txtPlayGuessed) as LinearLayout
for (i in word.indices) {
txtArray[i] = TextView(this)
txtArray[i]?.id = i
txtArray[i]?.setTextColor(resources.getColor(R.color.white))
txtArray[i]?.hint = " _ "
txtArray[i]?.setHintTextColor(resources.getColor(R.color.white))
txtArray[i]?.textSize = 25F
lLayout.addView(txtArray[i])
txtArray[i]?.isVisible = true
}
}
private fun showLetters(){
val str = txtPlayAddLetter.text.toString().toLowerCase(Locale.ROOT)
for (i in word.indices){
if (txtPlayAddLetter.text.single().toLowerCase() == word[i]){
txtArray[i]?.text = " ".plus(str.toUpperCase(Locale.ROOT)).plus(" ")
}
}
}
private fun checkGameState(){
if (correctGuesses == letters){
showVictory()
finish()
}
}
fun getWord(): String{
val bundle=intent.extras
val setword= bundle?.get("setword")
return getString(R.string.setwordplay,setword)
}
fun getClue(): String{
val bundle = intent.extras
val setclue = bundle?.get("setclue")
return getString(R.string.setwordplay,setclue)
}}
And here's my code from the activity sending the strings
fun load(){
val intent = Intent(this, PlayActivity::class.java)
intent.putExtra("setword", editTextTypeAWord.text.toString())
intent.putExtra("setclue", editTextHint.text.toString())
startActivity(intent)
}
You are trying to initialise the val with data from Intent. In PlayActivity, the init{} block and property initialisations run before onCreate().
To work around this issue you could try:
class PlayActivity : AppCompatActivity() {
companion object {
const val ARG_WORD = "word"
const val ARG_CLUE = "clue"
}
private lateinit var word: String
private lateinit var clue: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
word = intent.getStringExtra(ARG_WORD) ?: throw IllegalArgumentException("Word was not supplied")
clue = intent.getStringExtra(ARG_CLUE) ?: throw IllegalArgumentException("Clue was not supplied")
}
}
Call with:
val intent = Intent(this, PlayActivity::class.java).apply {
putExtra(PlayActivity.ARG_WORD, "giraffe")
putExtra(PlayActivity.ARG_CLUE, "long neck")
}
startActivity(intent)
Also, remember to refactor other properties that rely on word and clue to use either functions or getters, otherwise you'll run into UninitializedPropertyAccessException.
What using getters does is that every time you call the property the get()-portion is calculated. Since you're using the properties after onCreate(), your lateinit properties should already have been initialised from the intent you've received!
class PlayActivity : AppCompatActivity() {
companion object {
const val ARG_WORD = "word"
const val ARG_CLUE = "clue"
}
private lateinit var word: String
private lateinit var clue: String
private val letters: HashSet<Char>
get() = word.toLowerCase(Locale.ROOT).toCharArray().toHashSet()
private val txtArray: Array<TextView?>
get() = arrayOfNulls<TextView>(word.length)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
word = intent.getStringExtra(ARG_WORD) ?: throw IllegalArgumentException("Word was not supplied")
clue = intent.getStringExtra(ARG_CLUE) ?: throw IllegalArgumentException("Clue was not supplied")
}
}
I highly recommend you to read this: https://kotlinlang.org/docs/properties.html
val intent = Intent(this, PlayActivity::class.java).apply {
putExtra("setword", message)
}
startActivity(intent)
try this
private fun showVictory(){
val intent = Intent(this, VictoryActivity::class.java)
startActivity(intent)
finish()
}
use like this
intent?.getStringExtra("setword") ?: ""
intent?.getStringExtra("setclue") ?: ""
try this way
fun getWord(): String{
val setword= intent.getStringExtra("setword")
return getString(R.string.setwordplay,setword)
}
fun getClue(): String{
val setclue = intent.getStringExtra("setclue")
return getString(R.string.setwordplay,setclue)
}}

Android paging 3 library removing .cahcedIn(viewModelScope) will throw exception

I am working with paging 3.0.0-alpha11 library in my app.
There is a fragment that has a view model. in the view model I implemented paging data like this. there is one mutable live data for a parameter named promoId. promoId is a string that represents id of a model. i use it with another parameter named page to get a page of data from network. so the problem is that the first time i navigate to te fragment every thing works fine, but after popping the fragment and navigating to it for the second time, I get an error and app crashes.
my fragment code.
class ExploreFragment : DaggerFragment() {
private var _binding: FragmentExploreBinding? = null
private val binding get() = _binding!!
#Inject
lateinit var viewModelProviderFactory: ViewModelProviderFactoryImpl
private lateinit var exploreViewModel: ExploreViewModel
lateinit var commentsAdapter: LiveCommentAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initLists()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
exploreViewModel =
ViewModelProvider(this, viewModelProviderFactory)[ExploreViewModel::class.java]
observeComments()
commentsViewModel.getPromoComments(promoId)
}
}
private fun initLists() {
commentsAdapter = LiveCommentAdapter()
binding.rvComments.adapter = commentsAdapter
binding.rvSearchResults.adapter = searchResultAdapter
}
private fun observeComments() {
exploreViewModel.commentsLiveData.observe(viewLifecycleOwner) { comments ->
commentsAdapter.submitData(viewLifecycleOwner.lifecycle, comments)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
This is my view model
also note that I don't use cachedIn(viewModelScope) after commentRepository.getCommentsByPromo(promoId?:"") because it caches the data and does not call load method of paging data for the second time.
class ExploreViewModel #Inject constructor(
#Inject #JvmField var commentRepository: CommentRepository,
) :
ViewModel() {
private val promoIdLiveData: MutableLiveData<String?> = MutableLiveData()
var commentsLiveData: LiveData<PagingData<Comment>> = promoIdLiveData.switchMap { promoId ->
commentRepository.getCommentsByPromo(promoId?:"")
}
fun getComments(promoId: String) {
promoIdLiveData.value = promoId
}
}
And this is my repository code.
class CommentRepository #Inject constructor(#Inject #JvmField var retrofit: Retrofit) {
private val api: CommentsApi = retrofit.create(CommentsApi::class.java)
fun getCommentsByPromo(promoId: String):LiveData<PagingData<Comment>> = Pager(
config = PagingConfig(pageSize = 20, enablePlaceholders = false,prefetchDistance = 100),
pagingSourceFactory = { PromoCommentPagingSource(promoId, api) }
).liveData
}
and the error i get is
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.tivasoft.project, PID: 28896
java.lang.IllegalStateException: Attempt to collect twice from pageEventFlow, which is an illegal operation. Did you forget to call Flow<PagingData<*>>.cachedIn(coroutineScope)?
at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invokeSuspend(PageFetcherSnapshot.kt:88)
at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:10)
at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invokeSuspend(CancelableChannelFlow.kt:35)
at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:10)
at kotlinx.coroutines.flow.ChannelFlowBuilder.collectTo$suspendImpl(Builders.kt:344)
at kotlinx.coroutines.flow.ChannelFlowBuilder.collectTo(Unknown Source:0)
at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:60)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:349)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:27)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:49)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
at androidx.paging.AsyncPagingDataDiffer.submitData(AsyncPagingDataDiffer.kt:156)
at androidx.paging.PagingDataAdapter.submitData(PagingDataAdapter.kt:172)
at com.tivasoft.project.ui.explore.ExploreFragment$observeComments$$inlined$observe$1.onChanged(LiveData.kt:52)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:144)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:443)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:395)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:51)
at androidx.fragment.app.Fragment.performStart(Fragment.java:2737)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:365)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1194)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
I would be very thankful if anybody can tell me what's wrong
I'm a bit confused on exactly what you want to happen, but my understanding is that you want to reload from scratch whenever you navigate back.
The error is saying you cannot call .submitData on the same instance of PagingData twice. You're essentially trying load cached data unintentionally and since you didn't call cachedIn, Paging is throwing an exception.
An easy way to force Paging to load from scratch every time, is to make sure you construct a new instance of Pager each time.
So I would try calling observeComments() in onViewCreated and turn commentsLiveData into a function (instead of a var) which returns a new LiveData<PagingData> each time its called.
In my case I had a Transformations.switchMap(liveData) and that was causing the issue, the fix was to make the PagingData to cache before the switchMap
fun observable(): LiveData<PagingData<SomeModel>> {
val pagingData = //obtained somewhere
//make sure to cache it
val chachedPagingData = pagingData.cachedIn(viewModelScope)
return Transformations.switchMap(otherLiveData) { observed ->
//use observed for obtaining live attributes needed
chachedPagingData.filter {...}.map{...}
}
}
For me, that was the solution because the pagingData sometimes can be suspend, so the above would be more explanatory like this:
suspend fun observable(): LiveData<PagingData<SomeModel>> {
val someAttributeFromTheDb = //get it using ROOM
val pagingData = if (someAttributeFromTheDb == /*condition*/) {
suspendPagingData } else regularPagingData
val chachedPagingData = ...
}
Just replace MutableLiveData
private val promoIdLiveData: MutableLiveData<String?> = MutableLiveData()
with SingleLiveEvent
private val promoIdLiveData: SingleLiveEvent<String?> = SingleLiveEvent()
The SingleLiveEvent
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
#MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(
owner,
Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
)
}
#MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
#MainThread
fun call() {
value = null
}
companion object {
private val TAG = "SingleLiveEvent"
}
}

Why variable is not initialized even if it was created?

I don't really understand why logFile(and loggingProcess) are not initialized. The logcat shows that the file exists and createLogFile() method had been called when Activity was created, but when I'm trying to uploadLogs() from the Activity(using SandboxApp().uploadLogs()) it throws an exception
class SandboxApp: Application(), MyCallbacks {
companion object {
#JvmField
val TAG = SandboxApp::class.java.simpleName
}
private lateinit var loggingProcess: Process
private lateinit var logFile: File
private lateinit var fileDirectory: File
private var isLogging = false
private val formatter = SimpleDateFormat("dd-MM-yyyy_HH-mm", Locale.getDefault())
private lateinit var workManager: WorkManager
override fun onCreate() {
super.onCreate()
workManager = WorkManager.getInstance(this)
registerActivityLifecycleCallbacks(this)
}
fun createLogFile() {
val currentTime = formatter.format(Calendar.getInstance().time)
val fileName = "logs-$currentTime.log"
fileDirectory = File(filesDir.absolutePath + File.separator + "sandboxLog")
fileDirectory.mkdirs()
Log.d(TAG, "FileDir exists? $fileDirectory, ${fileDirectory.exists()}")
logFile = File(fileDirectory, fileName)
logFile.createNewFile()
Log.d(TAG, "File exists? $logFile, ${logFile.exists()}")
}
fun startLogging() {
isLogging = true
loggingProcess = Runtime.getRuntime().exec("logcat -f $logFile")
}
private fun stopLogging() {
isLogging = false
loggingProcess.destroy()
}
fun uploadLogs() {
stopLogging()
val data = Data.Builder()
.putString("file path", logFile.absolutePath)
.build()
val request = OneTimeWorkRequest.Builder(LogsWorker::class.java)
.setInputData(data)
.build()
workManager.enqueue(request)
createLogFile()
startLogging()
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
super.onActivityCreated(activity, savedInstanceState)
Log.d(TAG, "onActivityCreated method")
createLogFile()
startLogging()
}
}
This is the exception, tha same is for the loggingProcess variable
2020-08-04 15:55:51.338 8741-8741/com.example.sandboxlog E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sandboxlog, PID: 8741
kotlin.UninitializedPropertyAccessException: lateinit property logFile has not been initialized
at com.example.sandboxlog.SandboxApp.uploadLogs(SandboxApp.kt:66)
at com.example.sandboxlog.MainActivity$onCreate$2.onClick(MainActivity.kt:33)
at android.view.View.performClick(View.java:7125)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
upd: onCreate method of MainActivity, where uploadLogs is being called
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "MainActivity created")
Thread.setDefaultUncaughtExceptionHandler(CrashHandler())
button.setOnClickListener {
// creating an exception
RequestBody.create(MultipartBody.FORM, exceptionFile!!)
}
buttonSend.setOnClickListener {
SandboxApp().uploadLogs()
}
buttonSecondActivity.setOnClickListener {
Log.d(TAG, "Starting second activity")
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
You shouldn't create an instance of your app class SandboxApp by yourself, it is done by the system. To have access to the instance of SandboxApp you can use ctx.applicationContext property:
val app = context.applicationContext as SandboxApp
app.uploadLogs()
So in the OnClickListener it will look like the following:
buttonSend.setOnClickListener {
val app = applicationContext as SandboxApp
app.uploadLogs()
}

Categories

Resources