Android 擴充套件 OkHttp 支援請求優先順序排程
在當今這個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<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進行排序,並且從小到大的排了順序,那麼如果要從大到小排序呢,很簡單,修改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上,有興趣的可以下過來進行參考。PriorityOkHttp
相關文章
- Linux排程器:程序優先順序Linux
- Android程式優先順序Android
- linux中設定程式排程的優先順序別Linux
- 2.2.5排程演算法:時間片輪轉、優先順序排程、多級反饋排程演算法
- CSS優先順序CSS
- python運算子及優先順序順序Python
- 中斷優先順序
- Yacc使用優先順序
- Android網路請求(3) 網路請求框架OkHttpAndroid框架HTTP
- 平穩擴充套件:可支援RevenueCat每日12億次API請求的快取套件API快取
- SpringBoot配置檔案優先順序載入順序Spring Boot
- 運算子的優先順序
- SQL 優先順序join>whereSQL
- java運算子優先順序Java
- 如何使用Rust的gaffer實現優先順序的微批處理排程器 - njkRust
- android view 擴充套件方法AndroidView套件
- RMQ——支援合併和優先順序的訊息佇列MQ佇列
- flask路由系統、偏函式、CBV、模板、請求響應、session、請求擴充套件Flask路由函式Session套件
- Nachos實驗實現執行緒id、限制執行緒數和更改排程演算法(按優先順序排程)執行緒演算法
- [譯]HTTP/2的優先順序HTTP
- css 選擇器優先順序CSS
- Yarn任務優先順序配置Yarn
- ansible 變數優先順序示例變數
- C++運算子優先順序C++
- java setPriority()設定優先順序Java
- 封裝優先順序佇列封裝佇列
- kestra: 無限可擴充套件的開源編排和排程平臺套件
- 閃現, 請求擴充套件, 藍圖, 中介軟體(瞭解)套件
- 可擴充套件的資料庫系統,請求批評套件資料庫
- ASP.NET Core擴充套件庫之Http請求模擬ASP.NET套件HTTP
- Spring Boot中如何擴充套件XML請求和響應的支援Spring Boot套件XML
- html優先順序和層疊性HTML
- 任務卡片優先順序排序-Leangoo排序Go
- C語言運算子優先順序C語言
- 華為路由協議優先順序路由協議
- 設計中的優先順序(下)
- SAP UI configuration determination的優先順序UI
- 設計中的優先順序(上)
- C 語言運算子優先順序