How to get response from server after posting data using RxJava - android

I am trying to post data on server using retrofit2 and rxjava2 after data posted successfully on server I want to get response from server.I am using kotlin so how can I get server response in my app.
This is what I have done so far:
AddHero.kt
class AddHero : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_hero)
addHero.setOnClickListener {
if(hero.text.toString().equals("")){
Toast.makeText(applicationContext,"Enter superhero name",Toast.LENGTH_SHORT).show()
}
else if(movie.text.toString().equals("")){
Toast.makeText(applicationContext,"Enter movie name",Toast.LENGTH_SHORT).show()
}
else{
saveData()
}
}
}
private fun saveData() {
RetrofitClient.create().saveHero(hero.text.toString(),movie.text.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
}
}
RetrofitClient.kt
object RetrofitClient {
fun create():ApiService{
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(12,TimeUnit.SECONDS)
.readTimeout(12,TimeUnit.SECONDS)
.writeTimeout(12,TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://www.example.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.client(okHttpClient)
.build()
val service = retrofit.create(ApiService::class.java)
return service
}
}
ApiService.kt
interface ApiService {
#POST("createHero")
#FormUrlEncoded
fun saveHero(#Field("name") name:String,
#Field("movie") movie:String):Observable<Hero>
}
Hero.kt
data class Hero (
#SerializedName("name")
val name:String,
#SerializedName("movie")
val movie:String
)
Someone please let me know what I am doing wrong or missing. Any help would be appreciated.
THANKS

Your ApiService saveHero function returns Observable<Hero>, you should get your response in subscribe(onNext, onError), like this:
RetrofitClient.create().saveHero(hero.text.toString(), movie.text.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ hero -> Log.d("AddHeroTag", hero.toString()) },
{ error -> Log.e("AddHero", error.message, error) })
And don't forget to check if your object is non-null

Related

How to properly use Retrofit to call a JSON API in Kotlin

I've tried following several tutorials on calling an API and receiving a JSON response in Kotlin, and this is what I have so far:
interface APIService {
#GET("cursor/popular/10")
fun listRepos(): Call<Any?>
// #Path("user") user: String?
}
fun getURL(url: String): Call<Any?> {
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(APIService::class.java)
val data: Call<Any?> = service.listRepos()
Log.d("PRINT_JSON_HERE", "HERE: ${data}")
return data
}
When I call getURL("api_here") nothing happens, no error either.
Just wondering what I am doing wrong. I know it says the data class is "Any" but when I start getting a response I'll replace it with a proper data class.
This code is inside a class/ViewModel(). What am I doing wrong?
Api call returns a response and you need to implement onResponse and onFailure override method in your program after
val service = retrofit.create(APIService::class.java)
this put this code
service.enqueue(object : Callback<List<userItem>> {
override fun onResponse(
call : Call<List<userItem>>,
response: Response<List<userItem>>
) {
var data = response.body()
Log.d("data", data.toString)
}
override fun onFailure(call : Call<List<userItem>> , t : Throwable) {
"print toast if an error occurred"
}
)}
}
}
in this code userItem is a data class that can get data from the api.
I hope this can help you.
):

Kotlin coroutines doesn't work with retrofit - no response

