lateinit property binding has not been initialized though i did not set it as lateinit - kotlin

I faced that error when i was trying to update my views with new ViewBinding stuff. I don't define the value as "lateinit" but logccat says "lateinit property binding has not been initialized" why i m taking this ?
Thanks in advance.
The exception is on private val email and password rows.
class MainActivity : AppCompatActivity() {
private lateinit var auth : FirebaseAuth
private lateinit var binding: ActivityMainBinding
private val email = binding.emailText.text.toString()
private val password = binding.passwordText.text.toString()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
auth= FirebaseAuth.getInstance()
val guncelKullanici = auth.currentUser
if (guncelKullanici!= null) {
val intent = Intent(this, haber_akisi::class.java)
startActivity(intent)
finish()
}
}
fun girisYap ( view: View) {
if (email.isNotBlank() && password.isNotBlank()) {
auth.signInWithEmailAndPassword(email,password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val intent = Intent(this,haber_akisi::class.java)
startActivity(intent)
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(this,exception.localizedMessage,Toast.LENGTH_LONG).show()
}}else {
Toast.makeText(this,"Lütfen E-mail ve Password alanlarını doldurunuz",Toast.LENGTH_LONG).show()
}
}
fun kayitOl ( view : View) {
if ( email.isNotBlank() && password.isNotBlank() ) {
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val intent = Intent(this, haber_akisi::class.java)
startActivity(intent)
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(this, exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}else {
Toast.makeText(this,"Lütfen E-mail ve Password alanlarını doldurunuz",Toast.LENGTH_LONG).show()
}
}
}

This is because private val email = binding.emailText.text.toString() is using the "binding" variable before it has been initialized. The error is saying that the "lateinit var binding" has not been initialized yet but you are accessing it on private val email = binding.emailText.text.toString()
Edit: One way to solve this is to make email and password as lateinit vars too. Another way is to not have an email and password class level properties and just access the binding where it's needed like in girisYap() and kayitOl()

You are accessing binding(ActivityMainBinding Identifier) in the next lines, after its declaration.
You need to initialize binding before using it.

Related

Getting list from viewmodel in observe event -MVVM

I have a issue in getting a list returned in observe event in my activity. i am developing a login screen in MVVM. viewmodel is as follows.
my problem is i can get returned data in observe call back into a UI control. but same data returned assign into a list variable is empty. in other words, list returned unable to pass into a a list variable in an activity.
class LoginViewModel #Inject internal constructor (private val loginRepository: LoginRepository,private val usersRepository: UsersRepository): ViewModel() {
private var _userEmail:MutableLiveData<String>
private var _userPassword:MutableLiveData<String>
private var _userLoginData:MutableLiveData<UserLoginData>
private var allUsers:MutableLiveData<List<Users>>
private var findUser:MutableLiveData<List<Users>>
init{
_userEmail= MutableLiveData()
_userPassword= MutableLiveData()
_userLoginData= MutableLiveData()
allUsers= MutableLiveData()
findUser= MutableLiveData()
}
fun getEmail():LiveData<String>{
return _userEmail
}
fun getPassword():MutableLiveData<String>{
return _userPassword
}
fun userLogin(userEmail:String,userPassword:String):MutableLiveData<UserLoginData>{
_userEmail.postValue(userEmail)
_userPassword.postValue(userPassword)
viewModelScope.launch(Dispatchers.IO) {
var userlogindata:UserLoginData=loginRepository.userLogin(userEmail,userPassword)
_userLoginData.postValue(userlogindata)
}
return _userLoginData
}
fun getAllUsers():MutableLiveData<List<Users>>{
//lateinit var _allUsers:List<Users>
viewModelScope.launch(Dispatchers.IO) {
val _allUsers:List<Users> =usersRepository.getUsers()
allUsers.postValue(_allUsers)
}
return allUsers
}
fun findUser(userEmail:String):MutableLiveData<List<Users>>{
//lateinit var finduser:List<Users>
viewModelScope.launch(Dispatchers.IO) {
val _findUser:List<Users> =usersRepository.findUser(userEmail)
findUser.postValue(_findUser)
}
return findUser
}
}
in an activity i am observing the users list and getting the list into a list variable in the activity. code in the activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var loginViewModel: LoginViewModel
lateinit var loginData:UserLoginData
var users:List<Users> = emptyList()
var findUser:List<Users> = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java)
/*observe users list*/
loginViewModel.getAllUsers().observe(this, {It->
users=It
binding.textView.text=It[0].email.toString()
})
loginViewModel.findUser(binding.loginEditTextTextEmailAddressTxt.toString().trim()).observe(this,{it->
findUser=it
})
This program failed if i use data in the users or findUser lists.
Kindly help me to find the best practice in getting the changed data from viewmodel into an activity
ViewModel:
data class User(
var name: String
)
private val _allUsers = MutableLiveData<List<User>>()
private val allUsers: LiveData<List<User>> get() = _allUsers
fun fetchAllUsers(): LiveData<List<User>> {
viewModelScope.launch {
//delay is simulating network request delay
delay(1000)
//listOf is simulating usersRepository.getUsers()
_allUsers.value = listOf(User("name1"), User("name2"), User("name3"))
}
return allUsers
}
Fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.fetchAllUsers().observe(viewLifecycleOwner) { userList ->
userList.forEach {
Log.d("user", it.name)
}
}
You can try this way but I do not prefer returning liveData with function because you have to observe liveData once. You need to be careful observe once.

