android 请求优先级,Android 扩展 OkHttp 支持请求优先级调度

本文详细介绍了如何在Android中使用OkHttp实现网络请求的优先级调度。通过设置请求的tag并在Activity销毁时取消指定tag的请求,实现了低优先级请求的取消。同时,通过扩展OkHttp源码,添加优先级字段并实现比较器,配合优先级队列,确保高优先级请求优先执行,从而满足不同界面请求的优先级需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在当今这个App泛滥的时代,网络请求几乎是每一个App必不可少的一部分,请求几乎遍布App的每一个界面中。我们进入A界面后,App发起了一系列请求,这时候假如还有一部分请求没有被执行,我们就进入B界面开始新的网络请求,这时候原来A界面的网络请求我们有两个选择:取消A界面的所有未开始执行的网络请求

不取消A界面的所有网络请求,但是B界面的请求要优先于A界面的请求执行,B界面的网络请求执行完毕后再去执行A界面未执行完毕的请求。

对于第一种情况,我们很好做到,在Activity的onDestroy回调中取消该界面中所有请求,这里需要明确一点,本篇文章的网络层是OKHttp,既然选择了OkHttp,如果要在onDestroy中取消未开始执行以及已经开始执行的网络请求,就必须给每一个请求设置一个tag,然后通过该tag来需要网络请求。比较明智的做法是以该Activity的上下文的hash值作为tag。取消请求时将hash值传入,则该界面所有的请求都可以取消。

但是实际情况并非如此,有一部分网络请求我们不想取消它,仍然想要进行请求,因为这部分的请求比较重要,需要拉到客户端进行使用,取消这个请求可能会带来不必要的麻烦,因此,我们需要保留这些请求。但是我们进入了一个新的界面,新界面的网络优先级比较高,应该先被执行,这就是第二种情况。

每种情况有对应的解决方法,第一种情况显得比较简单,我们先来实现它。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();

}

}

}

}

当我们点击发送请求的按钮之后,所有请求都被设置了一个tag后发送出去,然后我们需要快速的点击跳转按钮,让当前页面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{    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 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进行排序,并且从小到大的排了顺序,那么如果要从大到小排序呢,很简单,修改compareTo方法即可@Overridepublic 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 {    @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 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中暴露这个优先级的变量,即prioritypublic 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 implements Comparator {    @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(), Util.threadFactory("OkHttp Dispatcher", false));

executorService = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,                    new PriorityBlockingQueue(60, new AsycCallComparator()), 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上,有兴趣的可以下过来进行参考。PriorityOkHttp

原文链接:http://www.codeceo.com/article/android-okhttp-scheduling.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值