retrofit Give kotlin.KotlinNullPointerException - kotlin

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

Related

How to use the information stored in mutableStateOf in Jetpack Compose

I have information in json and I retrieve it using retrofit2, everything works fine, I get the data in a List.
I need this information to fill elements in Jetpack Compose for which I use mutableStateOf to save the states.
My function that I use is the following:
fun jsonParsing(
dataRecox: MutableState<List<Event>>
) {
val TAG_LOGS = "Mariox"
val retrofit = Retrofit.Builder()
.baseUrl("http://myserversample.com/pGet/track/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitAPI = retrofit.create(APIService1::class.java)
retrofitAPI.getRecolector().enqueue(object : Callback<List<Event>> {
override fun onResponse(
call: Call<List<Event>>,
response: Response<List<Event>>
) {
val data = response.body()
val mydata = data!!
dataRecox.value = mydata
Log.i(TAG_LOGS, Gson().toJson(data))
}
override fun onFailure(call: Call<List<Event>>, t: Throwable) {
t.printStackTrace()
}
})
}
Mymodel:
data class Event (
val deviceID : Int,
val statusCode : Int,
val accountID : String,
val speedKPH : Int,
.
.
.
}
My composable:
#Composable
fun Greeting(name: String) {
val dataRecox = remember {
mutableStateOf(emptyList<Event>())
}
jsonParsing(dataRecox)
println("======")
println(dataRecox) // ok data
println(dataRecox.value). // ok data
//Uncommenting println(dataRecox.value[0]) I get empty.
//println(dataRecox.value[0])
//Text(text = dataRecox.value[0].uniqueID)
}
When I do not use the information in the console, by calling Greeting("Android") all the data is printed correctly:
The problem comes when I want to use that information:
For example, if I want to print in console println(dataRecox.value[0]) here it returns empty. If I want to use it with a composable Text: Text(text = dataRecox.value[0].uniqueID) it also gives me empty.
Can someone explain to me why this happens, because when I start using the information the data becomes empty.
The way you're doing is totally different of the recommended way... here's my suggestion.
Define a class to represent the screen's state.
data class ScreenState(
val events: List<Event> = emptyList(),
val error: Throwable? = null
)
Use a ViewModel to perform the API request and keep the screen state.
class EventsViewModel : ViewModel()
private val _screenState = MutableStateFlow<ScreenState>(ScreenState())
val screenState = _screenState.asStateFlow()
init {
jsonParsing()
}
fun jsonParsing() {
val TAG_LOGS = "Mariox"
val retrofit = Retrofit.Builder()
.baseUrl("http://myserversample.com/pGet/track/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitAPI = retrofit.create(APIService1::class.java)
retrofitAPI.getRecolector().enqueue(object : Callback<List<Event>> {
override fun onResponse(
call: Call<List<Event>>,
response: Response<List<Event>>
) {
val data = response.body()
Log.i(TAG_LOGS, Gson().toJson(data))
_screenState.update {
ScreenState(it.events)
}
}
override fun onFailure(call: Call<List<Event>>, t: Throwable) {
t.printStackTrace()
_screenState.update {
ScreenState(error = t)
}
}
})
}
}
Instantiate the ViewModel and use it in your screen...
#Composable
fun Greeting(name: String) {
val vm = viewModel<EventsViewModel>()
val screenState by vm.screenState.observeAsState()
LazyColumn(Modifier.fillMaxSize()) {
items(screenState.items) {
Text(it. accountID)
}
}
}

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

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.

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)

LiveData observation won't update

I want to send some data to the server and show its response to the user.
I'm using MVVM so I created a repository like this:
class Repository {
fun getData(context: Context, word: String): LiveData<String> {
val result = MutableLiveData<String>()
val request = object : StringRequest(
Method.POST,
"https://.......",
Response.Listener {
result.value = it.toString()
},
Response.ErrorListener {
result.value = it.toString()
}) {
#Throws(AuthFailureError::class)
override fun getParams(): MutableMap<String, String> {
val params = HashMap<String, String>()
params["word"] = word
return params
}
}
val queue = Volley.newRequestQueue(context)
queue.add(request)
return result
}
}
which just sends 'word' to the server and get its response.
my view model class contains just a mutableLiveData and a function. it is like this:
class ViewModel(application: Application) : AndroidViewModel(application) {
var result = MutableLiveData<String>()
fun getData(word: String): LiveData<String> {
val repository = Repository()
result = repository.getData(getApplication(), word) as MutableLiveData<String>
return result
}
}
I set an observation for result in my main Activity, therefore it is my MainActivity codes:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val model = ViewModelProviders.of(this).get(ViewModel::class.java)
submit.setOnClickListener {
model.getData(search_txt.text.toString())
}
model.result.observe(this, Observer {
Log.i("Log", "observe is :$it")
text.text = it
})
}
but it doesn't work! I get the user's input using an edit text and after pressing a button, I call getData function which is in my View Model class. but it returns always null and observation won't work.
I try to put observe the method in my button listener, in this way I get the result but it seems it's not a correct way because after I rotate my phone, all data ware gone and I need to fetch data from the server again while it shouldn't.

Retrofit 2 Get Github Users API always returning null

I tried to get json from the https://github.com/users.
I want to show username : yehezkiell like https://github.com/yehezkiell.
The retrofit showing success result, but its always return null. I'm new in this retrofit, please help
this my code
val postService = DataRepository.create()
postService.getUser("yehezkiell").enqueue(object : Callback<Users>{
override fun onFailure(call: Call<Users>?, t: Throwable?) {
Log.e("retrofitnya","gagal ${t}")
}
override fun onResponse(call: Call<Users>?, response: Response<Users>?) {
Log.e("retrofitnya","berhasil")
val data = response?.body()
Log.e("retrofitnya","berhasil ${data?.name}")
}
})
Retrofit Instance
interface RetrofitInstance {
#GET("users/{username}")
fun getUser(#Path("username") username:String ): Call<Users>
}
Data repo
object DataRepository {
fun create(): RetrofitInstance {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://github.com")
.build()
return retrofit.create(RetrofitInstance::class.java)
}
}
Users.kt
open class Users {
#SerializedName("name")
#Expose
open var name: String? = null
#SerializedName("username")
#Expose
open var username: String? = null
#SerializedName("email")
#Expose
open var email: String? = null
}
For debugging process, instead of de-serialization to Users object immediately after response, should we do somethings like these? :
De-serialize it to plain string first.
interface RetrofitInstance {
#GET("users/{username}")
fun getUser(#Path("username") username: String): Call<String>
}
Just log that string to show what we really get.
override fun onResponse(call: Call<String>?, response: Response<String>?) {
val responseBody = response?.body() ?: ""
Log.e("retrofitnya","response body as string = ${responseBody}")
}
(If we want to use it as Users after that) do manually de-serialize it.
val user: Users = Gson().fromJson(responseBody, Users::class.java)
If it is not too confidential, plz give us how you declare that Users data object like, for example, this Foo and Bar.
data class Foo(
#SerializedName("bar") val bar: Bar?
)
data class Bar(
#SerializedName("name") val name: String?
)
I solved this by myself, actually its my silly miss understanding which is that end point is wrong.
In my wrong code
object DataRepository {
fun create(): RetrofitInstance {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://github.com")
.build()
return retrofit.create(RetrofitInstance::class.java)
}
}
That wrong end point is
https://github.com
The true one is
https://api.github.com/