1. 程式人生 > >Android OkHttp + Retrofit 取消請求的方法

Android OkHttp + Retrofit 取消請求的方法

本文連結

前言

在某一個介面,使用者發起了一個網路請求,因為某種原因使用者在網路請求完成前離開了當前介面,比較好的做法是取消這個網路請求。對於OkHttp來說,具體是呼叫Callcancel方法。

如何找到這一個網路請求並取消掉它呢?

操作大致分為3步。第一步,在建立請求時,給請求(request)新增標記;第二步,根據標記,找到請求;最後,取消這個請求。

OkHttp中的tag

要取消一個請求,OkHttp中可以使用cancel方法,參考。

OkHttp的request物件有tag。可以根據tag來標示請求。參考Stack Overflow。

    //Set tags for your requests when you build them:
    Request request = new Request.Builder().
    url(url).tag("requestKey").build();

    //When you want to cancel:
    //A) go through the queued calls and cancel if the tag matches:
    for (Call call : mHttpClient.dispatcher().queuedCalls()) {
        if (call.request().tag().equals("requestKey"))
            call.cancel();
    }

    //B) go through the running calls and cancel if the tag matches:
    for (Call call : mHttpClient.dispatcher().runningCalls()) {
        if (call.request().tag().equals("requestKey"))
            call.cancel();
    }

Retrofit中並沒有顯示地提供取消請求的介面。2018年時Retrofit仍未提供直接訪問call物件的方法
那麼如何找到目標網路請求呢?

Retrofit加入自定義header

給每個與頁面(Activity,Fragment)相關的request加入自定義header,參考。
給OkHttpClient新增攔截器。標記出頁面的生存狀態。如果頁面銷燬了,則取消對應的request。

以GithubOnAndroid專案為例,https://github.com/RustFisher/GithubOnAndroid

新增標記

持有一個ConcurrentHashMap<String, Boolean>來標記頁面存活狀態。

    private static ConcurrentHashMap<String, Boolean> actLiveMap = new ConcurrentHashMap<>(); // 標記Activity是否存活

    public static void markPageAlive(String actName) {
        actLiveMap.put(actName, true);
    }

    public static void markPageDestroy(String actName) {
        actLiveMap.put(actName, false);
    }

Activity中登記介面狀態

給當前Activity起名字。每個Activity的標記名必須唯一。

private static final String MY_ACT_NAME = "xxx1Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NetworkCenter.markPageAlive(MY_ACT_NAME);
        // ...
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        NetworkCenter.markPageDestroy(MY_ACT_NAME);
        // ...
    }

OkHttpClient新增攔截器

給OkHttpClient新增攔截器,在攔截器中檢查頁面的存活情況。
檢查後,把這個自定義header移除掉。

    public static final String HEADER_ACT_NAME = "Activity-Name"; // 標記Activity介面名字

    private Interceptor lifeInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            String actName = request.header(HEADER_ACT_NAME);
            if (!TextUtils.isEmpty(actName)) {
                Log.d(TAG, "lifeInterceptor: actName: " + actName);
                Boolean actLive = actLiveMap.get(actName);
                if (actLive == null || !actLive) {
                    chain.call().cancel();
                    Log.d(TAG, "lifeInterceptor: 取消請求, actName: " + actName);
                } else {
                    Log.d(TAG, "lifeInterceptor: 發起請求, actName: " + actName);
                }
            }
            Request newRequest = request.newBuilder().removeHeader(HEADER_ACT_NAME).build();
            return chain.proceed(newRequest);
        }
    };


OkHttpClient = new OkHttpClient.Builder()
        .readTimeout(10, TimeUnit.SECONDS)
        .connectTimeout(10, TimeUnit.SECONDS)
        .addInterceptor(lifeInterceptor) // 新增攔截器
        .build();

call.cancel()後,不會再走Retrofit的subscribe方法。

新增header

    @GET("users/{owner}/repos")
    Observable<List<UserRepo>> userRepo(
            @Header(NetworkCenter.HEADER_ACT_NAME) @Nullable String actName,
            @Path("owner") String owner,
            @Query("sort") String sortType);