I'm trying to learn coroutines with retrofit and hilt.
There is simple api github https://api.github.com/users/JakeWharton/repos
But my code give in log:
D/OkHttp: --> GET https://api.github.com/users/JakeWharton/repos
D/OkHttp: --> END GET
without any reponse, despite the fact that using postman I get list with repos.
In my function loadData() debugger stop on the 1st lane, it doesn't come to println, something is wrong but don't know what.
my codes:
#Provides
fun provideGitHubService(retrofit: Retrofit): GitHubService{
return retrofit.create(GitHubService::class.java)
}
#Provides
fun provideOkHttpClient(): OkHttpClient {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return OkHttpClient
.Builder()
.addInterceptor(loggingInterceptor)
.build()
}
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://github.com") // don't know how to remove it but it will be override anyway
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
}
private fun getRepos() {
viewModelScope.launch {
loadData()
}
}
private suspend fun loadData() {
val response = service.getRepos()
println(response). //debugger doesn't come here
}
interface GitHubService {
#GET("https://api.github.com/users/JakeWharton/repos")
suspend fun getRepos() : Call<List<User>>
}
data class User(
#SerializedName("name") val name: String
)
You don't need Call when using suspend. Please update the getRepos to:
// annotations omitted
suspend fun getRepos() : List<User>
I think you did something wrong in the instance of retrofit. try this.
class User {
#Expose
#SerializedName("name")
var name: String? = null
}
interface GitHubService {
#GET("users/JakeWharton/repos")
suspend fun getRepos(): Call<List<User>>
}
fun provideGitHubService(retrofit: Retrofit): GitHubService{
return retrofit.create(GitHubService::class.java)
}
class Example {
private val TAG = "Example"
/* OKHTTP CLIENT */
private fun provideOkHttpClient(): OkHttpClient {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return OkHttpClient
.Builder()
.addInterceptor(loggingInterceptor)
.build()
}
/* RETROFIT INSTANCE */
private fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.github.com/") // don't know how to remove it but it will be override anyway
.addConverterFactory(GsonConverterFactory.create())
.client(provideOkHttpClient())
.build()
}
/* LOADING DATA */
suspend fun loadData() {
val apiInterface = provideGitHubService(provideRetrofit())
val call: Call<List<User>> = apiInterface.getRepos()
call.enqueue( object : Callback<List<User>> {
override fun onResponse(call: Call<List<User>>, response: Response<List<User>>) {
for (users in response.body()!!){
Log.e(TAG, "NAME: ${users.name}")
}
}
override fun onFailure(call: Call<List<User>>, t: Throwable) {
Log.e(TAG, "onFailure: ${t.message}")
}
})
}
}

How to parse response with retrofit2 Kotlin

