Unable to fetch JSON fromlocal host with retrofit in Android - android

I am trying to fetch JSON from localhost but unable to do so.
I can access the file by typing the URL (http://10.0.2.2/evapo/json_get_data.php) in browser of virtual device but somehow am not able to access it from within code.
Main Activity Class
class MainActivity : AppCompatActivity()
{
private val CROP_BASE_URL="http://10.0.2.2/"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
makeAPIRequest()
}
fun makeAPIRequest()
{
val api:APIRequest =Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(APIRequest::class.java)
GlobalScope.launch {
try {
Log.d("TEST", "makeAPIRequest: HERE")
val response:ServerResponse=api.getCropData()
//control never reach here
Log.d("TEST", "makeAPIRequest:"+response)
}catch (e:Exception)
{
e.printStackTrace()
}
}
}
}
API Request Interface
interface APIRequest {
#GET("evapo/json_get_data.php")
suspend fun getCropData():ServerResponse
}
Crop Response Class
data class CropResponse(
#SerializedName("server_response")
val serverResponse: List<ServerResponse>
)
Server Response Class
data class ServerResponse(
#SerializedName("cropName")
val cropName: String,
#SerializedName("eigth_month")
val eigthMonth: String,
#SerializedName("eleventh_month")
val eleventhMonth: String,
#SerializedName("fifth_month")
val fifthMonth: String,
#SerializedName("first_month")
val firstMonth: String,
#SerializedName("fourth_month")
val fourthMonth: String,
#SerializedName("nineth_month")
val ninethMonth: String,
#SerializedName("second_month")
val secondMonth: String,
#SerializedName("seventh_month")
val seventhMonth: String,
#SerializedName("sixth_month")
val sixthMonth: String,
#SerializedName("sowingDate")
val sowingDate: String,
#SerializedName("tenth_month")
val tenthMonth: String,
#SerializedName("third_month")
val thirdMonth: String,
#SerializedName("twelveth_month")
val twelvethMonth: String
)
json_get_data.php returns
{
"server_response": [
{
"cropName": "Cotton",
"sowingDate": "03-03-2020",
"first_month": "85.59",
"second_month": "185.134",
"third_month": "261.88",
"fourth_month": "388.608",
"fifth_month": "312.979",
"sixth_month": "219.848",
"seventh_month": "193",
"eigth_month": "0",
"nineth_month": "0",
"tenth_month": "0",
"eleventh_month": "0",
"twelveth_month": "0"
}
]
}
Log Cat
2020-09-01 13:10:06.608 10803-10828/dummy.dummy D/TEST: makeAPIRequest: HERE
Linking Stack trace from log cat, because character limit was reached
Stack trace
Solution
Changed the return type as specified in accepted answer.
Was passing a wrong URL instead of "http://10.0.2.2/" to baseURL()

Since your API returns a list you need to return
suspend fun getCropData(): CropResponse
in API Request Interface

Since your api is returning a list, you need to return CropResponse wrapped in the Resonse object from Retrofit in the getCropData() function as follows.
interface APIRequest {
#GET("evapo/json_get_data.php")
suspend fun getCropData() : Response<CropResponse>
}
Then, inside the coroutine you get the data as follows:
GlobalScope.launch {
try {
Log.d("TEST", "makeAPIRequest: HERE")
val response: CropResponse = api.getCropData().body()
val serverResponse: List<ServerResponse> = response.serverResponse
Log.d("TEST", "makeAPIRequest:"+response)
}catch (e:Exception)
{
e.printStackTrace()
}
}

Related

How to parse JSON data through Retrofit, Coroutine with Kotlin in android studio

I'm write code for JSON parsing, but JSON data is not retrieved. What would be the issue?
Below is the Data object class
data class UsersItem(
val avatar_url: String,
val events_url: String,
val followers_url: String,
val following_url: String,
val gists_url: String,
val gravatar_id: String,
val html_url: String,
val id: Int,
val login: String,
val node_id: String,
val organizations_url: String,
val received_events_url: String,
val repos_url: String,
val site_admin: Boolean,
val starred_url: String,
val subscriptions_url: String,
val type: String,
val url: String
)
Below is the List defined
class Users : ArrayList<UsersItem>()
Below is the ApiUtilities
object ApiUtilities {
private val BaseURl = "https://api.github.com/"
fun getInstance(): Retrofit {
return Retrofit.Builder().baseUrl(BaseURl)
.addConverterFactory(GsonConverterFactory.create()).build()
}
}
Below is the ApiInterface
interface ApiInterface {
#GET("/Users")
suspend fun getUsers(): Response<Users>
}
Below is the MainActivity,
I just tested to get the JSON Response with login id, but i'm not getting the result.
Please correct the code or give any suggestions
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val helloTxt = findViewById<TextView>(R.id.txt) as TextView
val userApi = ApiUtilities.getInstance().create(ApiInterface::class.java)
GlobalScope.launch {
val result = userApi.getUsers()
if (result.isSuccessful && result.body() != null) {
result.body()?.forEach {
Toast.makeText(this#MainActivity, "${it.id}", Toast.LENGTH_LONG).show()
Log.d("Manju", "${it.login}")
helloTxt.setText("${it.login}")
}
}
}
}
}
I think that you schould use "/users" with small "u":
interface ApiInterface {
#GET("/users")
suspend fun getUsers(): Response<Users>
}
insted of:
interface ApiInterface {
#GET("/Users")
suspend fun getUsers(): Response<Users>
}