My first observer called correctly but another was not called after inserted data to room database in kotlin android

In the application, I am fetching data from the web and from the observer change method, Insert that data to local db. that's fine. but after inserted to db, My second observer not called so my UI will not update.
ManActivity.class
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var adapter: MainAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout.activity_main)
setupViewModel()
setupUI()
setupObservers()
setupObservers2()
}
private fun setupViewModel() {
viewModel = ViewModelProviders.of(
this,
ViewModelFactory(ApiHelper(RetrofitBuilder.apiService))
).get(MainViewModel::class.java)
}
private fun setupUI() {
recyclerView.layoutManager = LinearLayoutManager(this)
adapter = MainAdapter(arrayListOf())
recyclerView.addItemDecoration(
DividerItemDecoration(
recyclerView.context,
(recyclerView.layoutManager as LinearLayoutManager).orientation
)
)
recyclerView.adapter = adapter
}
private fun setupObservers() {
viewModel.getUsers().observe(this, Observer {
//viewModel.getUserFromWeb()
it?.let { resource ->
when (resource.status) {
SUCCESS -> {
Log.d("MYLOG","MyAPIChange success")
recyclerView.visibility = View.VISIBLE
progressBar.visibility = View.GONE
resource.data?.let {
users -> viewModel.setUserListToDB(this,users)
//sleep(1000)
}
}
ERROR -> {
recyclerView.visibility = View.VISIBLE
progressBar.visibility = View.GONE
Log.d("MYLOG","MyAPIChange error")
Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
}
LOADING -> {
Log.d("MYLOG","MyAPIChange loading")
progressBar.visibility = View.VISIBLE
recyclerView.visibility = View.GONE
}
}
}
})
}
private fun setupObservers2() {
viewModel.getUserFromDB(this).observe(this, Observer {
users -> retrieveList(users)
Log.d("MYLOG","..MyDBChange")
})
}
private fun retrieveList(users: List<User>) {
adapter.apply {
addUsers(users)
notifyDataSetChanged()
}
}
}
MyViewModel.class
class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {
//lateinit var tempUser : MutableLiveData<List<User>>
fun getUsers() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
emit(Resource.success(data = mainRepository.getUsers()))
} catch (exception: Exception) {
emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
}
//emit(mainRepository.getUsers()) //direct call
}
fun getUserFromDB(context: Context) = liveData(Dispatchers.IO) {
emit(mainRepository.getUserList(context))
}
fun setUserListToDB(context: Context, userList: List<User>) {
/*GlobalScope.launch {
mainRepository.setUserList(context, userList)
}*/
CoroutineScope(Dispatchers.IO).launch {
mainRepository.setUserList(context, userList)
}
}
}
MyRepository.class
class MainRepository(private val apiHelper: ApiHelper) {
suspend fun getUsers() = apiHelper.getUsers() // get from web
companion object {
var myDatabase: MyDatabase? = null
lateinit var userList: List<User>
fun initializeDB(context: Context): MyDatabase {
return MyDatabase.getDataseClient(context)
}
/*fun insertData(context: Context, username: String, password: String) {
myDatabase = initializeDB(context)
CoroutineScope(Dispatchers.IO).launch {
val loginDetails = User(username, password)
myDatabase!!.myDao().InsertData(loginDetails)
}
}*/
}
//fun getUserList(context: Context, username: String) : LiveData<LoginTableModel>? {
suspend fun getUserList(context: Context) : List<User> {
myDatabase = initializeDB(context)
userList = myDatabase!!.myDao().getUserList()
Log.d("MYLOG=", "DBREAD"+userList.size.toString())
return userList
}
fun setUserList(context: Context,userList: List<User>){
myDatabase = initializeDB(context)
/*CoroutineScope(Dispatchers.IO).launch {
myDatabase!!.myDao().InsertAllUser(userList)
Log.d("MYLOG","MyDBInserted")
}*/
myDatabase!!.myDao().InsertAllUser(userList)
Log.d("MYLOG","MyDBInserted")
/*val thread = Thread {
myDatabase!!.myDao().InsertAllUser(userList)
}
Log.d("MYLOG","MyDBInserted")
thread.start()*/
}
}
DAO class
#Dao
interface DAOAccess {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun InsertAllUser(userList: List<User>)
// #Query("SELECT * FROM User WHERE Username =:username")
// fun getLoginDetails(username: String?) : LiveData<LoginTableModel>
#Query("SELECT * FROM User")
suspend fun getUserList() : List<User>
}
RetrofitBuilder
object RetrofitBuilder {
private const val BASE_URL = "https://5e510330f2c0d300147c034c.mockapi.io/"
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val apiService: ApiService = getRetrofit().create(ApiService::class.java)
}
Please can you know that what I am doing wrong here and why the second observer was not called after insert to db
Actually It was called when the screen launched but that time data not inserted so list size was 0 and after insert data this method will not call again. But Once I close app and again start then data will display bcoz at launch time, this method call and data got
I don't have enough reputation to commet, therefore I just bring a suggestion in this answer:
Suggestion/Solution
Room supports LiveData out of the box. So in your DAO you can change
suspend fun getUserList() : List<User>
to
suspend fun getUserList() : LiveData<List<User>>
Then in your repository adjust to
suspend fun getUserList(context: Context) : LiveData<List<User>> {
myDatabase = initializeDB(context)
userList = myDatabase!!.myDao().getUserList()
Log.d("MYLOG=", "DBREAD"+userList.value.size.toString())
return userList
}
and in the ViewModel
fun getUserFromDB(context: Context) = mainRepository.getUserList(context))
With these adjustments I think it should work.
Explaination
You used the liveData couroutines builder here
fun getUserFromDB(context: Context) = liveData(Dispatchers.IO) {
emit(mainRepository.getUserList(context))
}
As far as I understand this builder, it is meant to execute some asynchronous/suspend task and as soon as this task finishes the liveData you created will emit the result. That means that you only once receive the state of the user list an emidiately emit the list to the observer one single time and then this liveData is done. It does not observe changes to the list in the DB the whole time.
That is why it works perfectly for observing the API call (you want to wait until the call is finished and emit the response one single time), but not for observing the DB state(you want to observe the user list in the DB all the time and emit changes to the observer whenever the list is changed)

