在当今这个App泛滥的时代,网络请求差点儿是每个App不可缺少的一部分。请求差点儿遍布App的每个界面中。我们进入A界面后。App发起了一系列请求,这时候假如另一部分请求没有被运行,我们就进入B界面開始新的网络请求。这时候原来A界面的网络请求我们有两个选择:

  • 取消A界面的全部未開始运行的网络请求
  • 不取消A界面的全部网络请求,可是B界面的请求要优先于A界面的请求运行,B界面的网络请求运行完成后再去运行A界面未运行完成的请求。

对于第一种情况,我们非常好做到。在Activity的onDestroy回调中取消该界面中全部请求,这里须要明白一点,本篇文章的网络层是OkHttp,既然选择了OkHttp。假设要在onDestroy中取消未開始运行以及已经開始运行的网络请求,就必须给每个请求设置一个tag。然后通过该tag来须要网络请求。

比較明智的做法是以该Activity的上下文的hash值作为tag。

取消请求时将hash值传入。则该界面全部的请求都能够取消。

可是实际情况并不是如此,有一部分网络请求我们不想取消它,仍然想要进行请求,由于这部分的请求比較重要。须要拉到client进行使用,取消这个请求可能会带来不必要的麻烦,因此,我们须要保留这些请求。可是我们进入了一个新的界面,新界面的网络优先级比較高。应该先被运行,这就是另外一种情况。

每种情况有相应的解决方法。第一种情况显得比較简单,我们先来实现它。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn1;
private Button btn2;
private OkHttpClient mOkHttpClient;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
mOkHttpClient = new OkHttpClient();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("TAG", "onDestroy");
cancelByTag(this.hashCode());
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
sendRequest();
break;
case R.id.btn2:
startActivity(new Intent(this, SecondActivity.class));
finish();
break;
}
} private void sendRequest() {
Request.Builder builder = new Request.Builder(); builder.url("https://www.baidu.com").tag(this.hashCode()); Request request1 = builder.build();
Request request2 = builder.build();
Request request3 = builder.build();
Request request4 = builder.build();
Request request5 = builder.build();
Request request6 = builder.build();
Request request7 = builder.build();
Request request8 = builder.build();
Request request9 = builder.build();
Request request10 = builder.build(); final Call call1 = mOkHttpClient.newCall(request1);
final Call call2 = mOkHttpClient.newCall(request2);
final Call call3 = mOkHttpClient.newCall(request3);
final Call call4 = mOkHttpClient.newCall(request4);
final Call call5 = mOkHttpClient.newCall(request5);
final Call call6 = mOkHttpClient.newCall(request6);
final Call call7 = mOkHttpClient.newCall(request7);
final Call call8 = mOkHttpClient.newCall(request8);
final Call call9 = mOkHttpClient.newCall(request9);
final Call call10 = mOkHttpClient.newCall(request10); final Callback callback = new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("TAG", "failure. isCanceled:" + call.isCanceled() + " isExecuted:" + call.isExecuted());
} @Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("TAG", "success. isCanceled:" + call.isCanceled() + " isExecuted:" + call.isExecuted());
}
}; call1.enqueue(callback);
call2.enqueue(callback);
call3.enqueue(callback);
call4.enqueue(callback);
call5.enqueue(callback);
call6.enqueue(callback);
call7.enqueue(callback);
call8.enqueue(callback);
call9.enqueue(callback);
call10.enqueue(callback); } public void cancelByTag(Object tag) {
for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
} for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}
}

当我们点击发送请求的button之后,全部请求都被设置了一个tag后发送出去,然后我们须要高速的点击跳转button,让当前页面finish掉,之后就会回调onDestroy方法,onDestyoy方法中我们调用了取消请求的方法。假设还有请求没有開始运行,该请求就会被取消掉。这样,第一种情况就简单的实现了下。

在实现另外一种情况的时候。我们须要知道一个概念,就是一个集合中怎样对元素进行排序,通常,有两种做法。

  • 将待比較的类实现Comparable接口,调用Collections.sort(list)方法进行排序
  • 新建一个类实现Comparator接口。调用Collections.sort(list,comparator)方法进行排序

假如如今我们有一个类叫Person。它有两个属性,name和age。我们有一个List,里面都是Person。我们希望对这个List进行排序,而且排序的原则是依据age从小到大排序。

依照实现Comparable接口的方法,我们须要将Person实现该接口,就像这样子。

public class Person implements Comparable<Person>{
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person another) {
return this.age-another.age;
}
}

这时候我们生成一个都是Person实例的List,调用sort方法进行排序看下结果怎样

Person p1=new Person("张三",23);
Person p2=new Person("李四",12);
Person p3=new Person("王五",21);
Person p4=new Person("赵六",8);
Person p5=new Person("钱七",40);
List<Person> persons = Arrays.asList(p1, p2, p3, p4, p5);
System.out.println(persons);
Collections.sort(persons);
System.out.println(persons);