How to Parse Json in Kotlin Using Retrofit?

i am new to kotlin and i am in learning phase. I have followed many links but didn't able to understand completely.
I want Json response to show in my textview.
Problem: 1
I have tried this code but was unable to get data, but i want to get the items inside data object. Quote and author are coming null.
{
"status": 200,
"message": "Success",
"data": {
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
},
"time": "0.14 s"
}
Problem: 2
I dont know how to parse this response in textview
object ServiceBuilder {
private val client = OkHttpClient.Builder().build()
private val retrofit = Retrofit.Builder()
.baseUrl("https://url.com.pk/") // change this IP for testing by your actual machine IP
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}}
RestApi
interface RestApi{
#Headers("Content-Type: application/json")
#POST("api/getquotes")
abstract fun addUser(#Body userData: UserInfo): Call<UserInfo>}
RestAPiService
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<UserInfo>
{
override fun onFailure(call: Call<UserInfo>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<UserInfo>, response: Response<UserInfo>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser)
}
}
)
}
}
UserInfo
data class UserInfo (
#SerializedName("Quote")
val quote : String,
#SerializedName("Author")
val author : String
)
MainActivity
fun getQuotes() {
val apiService = RestApiService()
val userInfo = UserInfo("","")
apiService.addUser(userInfo) {
Log.d("Error registering user","errter")
/*if ( != null)
{
// it = newly added user parsed as response
// it?.id = newly added user ID
} else {
Log.d("Error registering user","errter")
}*/
}
}
Any help would be appreciated :)
Status, message and data are all part of the response so you need to take care of that. For example this
data class AddUserResponse(
val `data`: UserInfo, //like you defined it
val message: String,
val status: Int,
val time: String
)
This means parameter and response are different so the RestApi needs to be changed to this
abstract fun addUser(#Body userData: UserInfo): Call<AddUserResponse>}
This in turn also change the types in the service like
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<AddUserResponse>
{
override fun onFailure(call: Call<AddUserResponse>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<AddUserResponse>, response: Response<AddUserResponse>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser.data)
}
}
)
}
}
now in getQuotes you will have that it is a UserInfo object
apiService.addUser(userInfo) {
val returnedUserInfo = it
}
just follow my steps :
File->settings->Plugins
search for JSON To Kotlin class and install it
again click on File->New->Kotlin Data class from JSON
paste your json code here and click on generate. It will generate POJO classes and you will good to go.
The first thing I noticed, is that the data in your json is:
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
While your UserInfo defined #SerializedName("message") for Quote.

Unable to invoke no-args constructor for retrofit2.Call MVVM Coroutines Retrofit

