How can I get the URL and method of RetroFit reque

2019-07-17 15:17发布

问题:

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?

回答1:

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!



回答2:

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)