Observer pattern is not working in Android MVVM

I am trying to update my view according to my data in my ViewModel, using MVVM
I need in the method onCacheReception to update my map whenever zones is changing
ViewModel
class MainViewModel constructor(application: Application) : AndroidViewModel(application),
CacheListener {
private val instance = Initializer.getInstance(application.applicationContext)
private val _zones = MutableLiveData<List<Zone>>()
val zones: LiveData<List<Zone>>
get() = _zones
init {
CacheDispatcher.addCacheListener(this)
}
override fun onCacheReception() {
val zonesFromDB: List<Zone>? = instance.fetchZonesInDatabase()
_zones.value = zonesFromDB
}
}
MainActivity
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks, OnMapReadyCallback {
private val mainViewModel: MainViewModel = ViewModelProvider(this).get(MainViewModel(application)::class.java)
private lateinit var initializer: Initializer
private lateinit var map: GoogleMap
private val REQUEST_CODE_LOCATIONS: Int = 100
private val permissionLocationsRationale: String = "Permissions for Fine & Coarse Locations"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (checkForLocationsPermission()) {
setUp()
mapSetUp()
}
mainViewModel.zones.observe(this, Observer { zones ->
zones.forEach {
Log.i("YES DATA", "Data has been updated")
val latLng = it.lat?.let { it1 -> it.lng?.let { it2 -> LatLng(it1, it2) } }
val markerOptions = latLng?.let { it1 -> MarkerOptions().position(it1) }
map.addMarker(markerOptions)
}
})
}
My Log is never displaying and it doesn't seem while debugging that mainView.zones.observe { } is called when I receive some new data in my ViewModel
In the onCacheReception(), replace:
_zones.value = zonesFromDB
by:
_zones.postValue(zonesFromDB)
in case your onCacheReception() function is called from a worker thread.