I want to use coroutines in my project only when I use coroutines I get the error :Unable to invoke no-args constructor. I don't know why it's given this error. I am also new to coroutines.
here is my apiclient class:
class ApiClient {
val retro = Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
Here is my endpoint class:
#GET("v2/venues/search")
suspend fun get(
#Query("near") city: String,
#Query("limit") limit: String = Constants.limit,
#Query("radius") radius: String = Constants.radius,
#Query("client_id") id: String = Constants.clientId,
#Query("client_secret") secret: String = Constants.clientSecret,
#Query("v") date: String
): Call<VenuesMainResponse>
my Repository class:
class VenuesRepository() {
private val _data: MutableLiveData<VenuesMainResponse?> = MutableLiveData(null)
val data: LiveData<VenuesMainResponse?> get() = _data
suspend fun fetch(city: String, date: String) {
val retrofit = ApiClient()
val api = retrofit.retro.create(VenuesEndpoint::class.java)
api.get(
city = city,
date = date
).enqueue(object : Callback<VenuesMainResponse>{
override fun onResponse(call: Call<VenuesMainResponse>, response: Response<VenuesMainResponse>) {
val res = response.body()
if (response.code() == 200 && res != null) {
_data.value = res
} else {
_data.value = null
}
}
override fun onFailure(call: Call<VenuesMainResponse>, t: Throwable) {
_data.value = null
}
})
}
}
my ViewModel class:
class VenueViewModel( ) : ViewModel() {
private val repository = VenuesRepository()
fun getData(city: String, date: String): LiveData<VenuesMainResponse?> {
viewModelScope.launch {
try {
repository.fetch(city, date)
} catch (e: Exception) {
Log.d("Hallo", "Exception: " + e.message)
}
}
return repository.data
}
}
part of activity class:
class MainActivity : AppCompatActivity(){
private lateinit var venuesViewModel: VenueViewModel
private lateinit var adapter: HomeAdapter
private var searchData: List<Venue>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText = findViewById<EditText>(R.id.main_search)
venuesViewModel = ViewModelProvider(this)[VenueViewModel::class.java]
venuesViewModel.getData(
city = "",
date = ""
).observe(this, Observer {
it?.let { res ->
initAdapter()
rv_home.visibility = View.VISIBLE
adapter.setData(it.response.venues)
searchData = it.response.venues
println(it.response.venues)
}
})
this is my VenuesMainResponse data class
data class VenuesMainResponse(
val response: VenuesResponse
)
I think the no-args constructor warning should be related to your VenuesMainResponse, is it a data class? You should add the code for it as well and the complete Log details
Also, with Coroutines you should the change return value of the get() from Call<VenuesMainResponse> to VenuesMainResponse. You can then use a try-catch block to get the value instead of using enqueue on the Call.
Check this answer for knowing about it and feel free to ask if this doesn't solve the issue yet :)
UPDATE
Ok so I just noticed that it seems that you are trying to use the foursquare API. I recently helped out someone on StackOverFlow with the foursquare API so I kinda recognize those Query parameters and the Venue response in the code you provided above.
I guided the person on how to fetch the Venues from the Response using the MVVM architecture as well. You can find the complete code for getting the response after the UPDATE block in the answer here.
This answer by me has code with detailed explanation for ViewModel, Repository, MainActivity, and all the Model classes that you will need for fetching Venues from the foursquare API.
Let me know if you are unable to understand it, I'll help you out! :)
RE: UPDATE
So here is the change that will allow you to use this code with Coroutines as well.
Repository.kt
class Repository {
private val _data: MutableLiveData<mainResponse?> = MutableLiveData(null)
val data: LiveData<mainResponse?> get() = _data
suspend fun fetch(longlat: String, date: String) {
val retrofit = Retro()
val api = retrofit.retro.create(api::class.java)
try {
val response = api.get(
longLat = longlat,
date = date
)
_data.value = response
} catch (e: Exception) {
_data.value = null
}
}
}
ViewModel.kt
class ViewModel : ViewModel() {
private val repository = Repository()
val data: LiveData<mainResponse?> = repository.data
fun getData(longLat: String, date: String) {
viewModelScope.launch {
repository.fetch(longLat, date)
}
}
}
api.kt
interface api {
#GET("v2/venues/search")
suspend fun get(
#Query("ll") longLat: String,
#Query("client_id") id: String = Const.clientId,
#Query("client_secret") secret: String = Const.clientSecret,
#Query("v") date: String
): mainResponse
}
MainActivity.kt
private val viewModel by viewModels<ViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.getData(
longLat = "40.7,-74",
date = "20210718" // date format is: YYYYMMDD
)
viewModel.data
.observe(this, Observer {
it?.let { res ->
res.response.venues.forEach { venue ->
val name = venue.name
Log.d("name ",name)
}
}
})
}
}

How to fix retrofit errorjava.lang.IllegalStateException android

