I come from Java and I'm following a tutorial online regarding using the Volley library to make web requests in Android.
The instructor created the request variable like this:
val registerRequest = object : StringRequest(Method.POST, URL_REGISTER, Response.Listener {
println(it) // will print the response
complete(true)
}, Response.ErrorListener {
Log.d("ERROR", "Could not register user: $it")
complete(false)
}) {
override fun getBodyContentType(): String {
return "application/json; charset=utf-8"
}
override fun getBody(): ByteArray {
return requestBody.toByteArray()
}
}
I understand that he's creating a registerRequest variable of type StringRequest. But what I don't understand is why he prefixed StringRequest with object : here.
Also I understand that StringRequest constructor takes in an Int, String, Lambda, Lambda. After that it becomes confusing to me because the developer was able to declare some override methods after the constructor closes. Why did they do this? From what I can tell, this is similar to subclassing StringRequest, then writing the override methods there? Am I right?
Coming from Java, this way of writing code is quite unusual to me.
Related
I'm writing a Kotlin app that has a class. I need that class to extend JsonObjectRequest, since I need to override the function
override fun parseNetworkResponse(response: NetworkResponse?): Response<T>
That's because I need to interpret in Kotlin the HTTP response code the server is sending.
However, I admit to being new to Kotlin and haven't managed to figure out how to extend the JsonObjectRequest class. I keep running into silly compiler issues.
Can someone provide a quick example of that?
After a bit of iteration, i managed to finally figure it out. Posting it here since it may be useful to others -
class DataRequest(
method: Int,
uri: String,
jsonObject: JSONObject,
listener: Response.Listener<JSONObject>,
errorListener: Response.ErrorListener
) :
JsonObjectRequest(method, uri, jsonObject, listener, errorListener)
{
override fun parseNetworkResponse(response: NetworkResponse): Response<JSONObject>
{
try
{
val jsonString = String(
response.data,
Charset.forName(HttpHeaderParser.parseCharset(response.headers))
)
return Response.success(
JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)
)
} catch (e: UnsupportedEncodingException)
{
return Response.error(ParseError(e))
} catch (je: JSONException)
{
return Response.error(ParseError(je))
}
}
}
I am trying to create a custom function that will handles all my API response, but I don't know how to properly handle it.
This is my code
My Custom callback
interface ResponseCallback<T> {
fun onSuccess(response: T)
fun onError(code: Int, message: String)
}
This is how I call my request API
createRequest(getLogin(id), object : API.ResponseCallback<LoginResponse>{
override fun onError(code: Int, message: String) {
}
override fun onSuccess(response: LoginResponse) {
}
})
and This is how I handle my request
fun createRequest(source: Observable<*>, callback: ResponseCallback<*>) {
disposable.add(
source.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ it ->
//
// callback.onSuccess(it)
},
{
}
)
)
}
but my problem here is, the callback.onSuccess() parameter is Nothing and can't accept the callback.onSuccess(it)
Please help me, Thanks.
You should only use star projections if you don't know the specific type parameter for a generic type. When you use these, the compiler will prevent you from doing unsafe things with the parameterized type. For example, you've seen that you can't pass in any parameter to onSuccess, since you've essentially told the compiler that you don't know what its type should be.
The fix then is to give your createRequest function a type parameter instead, and make both the Observable be of that type, as well as the callback that you wish to invoke:
fun <T> createRequest(source: Observable<T>, callback: ResponseCallback<T>) {
...
callback.onSuccess(it) // works, since you're passing in a T to a method that requires a T
...
}
I create a mock of a class with mockk.
On this mock I now call a method that gets a lambda as a parameter.
This lambda serves as a callback to deliver state changes of the callback to the caller of the method.
class ObjectToMock() {
fun methodToCall(someValue: String?, observer: (State) -> Unit) {
...
}
}
How do I configure the mock to call the passed lambda?
You can use answers:
val otm: ObjectToMock = mockk()
every { otm.methodToCall(any(), any())} answers {
secondArg<(String) -> Unit>().invoke("anything")
}
otm.methodToCall("bla"){
println("invoked with $it") //invoked with anything
}
Within the answers scope you can access firstArg, secondArg etc and get it in the expected type by providing it as a generic argument. Note that I explicitly used invoke here to make it more readable, it may also be omitted.
I had to look for a bit more example for the callback and found some example in Kotlin Test with Mockk. In my case, it's a bit more specific.
I wanted to check and mock the onFailure and onSuccess case of a a custom callback implementation MyCustomCallback implementing the ListenableFutureCallback.
The code would look like that for my ExampleProducer class that would have a send function:
fun send(data: String) {
val responseFuture = kafkaTemplate.send(topic, data)
responseFuture.addCallback(MyCustomCallback())
}
So here who would the test go:
#Test
fun onFailureTest() {
kafkaTemplate: KafkaTemplate<String, String> = mockk()
val captureCallback = slot<ListenableFutureCallback<SendResult<String, String>>>()
every { callback.addCallback(capture(captureCallback)) } answers {
captureCallback.captured.onFailure(Throwable())
}
every { kafkaTemplate.send(any()) } returns callback
val prod: ExampleProducer = ExampleProducer()
prod.send("test")
// Then you can verify behaviour or check your captureCallback.captured
verify { kafkaTemplate.send(any()) }
assertNotNull(captureCallback.captured)
}
Maybe not exactly what you ask about, but you can use the funciton type for the mock:
val observerMock = mockk<(State) -> Unit>()
I am studying Kotlin programming for Android and I am trying to understand this code (that works) deeply.
It comes from the Volley library for network request:
//Network stuff
// Request a string response from the provided URL.
val jsonObjectRequest = object : JsonObjectRequest(Method.POST, http, ob,
Response.Listener<JSONObject> { response ->
// Display the first 500 characters of the response string.
Log.d("Debug","Response is: ${response.toString()} ")
},
Response.ErrorListener { error ->
Log.d("Debug","That didn't work! Code: ${error.message}")
})
{
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers.put("Content-Type", "application/json")
headers.put("Accept", "application/json")
return headers
}
}
My question is about the first block, right inside the constructor of the JsonObjectRequest object. I know the object construct, lambdas, classes, and interfaces but there is one little thing that I don't get here. Moreover, I have already seen this thread Pass interface as parameter in Kotlin.
My question is: what is happening in the fourth parameter used to construct the JsonObjectRequest? From what I see, there is a lambda overriding some function related to Response.Listener<JSONObject> but I don't find any reference to this syntax.
To conclude, the objectRequest has the former constructor:
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
and the listener has the following section:
public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
void onResponse(T response);
}
/** Callback interface for delivering error responses. */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
void onErrorResponse(VolleyError error);
}
Reading this I get that with this syntax we are implementing the Listener interface but I don't get why we use a lambda, since in the Listener there is no reference to it, and in particular what does this mean:
Response.Listener<JSONObject> { response ->
// Display the first 500 characters of the response string.
Log.d("Debug","Response is: ${response.toString()} ")
}
Anyone willing to explain this or pointing to some references related to this syntax?
This is a SAM-conversion. Because Response.Listener<JSONObject> is a SAM-interface (has a single method without a default implementation) and defined in Java, you can write
Response.Listener<JSONObject> /* lambda */
and the lambda is used as the implementation of the method. I.e. it's equivalent to
object : Response.Listener<JSONObject> {
override fun onResponse(response: JSONObject) {
Log.d("Debug","Response is: ${response.toString()} ")
}
}
Am building an API and using intercept(ApplicationCallPipeline.Call){} to run some logic before each route execution. I need to pass data from the intercept() method to the called route and
am setting data by using call.attributes.put() in the intercept() like this:
val userKey= AttributeKey<User>("userK")
call.attributes.put(userKey, userData)
And retrieve userData with call.attributes[userKey] .
What happens is that call.attributes[userKey] only works in the intercept() method where I have set the attribute. It doesn't work in the route where I need it.
It throws me
java.lang.IllegalStateException: No instance for key AttributeKey: userK
I wonder if am doing things in the right way
Here is the simplest code reproducing what you describe:
class KtorTest {
data class User(val name: String)
private val userKey = AttributeKey<User>("userK")
private val expected = "expected name"
private val module = fun Application.() {
install(Routing) {
intercept(ApplicationCallPipeline.Call) {
println("intercept")
call.attributes.put(userKey, User(expected))
}
get {
println("call")
val user = call.attributes[userKey]
call.respond(user.name)
}
}
}
#Test fun `pass data`() {
withTestApplication(module) {
handleRequest {}.response.content.shouldNotBeNull() shouldBeEqualTo expected
}
}
}
I intercept the call, put the user in the attributes, and finally respond with the user in the get request.
The test passes.
What ktor version are you using and which engine?