I need to send GET request with body from android application, but it seems that volley ignore it (body). It's always empty on the server side.
I was trying to send body in request and ovverride getBody() - no effect.
In Request.java I saw the comment that getBody only send body for POST or PUT.
Maybe I need to override another method or need to use another library for such task?
My last custom request:
package by.lsd.rmapiconnector
import com.android.volley.NetworkResponse
import com.android.volley.ParseError
import com.android.volley.Response
import com.android.volley.toolbox.HttpHeaderParser
import com.android.volley.toolbox.JsonRequest
import org.json.JSONArray
import org.json.JSONException
import java.io.ByteArrayOutputStream
import java.io.ObjectOutputStream
import java.io.UnsupportedEncodingException
import java.nio.charset.Charset
class CustomJsonRequest(
method: Int,
url: String?,
requestObject: HashMap<String, Any>,
private val mResponseListener: Response.Listener<JSONArray>,
errorListener: Response.ErrorListener?
) : JsonRequest<JSONArray>(
method,
url,
requestObject.toString(),
mResponseListener,
errorListener
) {
private val mRequestObject = requestObject
override fun deliverResponse(response: JSONArray) {
mResponseListener.onResponse(response)
}
override fun parseNetworkResponse(response: NetworkResponse): Response<JSONArray> {
return try {
val json = String(response.data, Charset.forName(HttpHeaderParser.parseCharset(response.headers)))
try {
Response.success(
JSONArray(json),
HttpHeaderParser.parseCacheHeaders(response)
)
} catch (e: JSONException) {
Response.error(ParseError(e))
}
} catch (e: UnsupportedEncodingException) {
Response.error(ParseError(e))
}
}
override fun getHeaders(): Map<String, String> {
val headers: HashMap<String, String> = HashMap()
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
//headers["Transfer-Encoding"] = "chunked"
return headers
}
override fun getBody(): ByteArray {
val byteOut = ByteArrayOutputStream()
val out = ObjectOutputStream(byteOut)
out.writeObject(mRequestObject)
return byteOut.toByteArray()
}
}
Related
error : "Not enough information to infer type variable T", I get this error while parsing Json
Here is my code:
package com.example.chatopenai
import android.os.Bundle
import android.util.Log
import android.widget.EditText
import android.widget.ImageButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.HeaderMap
import retrofit2.http.POST
class MainActivity : AppCompatActivity() {
private lateinit var questionEditText: EditText
private lateinit var answerTextView: TextView
private lateinit var generateButton: ImageButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get references to the UI elements
questionEditText = findViewById(R.id.message_input)
answerTextView = findViewById(R.id.textAns)
generateButton = findViewById(R.id.sendBtn)
// Set up the generate button click listener
generateButton.setOnClickListener {
// Get the user's question from the EditText
val question = questionEditText.text.toString()
// Send the request to the API
generateText(question)
}
}
// Service interface for the OpenAI API
interface OpenAIService {
#POST("v1/images/generations")
fun generateText(
#HeaderMap headers: Map<String, String>,
#Body request: Map<String, Any>
): Call<ResponseBody>
}
// Data class to hold the API response
data class ResponseData(val data: Data)
data class Data(val text: String)
private fun generateText(prompt: String) {
// Create a logger for debugging purposes
val logger = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
// Add the logger to the HTTP client
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
// Configure the Retrofit object
val retrofit = Retrofit.Builder()
.baseUrl("https://api.openai.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
// Create an instance of the OpenAIService
val service = retrofit.create(OpenAIService::class.java)
// Set the request parameters
// Set the request parameters
val request = mapOf(
"prompt" to prompt,
"model" to "text-davinci-002"
)
// Set the request headers
val headers = mapOf(
"Content-Type" to "application/json",
"Authorization" to "Bearer MY_API_KEY"
)
// Make the API request
service.generateText(headers, request).enqueue(object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
if (response.isSuccessful) {
// Parse the JSON response
val gson = Gson()
val responseString = response.body()!!.string()
val type = object : TypeToken<ResponseData>() {}.type
//I am getting error on below line on fromJson
val responseData = gson.fromJson(response.body()?.string(), type)
Log.d("JSON response", responseString)
Log.d("Type", type.toString())
// Display the response text in the TextView
answerTextView.text = responseData.data.text
} else {
// Display the error message in the TextView
answerTextView.text = response.errorBody()?.string()
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
// Display the error message in the TextView
answerTextView.text = t.message
}
})
}
}
Please help me to fix this bug or let me know I missed any dependencies. I have mentioned where is the bug on the code.
Aplogize for my bad code
I tried to modify the code but not resolved. May be I missed some dependencies I don't know. Please help me to solve and fix this error
I am developing a meme app in which you can share memes. I have used
"https://meme-api.herokuapp.com/gimme"
for API. Whenever I try to run the code I do not get the image.
I am new to kotlin and stuck in this for hours
MainActivity.kt
package com.example.memeapp
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.bumptech.glide.Glide
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun loadMeme(){
val queue = Volley.newRequestQueue(this)
val url = "https://meme-api.herokuapp.com/gimme"
// Request a string response from the provided URL.
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, url,null,
{ response ->
val url = response.getString("url")
val meme:ImageView= findViewById(R.id.memeImageView)
print(url)
Glide.with(this).load(url).into(meme)
},
{
})
queue.add(jsonObjectRequest)
}
fun nextMeme(view: View) {
}
fun shareMeme(view: View) {
}
}
the image is not showing but the buttons are showing, Note: the buttons are currently null.
// Request a string response from the provided URL.
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, url,null,
Response.Listener {response ->
val url = response.getString("url")
Glide.with(this).load(url).into(meme)
},
Response.ErrorListener{
Toast.makeText(this,"Something wrong", Toast.LENGTH_lONG).show()
})
queue.add(jsonObjectRequest)
}
I am trying to get data from an Http request to my own API. Running the request in my browser (swapping the IP with localhost) gets me:
["Herbalism","Mining","Skinning","Alchemy","Blacksmithing","Enchanting","Engineering","Inscription","Jewelcrafting","Leatherworking","Tailoring","Archaeology","Cooking","Fishing"]
I am using the following code, modified from the example provided here: https://www.tutorialspoint.com/how-to-use-volley-library-to-parse-json-in-android-kotlin-app
It does not print my "Print anything at all". From what I can tell this does nothing. I have tried many different things including suggestions from here: Can I do a synchronous request with volley? and here: Volley Timeout Error and feel that I am in no way closer to getting this request to work. The firewall permits this traffic. The catch JSONException and Response.ErrorListener are also not putting anything out. I am using plain http, no certs or anything. Ultimately I want to populate a Spinner with this data but for now I can't even get this most basic implementation to work.
package com.wowahapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.*
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.*
import org.json.JSONException
class AddRecipeActivity : AppCompatActivity() {
lateinit var searchTextView : TextView
private var requestQueue : RequestQueue? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_recipe)
searchTextView = findViewById<TextView>(R.id.searchTextView) as TextView
getAllProfessions(searchTextView)
}
fun getAllProfessions(searchText : TextView) {
val url = "http://192.168.0.24:49155/allprofessions"
val request = JsonObjectRequest(Request.Method.GET, url, null, Response.Listener {
response ->try {
val jsonArray = response.getJSONArray("name")
println("Print anything at all!")
for (i in 0 until jsonArray.length()) {
println(jsonArray.getJSONObject(i))
}
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { error -> error.printStackTrace() })
requestQueue?.add(request)
}
}
First create custom VolleyWebService class as follows:
class VolleyWebService constructor(context: Context) {
private var INSTANCE: VolleyWebService? = null
companion object {
#Volatile
private var INSTANCE: VolleyWebService? = null
fun getInstance(context: Context) =
INSTANCE ?: synchronized(this) {
INSTANCE ?: VolleyWebService(context).also {
INSTANCE = it
}
}
}
val requestQueue: RequestQueue by lazy {
Volley.newRequestQueue(context.applicationContext)
}
fun <T> addToRequestQueue(req: Request<T>) {
requestQueue.add(req)
}
}
Then modify your function getAllProfessions like this:
fun getAllProfessions(searchText : TextView) {
val url = "http://192.168.0.24:49155/allprofessions"
val request = JsonObjectRequest(Request.Method.GET, url, null, Response.Listener {
response ->try {
val jsonArray = response.getJSONArray("name")
println("Print anything at all!")
for (i in 0 until jsonArray.length()) {
println(jsonArray.getJSONObject(i))
}
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { error -> error.printStackTrace() })
//changes made here
VolleyWebService.getInstance(context).addToRequestQueue(request)
}
}
This document explain the implementation of a VolleyWebService class:
http://code.sunnyjohn.in/index.php/2020/12/24/retrieve-data-volley/
You have to instantiate the class, create a new request queue and then add to the request queue.
I'm learning to use the Retrofit library for different tasks, but don't fully understand how it works yet.
The main task is to get the body if the response code is 200, overwise (all other codes) just set flag:
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.http.GET
interface APIService {
#GET("/")
suspend fun getRoot(): Response<ResponseBody>
}
...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
button.setOnClickListener {
val url = editText.text.toString()
// url = "https://"+ "google.coN"
val retrofit = Retrofit.Builder()
.baseUrl(url)
.build()
val service = retrofit.create(APIService::class.java)
...
CoroutineScope(Dispatchers.IO).launch {
val response = service.getRoot()
withContext(Dispatchers.Main) {
if (response.isSuccessful){
Ok = true // address is ok
} else {
Ok = false // this address dosnt exist
}
....
}
}
}
}
}
Code works well (remastered from some tutor example) with good links but the app crashes whenever the address is wrong or poorly formatted, it requires a well-formatted URL ("https://"+)
How to modify code and add an exception and do pre-format of URL?
PS: Prob it is better to use OkHTTP directly, but I use integration
of GSON lib with this retrofit code, dropped for clarity
Thanx.
First, Create a sealed class to hold the result
sealed class ApiResult<out T : Any?>
data class Success<out T : Any?>(val data: T) : ApiResult<T>()
data class ApiError(val exception: Exception) : ApiResult<Nothing>()
Now write a helper function to map okhttp response to ApiResult
suspend fun <T : Any> handleApi(
call: suspend () -> Response<T>,
errorMessage: String = "Some errors occurred, Please try again later"
): ApiResult<T> {
try {
val response = call()
if (response.isSuccessful) {
isConnectedToNetwork = true
response.body()?.let {
return Success(it)
}
}
response.errorBody()?.let {
try {
val errorString = it.string()
val errorObject = JSONObject(errorString)
return ApiError(
RuntimeException(if(errorObject.has("message")) errorObject.getString("message") else "Error occurred, Try again Later"))
} catch (ignored: JsonSyntaxException) {
return ApiError(RuntimeException(errorMessage))
}
}
return ApiError(RuntimeException(errorMessage))
} catch (e: Exception) {
if (e is IOException) {
isConnectedToNetwork = false
}
return ApiError(RuntimeException(errorMessage))
}
}
Finally, use below to code to access the result
CoroutineScope(Dispatchers.IO).launch {
val result: ApiResult<ResponseBody> = handleApi( { service.getRoot() } )
when(result){
is ApiResult.Success -> // result.data will give you ResponseBody
is ApiResult.ApiError -> // result.exception will provide the error
}
}
There are few things which can help you with this, it will be more efficient:
Create a view model and create an instance of that in your activity.
In the view model, create a method for executing background tasks, like this:
private fun loadNetworkRequest(block: suspend () -> Unit): Job {
return viewModelScope.launch {
try {
block()
}catch (ex: Exception) {
Toast.makeText(appContext, ex.message, Toast.LENGTH_SHORT).show()
}
}
}
Add the suspend keyword for the request in the service file, which you want to execute using this method.
#GET("category/")
suspend fun getCategories(): Response<CategoryResponseModel>
Execute the request in the view model, like this:
fun performRequest(callback: (Boolean) -> Unit) {
loadNetworkRequest {
val response = service.getRoot()
callback.invoke(response.isSuccessful)
}
}
Call the request method in the activity.
button.setOnClickListener {
....
viewModel.performRequest { response ->
// ok = response
}
}
I'm trying to create a general Request class with kotlin, which I can use to make Request with Volley.
The problem I'm having is that I can not return the response of the Request.
I'm trying to get the response of the request so that I can proces the data.
I can't seem to find a good source which describes how to make a Helper class for making Request
Request class
import android.content.Context
import android.util.Log
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
class Request(var context: Context, var url: String) {
var response : String? = null
fun makePOSTRequest() {
val requestQueue: RequestQueue? = Volley.newRequestQueue(context)
val stringRequest = object : StringRequest(
Method.POST, url,
Response.Listener { response ->
}, Response.ErrorListener { error ->
Log.i("Error", "[" + error + "]")
}) {
override fun getParams(): Map<String, String> {
val params = HashMap<String, String>()
return params
}
}
requestQueue?.add(stringRequest)
}
fun makeGETRequest() {
val requestQueue: RequestQueue? = Volley.newRequestQueue(context)
val stringRequest = object : StringRequest(
Method.GET, url,
Response.Listener { response ->
println(response) // Response: {"message":"ok","locaties"[{"id":"739","name":"Company","code":"","klant":"Client"}]}
this.response = response // Here I'm trying to fill the response var
}, Response.ErrorListener { error ->
Log.i("Error", "[" + error + "]")
}) {
}
requestQueue?.add(stringRequest)
}
}
Implementation
var request = context?.let { Request(it, BuildConfig.API_URL + "getLocatiesLijst.php?name=" + bdl?.getString("name")) }
request?.makeGETRequest()
var response = request?.response
println(response) // This give Null back
I guess the problem is that request is async, but when you run this come, println("") will be called immediately. You should wait for a result.
You can add some callback to the listener or try to use coroutines.
I have been busy with implementing a request helper class with coroutines. And I think I have found a solution to my own problem. I'm using coroutines to make the request async.
Source of solution
Request.kt
import android.content.Context
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlin.coroutines.*;
class Request(var context: Context, var url: String) {
suspend fun makeGetRequest() = suspendCoroutine<String> { cont ->
val queue = Volley.newRequestQueue(context)
val stringRequest = StringRequest(
Request.Method.GET, url,
Response.Listener<String> { response ->
cont.resume("Response is: ${response}")
},
Response.ErrorListener { cont.resume("Something went wrong!") })
queue.add(stringRequest)
}
}
Implementation
class LocationFragment : Fragment(), CoroutineScope {
protected lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var rootView = inflater.inflate(R.layout.location_list, container, false)
//TODO doe verzoek voor lijst van locaties
val bdl = arguments
var request = context?.let { Request(it, BuildConfig.API_URL + "getLocatiesLijst.php?name=" + bdl?.getString("name")) }
job = Job()
launch {
val data = request?.makeGetRequest()
println(data)
}
}
Result
I/System.out: Response is:{"message":"ok","locaties":[{"id":"739","name":"Company","lce_code":"code","klant":"Client"}]}