Retrofit Adding tag to the original request object

此生再无相见时 提交于 2019-12-23 07:57:21

问题


I'm trying to solve a problem where I'll be making a couple of asynchronous calls and based on the original request, I'm performing a task. To solve this issue, I'm trying to add a TAG to each request and then on successful response, I can get the tag and take action based on the tag. Here, I'm using TAG only to identify the original request.

Problem

Before calling the enqueue method, I'm setting the tag to the original request. But when I get the response in the successful callback, I'm getting different tag that I didn't set. Somehow the request object itself is coming as the tag object there. I'm not sure, how???

Please check the code below-

GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
                final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");

                // Set the string tag to the original request object.
                call.request().newBuilder().tag("hello").build();


                call.enqueue(new Callback<List<Contributor>>() {
                    @Override
                    public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
                        Log.d("tag", response.raw().request().tag().toString());
                        // I'm getting Request{method=GET, url=https://api.github.com/repos/square/retrofit/contributors, tag=null} as the value of the tag. WHY????
                        final TextView textView = (TextView) findViewById(R.id.textView);
                        textView.setText(response.body().toString());
                    }
                    @Override
                    public void onFailure(Call<List<Contributor>> call, Throwable t) {
                        final TextView textView = (TextView) findViewById(R.id.textView);
                        textView.setText("Something went wrong: " + t.getMessage());
                    }
                });

Can somebody point out that what exactly I'm doing wrong here. Any help would be appreciated.


回答1:


This solution is clearly a hack, but it works.

Let's say you create your Retrofit service like this :

 public <S> S createService(Class<S> serviceClass) {

    // Could be a simple "new"
    Retrofit.Builder retrofitBuilder = getRetrofitBuilder(baseUrl); 

    // Could be a simple "new"
    OkHttpClient.Builder httpClientBuilder = getOkHttpClientBuilder();  

    // Build your OkHttp client
    OkHttpClient httpClient = httpClientBuilder.build();

    Retrofit retrofit = retrofitBuilder.client(httpClient).build();

    return retrofit.create(serviceClass);
}

You will need to add a new CallFactory to your Retrofit instance, so it adds a tag every-time. Since the tag will be read-only, we will use an array of Object containing only one element, which you will be able to change later on.

Retrofit retrofit = retrofitBuilder.client(httpClient).callFactory(new Call.Factory() {
        @Override
        public Call newCall(Request request) {

            request = request.newBuilder().tag(new Object[]{null}).build();

            Call call = httpClient.newCall(request);

            // We set the element to the call, to (at least) keep some consistency
            // If you want to only have Strings, create a String array and put the default value to null;
            ((Object[])request.tag())[0] = call;

            return call;
        }
    }).build();

Now, after creating your call, you will be able to change the contents of your tag:

((Object[])call.request().tag())[0] = "hello";



回答2:


For me this code is working

val CLIENT: OkHttpClient = OkHttpClient.Builder().apply {
    addInterceptor(TagInterceptor())
}.build()

val SERVER_API: ServerApi = Retrofit.Builder()
    .client(CLIENT)
    .baseUrl(BASE_URL)
    .build()
    .create(ServerApi::class.java)

interface ServerApi {

    @GET("api/notifications")
    @Tag("notifications")
    suspend fun getNotifications(): ResponseBody
}

@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Tag(val value: String)

internal class TagInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val builder = request.newBuilder()
        request.tag(Invocation::class.java)?.let {
            it.method().getAnnotation(Tag::class.java)?.let { tag ->
                builder.tag(tag.value)
            }
        }
        return chain.proceed(builder.build())
    }
}

Then cancel

fun OkHttpClient.cancelAll(tag: String) {
    for (call in dispatcher().queuedCalls()) {
        if (tag == call.request().tag()) {
            call.cancel()
        }
    }
    for (call in dispatcher().runningCalls()) {
        if (tag == call.request().tag()) {
            call.cancel()
        }
    }
}

CLIENT.cancelAll("notifications")


来源:https://stackoverflow.com/questions/42066885/retrofit-adding-tag-to-the-original-request-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!