I have parse the data from this link
https://api.androidhive.info/contacts/
But I am getting error as
E/onĀ FailureĀ :: retrofit errorjava.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Below is the code which I done.
class RetrofitService {
val liveUserResponse:MutableLiveData<List<ContactBase>> = MutableLiveData()
companion object Factory {
var gson = GsonBuilder().setLenient().create()
fun create(): ApiInterface {
Log.e("retrofit","create")
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("https://api.androidhive.info/")
.build()
return retrofit.create(ApiInterface::class.java)
}
}
fun loadContactsData(): MutableLiveData<List<ContactBase>>? {
Log.e("loadAndroidData","yes")
val retrofitCall = create().getContacts()
retrofitCall.enqueue(object : Callback<List<ContactBase>> {
override fun onFailure(call: Call<List<ContactBase>>, t: Throwable?) {
Log.e("on Failure :", "retrofit error"+t)
Log.e("on Failure :", "retrofit error"+call)
}
override fun onResponse(call: Call<List<ContactBase>>, response: retrofit2.Response<List<ContactBase>>) {
val list = response.body()
for (i in list.orEmpty()){
Log.e("on response 1:", ""+i)
}
liveUserResponse.value = list
Log.e("hasActiveObservers 1", liveUserResponse.hasActiveObservers().toString()+" check")
Log.e("on response 2 :", liveUserResponse.toString()+" check")
}
})
return liveUserResponse
}
}
But it's always going to Failure state.
data class ContactBase (val contacts : List<Contacts>)
data class Contacts (
val id : String,
val name : String,
val email : String,
val address : String,
val gender : String,
val phone : Phone
)
data class Phone (
val mobile : String,
val home : String,
val office : String
)
interface ApiInterface{
#GET("contacts/")
fun getContacts(): Call<List<ContactBase>>
}
class AndroidViewModel:ViewModel(){
private val retrofitService = RetrofitService()
fun getContactsData(): MutableLiveData<List<ContactBase>>?{
return retrofitService.loadContactsData()
}
}
I cross verified the url too and pojo class. But it always go to failure case in retrofit.
The error says that the incomming JSON starts with a { and not with a [ which means it's an object and not an array of objects.
So you should be having a class that has an array of Contact in order to make that call successful.
A small heads up: Since you are using GSON, your model classes would need the implementation of #SerializedName(string) annotation above the variables.

Kotlin API call using Retrofit

I'm new to Kotlin, Android and OOP in general (Natural-ADABAS background, never did Java, C++, etc) so I'm pretty desperate.
I have an API whose data looks like this, an array of book details:
API data sample
I'm confused about data models. I know it's supposed to look like how the data in the API and return an array but how exactly do I code it in Kotlin? And then how do I parse it? I've read some tutorials but they all differ. Some use an object, and some use a class.
I'm also probably breaking some standard by putting everything in the main activity but I haven't gotten to that part yet.
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
class MainActivity : AppCompatActivity()
{
private val api: RestAPI = RestAPI()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val apiGetBooks = api.getBooksList("token123123123")
val response = apiGetBooks.execute()
if (response.isSuccessful) {
val books = response.body()?.title
println(books)
} else {
println("error on API") // What do I do?
}
}
object Model {
val ResultArray : MutableList<BookProperties>? = null
}
data class BookProperties (val id: Int,val title: String, val coverURI: String, val pageURI: String, val pageCount: Int, val languageId: Int,val description: String, val isFree: Boolean) {
}
private val buriApi: MainActivity.BooksAPI? = null
class RestAPI {
private val buriApi: BooksAPI
init {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.someurl.com")
.addConverterFactory(MoshiConverterFactory.create())
.build()
buriApi = retrofit.create(BooksAPI::class.java)
}
fun getBooksList(token: String): Call<BookProperties>{
return buriApi.getBooks(token)
}
}
fun getBooksList(token: String): Call<MainActivity.BookProperties> {
return buriApi!!.getBooks(token)
}
interface BooksAPI {
#GET("/v1/books")
fun getBooks (#Query("token")token: String) : Call<BookProperties>
}
}
After much googling, I finally solved my problem thanks to How to Quickly Fetch Parse JSON with OkHttp and Gson on YouTube.
fun fetchBooks () {
println("fetching books")
val url = "https://api.someurl.com/v1/books?"
val request = Request.Builder().url(url).build()
println(request)
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call?, response: Response?) {
val body = response?.body()?.string()
println(body)
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
e?.printStackTrace()
}
})
}
Still need to format the data and figure out how to turn on wifi in my Android emulator but at least I can consume the JSON.
Let's start with a sample and I guess you can map it accordingly to your requirement.
I don't have your JSON as text so I am giving an example of mine.
sample JSON response
{
"status": true,
"message": "User created Successfully.",
"response": {
"user": {
"id": 12,
"email": "testmail#gmailtest.com"
},
"token": "eyJlbWFpbCI6ImVzaGFudHNhaHUxMTBAZ21hc2kyMmwuY29tIiwidXNlcklkIjoxNSwiaWF0IjoxNTIxNTYyNjkxfQ"
}
}
so create a new class and name it something like this
CreateResponse.kt
and just map those objects and arrays from json to data classes and list here.
data class CreateUserResponse(override val status: Boolean? = null,
override val message: String? = null,
val response: Response? = null)
data class Response(val user: User?, val token: String)
data class User(val id: Int, val email: String)
easy right, now with Kotlin you can declare your data classes without creating separate files each time for each object just create one file and declare all of them at once.
I'm attaching some of the resources here which may help you understand the things better.
https://antonioleiva.com/retrofit-android-kotlin/
https://segunfamisa.com/posts/using-retrofit-on-android-with-kotlin

Categories

Resources