How can I get the URL and method of RetroFit request on onSubscribe of RxJava 2 custom operator?
Asked Answered
A

2

2

So I'm trying to integrate Firebase performance for Http requests and add them manually as they show here (step 9).

I'm using Retrofit 2 and RxJava 2, so I had the idea of doing a custom operator, check code below:

Retrofit 2 Client

@GET("branch-{environment}/v2/branches")
    fun getBranch(@Path("environment") environment: String, @Query("location") location: String, @Query("fulfilment_type") fulfilmentType: String): Single<Response<GetBranchResponse>>

RxJava Call to the Retrofit Client

private val client: BranchClient = clientFactory.create(urlProvider.apiUrl)

override fun getBranch(postCode: String, fulfilmentType: FulfilmentType): Single<GetBranchResponse> {
        return client
                .getBranch(environment, postCode.toUpperCase(), fulfilmentType.toString())
                .lift(RxHttpPerformanceSingleOperator(URL?, METHOD?))
                .map { it.body() }
                .subscribeIO() //custom Kotlin extension 
                .observeMain() //custom Kotlin extension 
                ...
    } 

RxJava 2 Custom Operator via lift:

class RxHttpPerformanceSingleOperator<T>(private val url: String, private val method: String) : SingleOperator<Response<T>, Response<T>> {


    private lateinit var metric: HttpMetric

    @Throws(Exception::class)
    override fun apply(observer: SingleObserver<in Response<T>>): SingleObserver<in Response<T>> {
        return object : SingleObserver<Response<T>> {

            override fun onSubscribe(d: Disposable) {

                    metric = FirebasePerformance.getInstance().newHttpMetric(url,
                            method.toUpperCase())
                    metric.start()


                observer.onSubscribe(d)
            }

            override fun onSuccess(t: Response<T>) {
                observer.onSuccess(t)

                //More info: https://firebase.google.com/docs/perf-mon/get-started-android
                metric.setRequestPayloadSize(t.raw().body().contentLength())
                metric.setHttpResponseCode(t.code())
                metric.stop()
            }

            override fun onError(e: Throwable) {
                observer.onError(e)
                metric.stop()
            }


        }
    }

So currently I'm not sure how it the proper way to get the URL and METHOD of the request (marked as URL? and METHOD? ) to send to the operator,

I need them on onSubscribe to start the metric.. and there I don't have the response with it...


Currently UGLYYYYYYYY my way to do it is:

Add to the Retrofit Client:

@GET("branch-{environment}/v2/branches")
    fun getBranchURL(@Path("environment") environment: String, @Query("location") location: String, @Query("fulfilment_type") fulfilmentType: String): Call<JsonObject>

Add add the parameters as:

val request = client.getBranchURL(environment, postCode.toUpperCase(), fulfilmentType.toString()).request()

url = request.url().toString()
method = request.method()

This makes me have 2 entries on the Client for each request... which makes no sense.


Some helpful clues along the way: - How to get the request url in retrofit 2.0 with rxjava?

Azimuth answered 18/1, 2018 at 23:42 Comment(0)
U
5

Add a Retrofit Interceptor to your HttpClient.Builder with the FirebaseInstance and generate your HttpMetrics there:

class FirebasePerformanceInterceptor(val performanceInstance: FirebasePerformance) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        //Get request values
        val url = request.url().url()
        val requestPayloadSize = request.body()?.contentLength() ?: 0L
        val httpMethod = request.method()

        //Initialize http trace
        val trace = performanceInstance.newHttpMetric(url, httpMethod)
        trace.setRequestPayloadSize(requestPayloadSize)
        trace.start()

        //Proceed
        val response = chain.proceed(chain.request())

        //Get response values
        val responseCode = response.code()
        val responsePayloadSize = response.body()?.contentLength() ?: 0L

        //Add response values to trace and close it
        trace.setHttpResponseCode(responseCode)
        trace.setResponsePayloadSize(responsePayloadSize)
        trace.stop()

        return response
    }
}

You can directly copy and paste this code and it will work. Enjoy!

Unhitch answered 31/5, 2018 at 14:34 Comment(0)
D
1

What I'm going to suggest doesn't necessarily goes to your approach, it's just a different way of thinking about what you're trying to accomplished.

I would suggest 2 different approaches:

  • Create your own observer (So create a class that extends Observer) that receives a Retrofit Call object and do your firebase logic in the subscribeActual method.
  • Use Aspectj to define an annotation that will be processed when the Retrofit call is about to be executed and you can do the firebase logic inside the Aspect. (I'm not sure how Aspectj and kotlin works tho)
Doloritas answered 19/1, 2018 at 10:5 Comment(1)
I've implemented the first suggestion, and it works. Still I'm curious to find if a solution to the above approach appears.Azimuth

© 2022 - 2024 — McMap. All rights reserved.