プライマー
OkHttpよく知られているサードパーティ製のネットワークFrameworkのSDK、使いやすく、優れた性能が、カーネルは単純ではない、記事のこのシリーズ、希少ハードコア知識のポイントを詳細に説明すること。ハードコアは何ですか、それは綿密な研究するために、あなたが過去の周りに間違いはありません、です知識ポイント
ボディ概要
- 何そのOkHttp?
- OkHttpどのように使用するには?
- 詳細な販売代理店:OkHttpソースコアクラスの一つ
- インターセプタ説明:コアクラスOkHttpソースの一つ
A.テキスト
1.OkHttpは何ですか
OkHttpは、根本的な改造など他の多くの人気のフレームワークはまたokhttp非常に人気の高いネットワークプログラミングフレームワーク、業界大手の男スクエアオープンソースの会社ですが、カプセル化されており、ダイナミックエージェントの注釈の反射を使用しています。人気のバージョン:3.10.0、最新バージョン:4.0.1、しかしkotlin変更し、Java言語から実現されます。
他のネットワークに対するフレームは、次のような利点があります。
- SPDY、Http1.X、HTTP2、QuIC社とのWebSocketをサポートしています
- TCP(ソケット)、待ち時間を短縮するための要求を基礎となる多重接続プール
- シームレスなサポートGZIPデータトラフィックを削減
- ネットワーク要求を減らすために、応答データバッファを複製
- その他の自動リトライ要求失敗したIPホストが自動的にリダイレクト
2.OkHttp使用する方法
Gradleの依存関係を追加します。
dependencies {
....
implementation ("com.squareup.okhttp3:okhttp:4.0.1")
}
Javaのコール(同期要求、非同期リクエスト)
public class MyRequest {
/**
* 异步请求
*/
public void sendReqAsync() {
OkHttpClient client = new OkHttpClient.Builder().build();
Request request=newRequest.Builder().url("http://www.baidu.com").build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call,@NotNull IOException e) {
Log.d("sendReqTag", "onFailure:"+e.getLocalizedMessage());
}
@Override
public void onResponse(@NotNull Call call,@NotNull Response response) throws IOException {
String s = new String().concat(response.code() +"\n")
.concat(response.message()+"\n")
.concat(response.body().string());
Log.d("sendReqTag","onSuccess\n "+ s);
}
});
}
/**
* 同步请求
*/
public void sendReqSync() {
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://www.baidu.com").build();
Call call = client.newCall(request);
try {
Response response = call.execute();
String s = new String().concat(response.code() + "\n")
.concat(response.message() + "\n")
.concat(response.body().string());
Log.d("sendReqTag","onSuccess\n "+s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注目すべきいくつかの詳細があることを特徴とする請求OkHttp一般的に、単純な、上記のように使用します:
1、アプリケーション層OkHttpは、必然的に4つの重要な要素を含んで:
- OkHttpClientタイプ(クライアントインスタンスOkHttpを生成)
- Requestクラス(カプセル化リクエスト)
- コールタイプ(ネットワークタスクパッケージ、及びその同期に注意し、同期または非同期実行を実行するかどうかを決定-
リクエストがメインスレッドに配置することができない、要求は、非同期であってもよいです) - レスポンスクラス(ネットワークタスクの実行後にコールバック)
2.ネットワーク要求を実行し、マニフェストでINTERNET権限を適用する必要があり、または例外がスローされます。
図3は、完全なアウトを実行するための要求は、プロセスを以下に示します:
詳細な販売代理店:OkHttpソースコアクラスの一つ
上述,提到Call类,可以选择性执行 同步或者异步请求,但是无论同步异步,都一定会经过一个门户:"分发器" : 索引进源码(okhttp v3.10.0):
虽然用户不需要直接操作分发器,但是 分发器,作为 OkHttp架构的一个门户层,是所有请求的必经之路,其中的代码还是有必要了解细节的。
3.同步请求
进入 分发器Dispatcher
之后, 会执行 getResponseWithInterceptorChain()
来执行这个 Call任务,得到一个 Response
,其中的细节分为两步:
1、
client.dispatcher().executed(this);
,进入源码可以看到 仅仅是执行了runningSyncCalls.add(call);
,将call对象加入到了一个双端队列Deque<RealCall>runningSyncCalls
中。
2、getResponseWithInterceptorChain()
是执行网络请求的核心内容,涉及到拦截器,在这一节上暂时不详述。
同步请求的执行步骤十分简单,将任务加入到 runningSyncCalls列表,并且直接执行核心方法,同步阻塞拿到response。
4.异步请求
异步请求进入分发器之后,
可能会被加入到 Deque<AsyncCall>runningAsyncCalls
这么一个双端队列中,然后 executorService().execute(call);
实际上是用了线程池来执行了这个异步任务。但是,请注意(还是刚才的enqueue方法代码)这里有一个判断条件 if分支 :
这个条件是否满足,将会直接决定是直接执行这个任务,还是将任务加入到 readyAsyncCalls 双端队列.
那么设置这个条件的目的是什么呢?从变量命名来看: runningAsyncCalls 执行中的异步任务 runningCallsForHost 同一个域名正在执行的任务数 readyAsyncCalls 预备执行的任务队列(尚未执行)
当正在执行的任务数小于最大值(默认为64)并且,同一个域名正在请求的任务数小于最大值(默认5)时,才会立即执行,否则,这个任务会被加入到 readyAsyncCalls中等待安排。
那么问题来了,readyAsyncCalls中的任务什么时候会被执行?追踪代码:追踪 readyAsyncCalls 的使用代码,找到 遍历这个队列的地方:
继续追踪,找到了这个 finish方法:
继续追踪finish在哪里调用的,找到两处:
所以,得出结论:在一个任务(无论同步还是异步)结束之后,分发器中的异步任务,存在两个队列,一个 running队列,一个 ready队列,当 running队列的size小于最大值,并且同一个域名正在执行的任务数小于最大值时,可以直接加入到running队列,立即执行。如果不满足这条件,这个异步任务就会被加入到 ready队列.
在任意一个任务( 无论同步或是异步任务)执行完毕( 无论成败)之后,就会遍历 ready队列,每次从 ready队列中取出一个任务,判断同时执行的异步任务数是否达到上限,并且同一主机的访问数是否达到上限,如果都满足,就加入到running队列,并且立即执行,不满足,就停止遍历。周而复始,直到所有的异步任务都执行完。
文字不够形象,画个图表示。
关于okhttp的分发器Dispatcher用到的线程池
同步请求,没有用到线程池。
但是异步请求的代码中,有这么一句。
我们知道,为什么这里会用到线程池呢?
1.观察 同步或者异步的call的实例
那么这个 Call是什么?它是一个接口,它的唯一实现类是 RealCall,
在RealCall
中,异步请求的执行方法,enqueue()
其实是交给了 分发器一个AsyncCall
对象,它继承自 NamedRunnable
可命名的 Runnable
任务。所以,这里可以用 线程池 ExecutorService
来执行这个Runnable
.
进一步观察这个线程池的细节:
它是一个核心线程数为0的线程池,并且使用了一个无容量的阻塞队列作为参数。其实也不不必自己去创建线程池,而可以直接使用 Executors.newCachedThreadPool();
来创建,效果一样。线程池,系统提供了有多种默认实现
为什么okhttp偏偏选择了这一种?
为了实现最大并发量。
详解如下:既然这里提到了线程池,那么就把线程池的基本机制整理一下:
线程池的构造函数中,有一个阻塞队列参数。
它有3个实现类:ArrayBlockingDeque/ LinkedBlockingDeque / SynchronousQueue
是我们线程池经常用的。前面2个都是有容量的,而第三个是无容量的,加入进去,一定会失败。而参照上面线程池的工作流程图,如果加入失败,就会尝试去非核心线程执行任务。这样,便保证了每一个提交进来的异步任务,都会立即尝试去执行,而不是塞入等待队列中等待空闲线程,从而确保了 异步任务的并发。
OkHttp源码核心类之一:拦截器简述
上面讲解分发器的时候,提到了 RealCall类的 getResponseWithInterceptorChain()
方法。它是一个网络请求执行的真正核心方法。进入方法:
- 新建一个拦截器List,并且放入各种拦截器对象
- 将拦截器list,交给RealInterceptorChain,进行责任链模式的调用,最终得出Response.
まず、基本的なデザインパターンの21種類であるChain of Responsibilityパターン、一つの行動パターンを説明します。次の場合は、それを説明することができます:
国有企業のグループは、機器を購入するときに、全体のタスク・フローの観点によると、5つのオブジェクトが存在するから、調達プロセスに始まり調達タスクに影響を与える時間になることができます今度は隣の調達プロセスを担当しています。ゼネラルマネージャーは、彼は彼が最終的な結果のみを気に、操作方法以下の人を気にしませんでした。この場合は、okhttp責任チェーンモードが言ったように、ユーザーがプロセスを経たものを最後に、この要求を心配する必要はありません、彼は知っていた、私は要求を与え、あなたは私の応答、およびコースを与える必要があり、中で役割を果たしていますあなたは、オブジェクト指向プログラム開発の最小の原則に到達するために知っているように、インタセプタの様々な種類、ユーザーは、知っている必要はありません。
そして、これらのインターセプタは、それがokhttpの中核である、次の記事を詳細に説明します。
III。結論
この資料では、細部のokhttpの詳細な説明をしたい場合は、それぞれの記事は非常に退屈で退屈になり、開口部okhttpあるので、私は重要なノードが焦点を当てました。長期的な発展に、ゆっくりと確実に、最初の位置を占め、世界を征服した後、拡大しようとすると、ステップバイステップに地面を失うのが好き