输出结果例如以下

[Person{name=’张三’, age=23}, Person{name=’李四’, age=12}, Person{name=’王五’, age=21}, Person{name=’赵六’, age=8}, Person{name=’钱七’, age=40}]

[Person{name=’赵六’, age=8}, Person{name=’李四’, age=12}, Person{name=’王五’, age=21}, Person{name=’张三’, age=23}, Person{name=’钱七’, age=40}]

能够看到按age进行排序,而且从小到大的排了顺序。那么假设要从大到小排序呢,非常easy,改动compareTo方法就可以

@Override
public int compareTo(Person another) {
return another.age-this.age;
}

假设实现Comparator接口。那么我们无需改动Person类,最原始的Person类例如以下

public class Person{
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

取而代之的方法便是新建一个类实现Comparator接口

public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person person1, Person person2) {
return person1.getAge()-person2.getAge();
} }

在进行排序的时候将比較器传入就可以。


Person p1=new Person("张三",23);
Person p2=new Person("李四",12);
Person p3=new Person("王五",21);
Person p4=new Person("赵六",8);
Person p5=new Person("钱七",40); List<Person> persons = Arrays.asList(p1, p2, p3, p4, p5);
System.out.println(persons);
Collections.sort(persons,new PersonComparator());
System.out.println(persons);

知道了怎样比較一个类并进行排序后。我们開始我们的正式内容。让okhttp支持优先级调度,也就是文章开头的另外一种情况。B界面的网络请求比A界面的网络请求优先级要高,因此我们应该有一个变量来代表这样的优先级。然后我们须要依据该优先级进行排序。

非常遗憾的是Okhttp默认是不支持优先级调度的,我们不得不改动OkHttp底层的源代码进行扩展支持,但这又是万不得已的。

在RealCall这个类里面,有一个内部类AsyncCall。全部异步运行的网络请求终于都会被包装成这一个类型。OkHttpClient中的newCall将Request对象包装成RealCall,而RealCall中的enqueue则将自己转换成一个AsyncCall对象进行异步运行,AsyncCall是Runnale对象的间接子类。因此。我们代表优先级的变量应该存储在AsyncCall这个类中,也就是priority。

 final class AsyncCall extends NamedRunnable{
//other field
private int priority;
private AsyncCall(Callback responseCallback, boolean forWebSocket) {
super("OkHttp %s", originalRequest.url().toString());
//other field
this.priority = originalRequest.priority();
} int priority() {
return originalRequest.priority();
}
//other method
}

相同的,我们须要在Request中暴露这个优先级的变量,即priority

public final class Request {
//other field
private final int priority;
private Request(Builder builder) {
//other field
this.priority=builder.priority;
}
public int priority(){
return priority;
} //other method
public static class Builder {
//ohther field
private int priority;
private Builder(Request request) {
//other field
this.priority=request.priority;
} public Builder priority(int priority){
this.priority=priority;
return this;
}
//other method
}
}

之后我们须要实现一个比較器。依据优先级由大到小进行排序

public class AsycCallComparator<T> implements Comparator<T> {
@Override
public int compare(T object1, T object2) {
if ((object1 instanceof RealCall.AsyncCall)
&& (object2 instanceof RealCall.AsyncCall)) {
RealCall.AsyncCall task1 = (RealCall.AsyncCall) object1;
RealCall.AsyncCall task2 = (RealCall.AsyncCall) object2;
int result = task2.priority()
- task1.priority();
return result;
}
return 0;
}

然后。OkHttp内部有一个Dispatcher分发器,分发器内部有一个ExecutorService,ExecutorService是能够自己进行配置,然后变成能够依据优先级调度的,默认的分发器是使用SynchronousQueue进行调度。我们须要将它改成优先队列,将原来的新建对象凝视掉,替换成我们的优先队列,优先队列的创建须要传入一个比較器,也就是刚才我们创建的那个比較器。

以下这种方法就是Dispatcher中设置线程池的方法