this weird run time exception keep coming up

This is the runtime exception: "lateinit property name Session has not been initialized" that keeps coming up.
class GalasatyActivity : BaseActivity() {
private lateinit var binding: ActivityGalasatyBinding
private val list = ArrayList<Post>()
private lateinit var readingTypes: String
private lateinit var student :String
private lateinit var statue :String
private var sessionType by Delegates.notNull<Int>()
private lateinit var nameSession:String
private lateinit var mSharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityGalasatyBinding.inflate(layoutInflater)
setContentView(binding.root)
setUpActionBar()
setupGalasatyList()
mSharedPreferences = getSharedPreferences(Constants.PREFERENCE_NAME, Context.MODE_PRIVATE)
//nameSession = findViewById<TextView>(R.id.tv_galsa).toString()
readingTypes = findViewById<TextView>(R.id.tv_reading_type).toString()
student = findViewById<TextView>(R.id.tv_student).toString()
statue = findViewById<TextView>(R.id.tv_statue).toString()
sessionType = findViewById<TextView>(R.id.tv_session_type).toString().toInt()
nameSession = findViewById<TextView>(R.id.tv_galsa).toString()
}
private fun setUpActionBar(){
setSupportActionBar(binding.toolBarGalasaty)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.toolBarGalasaty.setNavigationOnClickListener {
doubleBackToExit()
}
}
private fun setupGalasatyList(){
hideProgressBar()
//getGalasatDetails(name,readingTypes,student,statue,sessionType)
DataRetrofit.instance.getSessions(nameSession,readingTypes,student,statue,sessionType).enqueue(object :Callback<Post?>{
override fun onResponse(call: Call<Post?>, response: Response<Post?>) {
binding.rvGalasatyList.layoutManager = LinearLayoutManager(this#GalasatyActivity,LinearLayoutManager.VERTICAL,false)
binding.rvGalasatyList.setHasFixedSize(true)
val galsaAdapter = GalasatyItemListAdapter(this#GalasatyActivity, list)
binding.rvGalasatyList.adapter = galsaAdapter
galsaAdapter.setOnClickListener(object : GalasatyItemListAdapter.OnClickListener{
override fun onClick(position: Int, model: Post) {
startActivity(Intent(this#GalasatyActivity,GalsaActivity::class.java))
}
})
}
override fun onFailure(call: Call<Post?>, t: Throwable) {
Toast.makeText(this#GalasatyActivity, t.message, Toast.LENGTH_SHORT).show()
}
})
}
private fun getGalasatDetails(name:String, readingTypes: String, student:String, statement:String, sessionType:Int){
if (Constants.isInternetAvailable(this)){
val retrofit: retrofit2.Retrofit = retrofit2.Retrofit.Builder().baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
val service: DataAPI = retrofit.create(DataAPI::class.java)
val listCall: Call<Post> = service.getSessions(name,readingTypes,student,statement,sessionType)
showProgressDialog(resources.getString(R.string.please_wait))
listCall.enqueue(object :Callback<Post?> {
#RequiresApi(Build.VERSION_CODES.N)
override fun onResponse(call: Call<Post?>, response: Response<Post?>) {
if (response.isSuccessful){
hideProgressBar()
val galasatList: Post? = response.body()
val sessionResponseJsonString = Gson().toJson(galasatList)
val editor = mSharedPreferences.edit()
editor.putString(Constants.SESSIONS_RESPONSE_DATA, sessionResponseJsonString)
editor.apply()
setupUI()
Log.i("Response result","$galasatList")
}else{
when(response.code()){
400->{
Log.e("Error 400","Bad connection")
}404->{
Log.e("Error 404","Not found")
}else->{
Log.e("Error","Generic error")
}
}
}
}
override fun onFailure(call: Call<Post?>, t: Throwable) {
hideProgressBar()
Log.e("erorrr", t.message.toString())
}
})
}else{
Toast.makeText(this,"No internet connection", Toast.LENGTH_SHORT).show()
}
}
private fun setupUI(){
val galasatResponseJsonString = mSharedPreferences.getString(Constants.SESSIONS_RESPONSE_DATA,"")
if (!galasatResponseJsonString.isNullOrEmpty()) {
val galasatList = Gson().fromJson(galasatResponseJsonString, Post::class.java)
for (i in Post.toString()) {
readingTypes = galasatList.readingType
student = galasatList.student
statue = galasatList.student
nameSession = galasatList.name
}
}
}
}
You're initializing a view into a string which is outrightly wrong.
See, here you've declared a variable as lateinit var nameSession: String and then you're initializing it with a view as nameSession = findViewById<TextView>(R.id.tv_galsa).toString() , that too as String which actually doesn't make sense.
Second, the error is about nameSession which as I said earlier is used first in the line no. 64 - DataRetrofit.instance.getSessions(nameSession,..., so the code breaks at this very point and you think that it will work with others, just not this which is again not the case because your code is wrong.
See, There are two cases, Either you want to pass the text value of the TextView nameSession and others, or the view in the getSessions().
To passs the view, declare it as lateinit var nameSession: TextView, initialize it as nameSession = findViewById<TextView>(R.id.tv_galsa) and pass it as DataRetrofit.instance.getSessions(nameSession,...).
To pass the text value of the TextView, declare and initialize it exactly as my first point, just pass it as DataRetrofit.instance.getSessions(nameSession.text,...).
Do the 2nd point for others as well. You DON'T convert the view to String, you convert the value of the view to String, and to get the value, you use the .text property of the TextView as I've used in the 2nd point.(setText() and getText() in Java).
So, convert all your variables as Views, and then pass their value using the .text property. One more of doing it will be nameSession = binding.tv_galsa.text.

retrofit Give kotlin.KotlinNullPointerException

Data get from the Sql server and get data json. this json data parsing retofit2.
Created Login Activity but its give error
MainActivity.kt
class MainActivity : AppCompatActivity() {
internal lateinit var api : APIInterface
private var compositeDisposable : CompositeDisposable? = null
var userName : String? = null
var password : String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnLogin.setOnClickListener {
userName = tvUsername.text.toString()
password = tvPassword.text.toString()
getUserName(userName!!,password!!)
}
}
fun getUserName(user : String, pass : String){
val retrofit = APIClient.apIClient
if (retrofit != null) {
api = retrofit.create(APIInterface::class.java)
}
compositeDisposable!!.add(api.getLoginData(user, pass)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
if (it.success.equals(1)){
val intent = Intent(this,Company::class.java)
startActivity(intent)
Toast.makeText(this,"Login Successfully!!!",Toast.LENGTH_LONG).show()
}else{
Toast.makeText(this,"UserName or Password is Wrong!!!",Toast.LENGTH_LONG).show()
}
},{
Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
})
)
}
}
when Debbuger reached on compositeDisposable!!.add(api.getLoginData(user, pass) it's give Error kotlin.kotlinNullPointerException
RestApi Url :
http://localhost/Account/Login.php?user=ABC&pass=1
APIClient.kt
object APIClient {
val BASE_URL = "http://10.0.2.2/Account/"
var retrofit:Retrofit? = null
val apIClient:Retrofit?
get() {
if (retrofit == null)
{
retrofit = Retrofit.Builder().
baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
return retrofit
}
}
APIInterface.kt
interface APIInterface {
#GET("Login.php")
fun getLoginData(#Query("user") user : String,
#Query("pass") pass : String) : Observable<LoginList>
}
The most likely cause for the NullPointerException is that compositeDisposable is null.
At the beginning of MyActivity that variable is initialised to null and then it's never changed, so when you use the !! operator the exception is thrown.
I think you can initialise compositeDisposable directly with the correct value, i.e. something like val compositeDisposable = CompositeDisposable().
Also, val should be preferred over var whenever possible – as immutability is easier to control – and userName and password could probably be local variable or at least private