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"}]}
Related
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()
}
}
This question already has an answer here:
How can I use coroutines with volley so that my code can be written like sychronous?
(1 answer)
Closed 1 year ago.
I am very new to Kotlin and Android Development. I am trying to call one external API (GET) and get the response so that i can parse the response and use the information as i want.
MainActivity
class MainActivity : AppCompatActivity(), View.OnClickListener {
private fun drawResult()
{
val response = getPrice("laptop",...) //How to call this method and get response
}
fun getPrice(label : String, callback: VolleyCallback, function: () -> Unit) {
val url: String = "API_ENDPOINT"
val strReq = StringRequest(
Request.Method.GET, url,
{ response -> callback.onSuccess(response) }
) { volleyError -> callback.onError(volleyError.toString() + "") }
Volley.newRequestQueue(this).add(strReq)
}
}
interface VolleyCallback {
fun onSuccess(result: String?)
fun onError(result: String?)
}
I have created one method getPrice. I am trying to call this method in drawResult() and get the Response. But i am not able to understand how to do it in Kotlin.
For your use-case suspendCoroutine would be a good choice. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/suspend-coroutine.html
With this you can convert your callback based methods to a suspending functions.
Inside the callback you can call cont.resume(response) for suspending function to resume with response or some error.
Example:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val url =
"https://raw.githubusercontent.com/gayanvoice/volley-kotlin/master/data/sample.txt"
GlobalScope.launch {
//usage
val text = getText(url)
val text1 = getText(url)
Log.d("VolleyTest-", text + text1)
}
}
//declare with return type you'll pass in cont.resume
suspend fun getText(url: String): String? =
suspendCoroutine { cont ->
val callback1 = Response.Listener<String> { response -> cont.resume(response) }
val callback2 = Response.ErrorListener { error -> cont.resume(error.toString()) }
val queue = Volley.newRequestQueue(this)
val stringRequest = StringRequest(Request.Method.GET, url, callback1, callback2)
queue!!.add(stringRequest)
}
}
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 just tried today to develop a new app in Kotlin about grabbing data from any website using Volley. I encounter a problem when creating an StringRequest instance and I don't know how to solve it.
I get this error when creating object Response.Listener<String> and Response.ErrorListener:
The class doesn't have a constructor
The code is below:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Btn.setOnClickListener {
val queue = Volley.newRequestQueue(this)
val url = "http://www.google.com"
val stringRequest = StringRequest(Request.Method.GET, url,
object:Response.Listener<String>() {
override fun onResponse(response:String) {
// Display the first 500 characters of the response string.
textView.setText("Response is: " + response.substring(0, 500))
}
}, object:Response.ErrorListener() {
override fun onErrorResponse(error:VolleyError) {
textView.setText("That didn't work!")
}
})
queue.add(stringRequest)
}
}
}
Thank you in advance.
When you create an object which implements an interface, curly braces () is not needed, since interface does not have a constructor. On the other hand, when you create an object which extends a class, curly braces is needed. For example:
interface MyListener {
fun success()
}
abstract class MyAbstractClass {
abstract fun fail()
}
//Create an object which extends MyAbstractClass and implements MyListener
val impl = object: MyAbstractClass(), MyListener {
override fun success() { TODO() }
override fun fail() { TODO() }
}
So, you need to remove the curly braces for Response.Listener and Response.ErrorListener:
val stringRequest = StringRequest(Request.Method.GET, url,
object: Response.Listener<String> {
override fun onResponse(response:String) {
// Display the first 500 characters of the response string.
textView.setText("Response is: " + response.substring(0, 500))
}
}, object: Response.ErrorListener {
override fun onErrorResponse(error:VolleyError) {
textView.setText("That didn't work!")
}
})
Since Response.Listener and Response.ErrorListener is SAM type defined in Java and Kotlin supports SAM conversions, you may simplify the code in this way:
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> {
response ->
// Display the first 500 characters of the response string.
textView.setText("Response is: " + response.substring(0, 500))
}, Response.ErrorListener {
error ->
textView.setText("That didn't work!")
})
//Or even
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> {
// Display the first 500 characters of the response string.
textView.setText("Response is: " + it.substring(0, 500))
}, Response.ErrorListener {
textView.setText("That didn't work!")
})
Notes: SAM conversion is not supported for interfaces defined in Kotlin at this moment.