I'm stuck with parsing the response. In Swift I can make a codable to help parsing the json response. I'm new to Kotlin and I'm working on someone else existing project. I made a data class for string and boolean but I don't know the syntax to parse it. Please help and thank you.
The responseBody json
{
"bearerToken": "########",
"staySignIn": false
}
//Interface
interface PostInterface {
class User(
val email: String,
val password: String
)
#POST("signIn")
fun signIn(#Body user: User): Call<ResponseBody>
//Network handler
fun signIn(email: String, password: String): MutableLiveData<Resource> {
val status: MutableLiveData<Resource> = MutableLiveData()
status.value = Resource.loading(null)
val retrofit = ServiceBuilder.buildService(PostInterface::class.java)
retrofit.signIn(PostInterface.User(email, password)).enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
errorMessage(status)
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.code() == 200) {
try {
status.value = //how to parse using the model??
} catch (ex: Exception) {
parseError(400, response.body().toString(), status)
}
} else {
//do something...
}
}
})
return status
}
//Model
data class SignInModel(
#field:SerializedName("bearerToken")
val bearerToken: String? = null,
#field:SerializedName("staySignIn")
val staySignIn: Boolean? = null
)
//Storing value class
class RrefManager constructor(var applicationContext: Context) {
private fun getSharedPrefEditor(): sharedPrefEditor.Editor {
return applicationContext.getSharedPrefEditor(prefStorageName, Context.MODE_PRIVATE).edit()
}
public fun setBearerToken(token: String) {
getSharedPrefEditor().putString("bearerToken", token).apply()
}
public fun setStaySignIn(enabled: Boolean) {
getSharedPrefEditor().putBoolean("staySignIn", enabled).apply()
}
}
//SignIn Button
viewModel.signIn().observe(viewLifecycleOwner, androidx.lifecycle.Observer { v ->
if (v.status == Resource.Status.SUCCESS) {
val model = v.data as SignInModel
pref.setToken(model.token as String) //storing value
pref.setTwoFactorEnabled(model.twoFactorEnabled as Boolean) //storing value
} else if (v.status == Resource.Status.ERROR) {
//do something...
}
})
I think your best option to achieve something like the codable in swift is to use Gson library for parsing api responses.
When you create the retrofit instance you pass the gson converter to the builder like:
val retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
After you have done that you can make the api return the response you have as the data class, like:
//Interface
interface PostInterface {
#POST("signIn")
fun signIn(#Body user: User): Call<SignInModel>
}
To read the answer from the callback on your class, the response inside the network call is already parsed into your model in the callback. All the retrofit callback should be changed to receive Callback and then you can access directly like status.value = response.body()
For more info you can consult the retrofit library page where it gives all the details and explanations on how to use it correctly.
https://square.github.io/retrofit/

Retrofit response is always null

I'm trying to get user name from gitHub Api but retrofit response return always null. When I'm trying to show user name in toast I see : null. I tried change retrofit path but it didn't work. Everything looks fine but I don't know why I get this error all the time.
interface GitHubApi{
#GET("/users/{user}")
fun getUser(#Path("user") user: String): Call<User>
companion object Factory {
fun getClient(): GitHubApi {
val url = "https://api.github.com/"
val interceptor = HttpLoggingInterceptor()
.apply { level = HttpLoggingInterceptor.Level.BODY }
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
return retrofit.create(GitHubApi::class.java)
}
}}
user model:
data class User(val userName: String)
MainActivity:
private fun createApiService() : GitHubApi{
val url = "https://api.github.com/"
val interceptor = HttpLoggingInterceptor()
.apply { level = HttpLoggingInterceptor.Level.BODY }
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
return retrofit.create(GitHubApi::class.java)
}
private fun loadData() {
val api = GitHubApi.getClient()
api.getUser("fabpot").enqueue(object : Callback<User> {
override fun onFailure(call: Call<User>, t: Throwable) {
t.printStackTrace()
}
override fun onResponse(
call: Call<User>,
response: Response<User>
) {
if (!response.isSuccessful) {
runOnUiThread { showErrorMessage(response.code().toString()) }
}
response.body()?.let { showErrorMessage(it.repoName) }
}
})
}
There is no key userName exist in github api.
try editing your data class this way.
data class User(val name: String)
Try removing the beginning slash "/" in your #GET method.

Infinite updating of the RecyclerView list

With the help of dagger and rxJava I update the list in a RecyclerView. Everything works well, the list is displayed. But the problem is that in the logs I see how this list is updated every second. What could be the problem? In a similar project but in Java everything works correctly, the list is updated once at startup.
My Network Module:
#Module(includes = [ViewModelModule::class])
class NetworkModule {
companion object {
const val KEY = "key"
const val BASE_URL = "base_url"
}
#Provides
#Singleton
fun provideOkHttp(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val original = chain.request()
val originalHttpUrl = original.url
val url = originalHttpUrl.newBuilder()
//.addQueryParameter("apikey", KEY)
.build()
val requestBuilder = original.newBuilder()
.url(url)
.header("apikey", KEY)
val request = requestBuilder.build()
return chain.proceed(request)
}
})
// logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
httpClient.addInterceptor(logging)
return httpClient.build()
}
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(provideOkHttp())
.build()
}
#Provides
#Singleton
fun provideContactsService(retrofit: Retrofit) : ContactsService{
return retrofit.create(ContactsService::class.java)
}
}
My ViewModel:
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
loadData()
return mutableLiveData
}
fun loadData() {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
getContactMutableLiveData().value = t
}
override fun onError(e: Throwable) {
}
})
)
}
}
And my Activity:
contactsViewModel.getContactMutableLiveData().observe(this#ContactListActivity, Observer {
mAdapter = ContactsAdapter(this#ContactListActivity, it as ArrayList<ContactsModel>)
recycler_contacts.layoutManager =
LinearLayoutManager(applicationContext, OrientationHelper.VERTICAL, false)
recycler_contacts.adapter = mAdapter
recycler_contacts.setHasFixedSize(true)
mAdapter.sortByName()
})
Okay if you only want to update your data list only once... I would recommend you look into a single live event that would trigger the reloading of your recycler view
As such
//in repo
private SingleLiveEvent<Boolean> listHasBeenUpdate=new SingleLiveEvent<>();
//setItsGetterInTheRepo
public SingleLiveEvent<Boolean> getListHasBeenUpdated(){
return listHasBeenUpdated();
}
//uponSucessfuly fetching your list from retrofit
listHasBeenUpdated=true;
//pass list to viewmodel
then in the ViewModel, I would set the list to be an Observable Data which would be updated once it's fetched from retrofit (Consider using room db to store this)
//use a setter to set the list from Repo
ObservableField<List<Contacts>> list=new ObservableField<>();
public SingleLiveEvent<List<Contacts>> fetchContacts(){
return myRepo.getListHasBeenUpdated();
}
In your activity class now observe the single live event like so
viewModel.fetchContacts().observe(this,contacts->{
if(contacts){
//update Recycler
}
});
hope this helps you.
It was a logical error. You need to rewrite the loadData function as shown below
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
mutableLiveData.value = t
}
override fun onError(e: Throwable) {
}
}))
return mutableLiveData
}
}

Categories

Resources