本文最后更新于:2023年4月15日 下午
本文链接
前言 在某一个界面,用户发起了一个网络请求,因为某种原因用户在网络请求完成前离开了当前界面,比较好的做法是取消这个网络请求。对于OkHttp来说,具体是调用Call
的cancel
方法。
如何找到这一个网络请求并取消掉它呢?
操作大致分为3步。第一步,在建立请求时,给请求(request)添加标记;第二步,根据标记,找到请求;最后,取消这个请求。
OkHttp中的tag 要取消一个请求,OkHttp中可以使用cancel方法,参考 。
OkHttp的request对象有tag。可以根据tag来标示请求。参考Stack Overflow 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Request request = new Request .Builder(). url(url).tag("requestKey" ).build();for (Call call : mHttpClient.dispatcher().queuedCalls()) { if (call.request().tag().equals("requestKey" )) call.cancel(); }for (Call call : mHttpClient.dispatcher().runningCalls()) { if (call.request().tag().equals("requestKey" )) call.cancel(); }
Retrofit中并没有显示地提供取消请求的接口。2018年时Retrofit仍未提供直接访问call对象的方法 那么如何找到目标网络请求呢?
给每个与页面(Activity,Fragment)相关的request加入自定义header,参考 。 给OkHttpClient添加拦截器。标记出页面的生存状态。如果页面销毁了,则取消对应的request。
以GithubOnAndroid项目为例,https://github.com/RustFisher/GithubOnAndroid
添加标记 持有一个ConcurrentHashMap<String, Boolean>来标记页面存活状态。
1 2 3 4 5 6 7 8 9 private static ConcurrentHashMap<String, Boolean> actLiveMap = new ConcurrentHashMap <>(); public static void markPageAlive (String actName) { actLiveMap.put(actName, true ); }public static void markPageDestroy (String actName) { actLiveMap.put(actName, false ); }
Activity中登记界面状态 给当前Activity起名字。每个Activity的标记名必须唯一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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移除掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static final String HEADER_ACT_NAME = "Activity-Name" ; 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方法。
1 2 3 4 5 @GET("users/{owner}/repos") Observable<List<UserRepo>> userRepo ( @Header(NetworkCenter.HEADER_ACT_NAME) @Nullable String actName, @Path("owner") String owner, @Query("sort") String sortType) ;
更多请参考:
Android OkHttp + Retrofit 使用示例
Android OkHttp + Retrofit 取消请求的方法
Android OkHttp + Retrofit 下载文件与进度监听
Android OkHttp + Retrofit 断点续传