    public synchronized ExecutorService executorService() {
if (executorService == null) {
// executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
// new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
executorService = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new PriorityBlockingQueue<Runnable>(60, new AsycCallComparator<Runnable>()), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}

之后我们模拟发送10个不同优先级的请求,而且优先级是乱序的。控制台则会输出

14===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

500===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

100===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

40===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

34===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

30===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

20===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

10===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

5===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

2===Response{protocol=http/1.1, code=200, message=OK, url=https://www.baidu.com/}

非常明显的看到除了第一个请求外,其它请求是一个有序的优先队列。

这仅仅是一个简单的实现參考,详细实现方案还得看你自己的需求。

这样是扩展了OkHttp支持优先级调度,可是终于还是通过改动底源代码实现。尽管改动的代码不多,但也是改动,在不到万不得已的情况下。还是建议不要这么干。

我将改动后的OkHttp源代码放到了Github上,有兴趣的能够下过来进行參考。

Android 扩展OkHttp支持请求优先级调度的更多相关文章

  1. Android okHttp网络请求之缓存控制Cache-Control

    前言: 前面的学习基本上已经可以完成开发需求了,但是在项目中有时会遇到对请求做个缓存,当没网络的时候优先加载本地缓存,基于这个需求我们来学习一直okHttp的Cache-Control. okHttp ...

  2. Android okHttp网络请求之Json解析

    前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...

  3. Android okHttp网络请求之Get/Post请求

    前言: 之前项目中一直使用的Xutils开源框架,从xutils 2.1.5版本使用到最近的xutils 3.0,使用起来也是蛮方便的,只不过最近想着完善一下app中使用的开源框架,由于Xutils里 ...

  4. Android okHttp网络请求之Retrofit+Okhttp+RxJava组合

    前言: 通过上面的学习,我们不难发现单纯使用okHttp来作为网络库还是多多少少有那么一点点不太方便,而且还需自己来管理接口,对于接口的使用的是哪种请求方式也不能一目了然,出于这个目的接下来学习一下R ...

  5. Android 中OKHttp请求数据get和post

    1:在Android Studio 的 build.gradle下  添加 然后再同步一下 compile 'com.squareup.okhttp:okhttp:2.4.0'compile 'com ...

  6. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  7. android开发--okhttp

    一.okhttp简单实用: 一般的get请求 一般的post请求 基于Http的文件上传 文件下载 加载图片 支持请求回调,直接返回对象.对象集合 支持session的保持 二.实用教程 1.添加an ...

  8. Android之OkHttp详解

    文章大纲 一.OkHttp简介二.OkHttp简单使用三.OkHttp封装四.项目源码下载   一.OkHttp简介 1. 什么是OkHttp   一般在Java平台上,我们会使用Apache Htt ...

  9. 关于OkHttp–支持SPDY协议的高效HTTP库 com.squareup.okhttp

    转载:http://liuzhichao.com/p/1707.html OkHttp–支持SPDY协议的高效HTTP库 柳志超博客 » Program » Andriod » OkHttp–支持SP ...

随机推荐

  1. 〖Linux〗Debian 7.1.0 Wheezy使用ltib报错的解决办法

    报错内容: scue@Link:/home/work/ltib$ ./ltib Processing platform: Phytec board with the NXP LPC32XX SoC = ...

  2. ubuntu——更新、编译、启动内核

    步骤如下: 1.make mrproper Linux下面去编译项目之前,一般常会用make mrproper去先删除之前编译所生成的文件和配置文件,备份文件等,其中,mrproper和distcle ...

  3. linux下vi编辑文件

    vi 文件名.进入读文件模式 按i进入编辑模式 按g切光标换到第一行,按G光标切换到最后一行. 按Esc退出编辑模式 :q退出 :wq保存退出 以上命名后面加上!表示强制运行

  4. iOS 获得通讯录中联系人的所有属性--b

    ABAddressBookRef addressBook = ABAddressBookCreate(); CFArrayRef results = ABAddressBookCopyArrayOfA ...

  5. DotNet Core 2.0使用MySql实现Code First

    本教程使用vs2017 + dotnet core2.0 + MySql5.7.19 1.打开vs2017,文件>新建>项目,选择Asp.Net Core Web应用程序. 2.项目名称可 ...

  6. EMQ 压测问题

    一.单台服务器最高只能跑2W多一点问题描述 一直使用benchmark测试单台EMQ都没有超过3W链接数 一个独立的外网IP只能提供最多6W的端口号,但每个TCP需要分配一个指定的端口号.所以理论上讲 ...

  7. curl命令常用参数

    -a/--append 上传文件时,附加到目标文件 -A/--user-agent <string> 设置用户代理发送给服务器 -anyauth 可以使用“任何”身份验证方法 -b/--c ...

  8. bcdedit的研究

    首先说明下引导: 微软在Vista之前的系统,采用的是Ntldr来进行引导系统,使用的是boot.ini文件. 在目前的Vista和win7中,采用的是新的引导方式Windows Boot Manag ...

  9. NPM 模块恩仇录

    vue-clickoutside 点击元素以外的东西时会触发的事件.好东西.其实可以利用全局event来判断当前点击的对象来判断也一样.但这个显然更舒服 传送门:https://www.npmjs.c ...

  10. Atitit. 软件---多媒体区---- jmf 2.1.1 Java Media Framework 支持的格式

    Atitit. 软件---多媒体区---- jmf 2.1.1 Java Media Framework 支持的格式 JMF,全名为Java Media Framework,它可以在java appl ...