解析オープンソースプロジェクトのRetrofit(二)フレームワーク編
21665 ワード
作者の労働の成果を大切にして、もし転載するならば、出所を明記して下さい.http://blog.csdn.net/zhengzechuan91/article/details/50349934
Retrofitはsquareのソースのhttpフレームで、簡単に使いやすくて、okhttpとRxJavaをサポートしています.もしあなたが煩雑なhttp要求を配置するために自分のネット要求フレームを書きたくないなら、この優雅なフレームを試してみてもいいです.
上記のブログでは、プロジェクトでの利用を紹介しました.今日はこの枠組みの実現原理について説明します.
Retrofitの原理
まず、Retrofitの運行原理を説明します.は、RealServiceを生成し、Real ServiceをRealServiceとして、ReltAdapterの内部的なResTradlerダイナミックエージェントとして生成する. RealServiceの方法を呼び出すと、Relt Handler.invoke()によってプロキシされます. は、Request Buiderの例を生成し、set ArgementsによってRequest Buiderの例にパラメータを解析し、パラメータがPOJOタイプであれば、CoverterのtoBody(Object)を介してPOJOをTypedOutputに変換する. 私たちが設定したRequest Interceptorを利用して、http要求を送信する前にRequest Buiderをブロックし、パラメータを追加して、最後にRequestに変換します. はCient.executeを実行して、Requestに戻ります. Reponseの状態コードが2*である場合、処理を継続します.そうでなければ、様々なRetrofitErr異常をスローします. が必要とするリターンタイプはResonseで、同期は直接リターンし、非同期はResonseWrapperとして包装する. に必要な戻りタイプがReponseでなければ、設定されたConterのfrom Body(Typed Input、Type)によりType InputをPOJOに変換し、同期してそのままリターンし、非同期でRespnseWrapperに包装します. 上で見たように、このフレームワークは異なるプラットフォームをPlatformとして抽象化し、異なる要求方式をRequestとして抽象化し、異なるリターン結果をReponseとして抽象化し、CoverterはRequestとPOJOの変換とReponseとPOJOの変換を提供しています.
私たちはフレームに対して全体的な理解を持っているので、フレームに対する理解を深めるために、いくつかの詳細を見てみます.
フレーム
私たちは上の考え方に基づいてプラットフォーム、要求、ブロック、バックの四つの角度からフレームに対して深く分析します.
プラットフォーム
プラットフォームの選択は抽象的なPlatformによって切り替わります.
プラットフォームに関する変数は、Platformで抽象化されています.
ApachClientはapacheのHttp Clientのパッケージであり、UrlConnection CientはjavaのUrlConnection Clientのパッケージであり、OkClientはokhttpフレームのOkHttpClientのパッケージである.
ApachClientとUrlConnection Cientの要求コードを見てみます.
まず、Request Builderという種類を見てみましょう.このクラスは呼び出す方法の解析です.中にはTyped Outputタイプのbodyに対して3つの場所が割り当てられています.
要求
Reset Adapter()は、まず代理が必要なのはインターフェースであると判断し、他の種類やインターフェースとの継承や実現の関係がないと判断します.そして、RealServiceは、ReltAdapterの内部クラスのRelt Handler動的エージェントインターフェースを介して行われる.
次のすべての操作は、RestHandler()で行われます.
serviceMethodInfocacheにはRealServiceインターフェースごとに対応するRestMethodInfoキューがキャッシュされています.各MethodはRets MethodInfoに対応しています.
RealServiceインターフェースに対応するキューをデフォルトで作成し、serviceMethodInfoCacheに追加します.
RealServiceがget List()メソッドを呼び出してデータを取得すると、RealServiceがRest Handler()に実行されます.
この方法の論理を重点的に見ます.
この時、この方法の注釈はすでに解析済みで、このRequest Builderを作成します.
遮る
http要求前に、headに公共パラメータを追加したいかもしれません.フレームワークはRequest Interceptorインターフェースを提供してくれます.Request Intercept orを通じて、パラメータを修正できます.
戻る
ブロックが終わったら、Request Buiderを通じて、urlのルートと相対パスを合わせて組み付けて、Request BuiderをRequestに転化しました.
リターンされた状態コードが2 XXである場合、APIの成功を要求することを説明する.
もし戻ってきたのがレスリングだったら、そのまま戻ってきます.
使用時の注意点をまとめます.1.非同期の最後のパラメータはCallbackで、戻りのタイプはvoidです.同期パラメータにはCallbackがありません.戻るタイプはObjectです.2.方法によってのみ注釈されている@FormUrlEncoded、@MultiiPadとパラメータ注釈@Bodyが注釈されている場合、パラメータの中にPOJOパラメータがあります.3.requestのPOJOはCoverter_Body(Object)を介してTypedOutputに変換されました.4.レスポンスはCoverter_from Body(Typed Input、Type)を通じてTyped InputをPOJOに変換したものです.5.@GETまたは@POSTの相対パスは/で始まる必要があります.さもなければ異常を投げます.6.私たちが定義したAPIを要求するインターフェースのパラメータにはnull値がないと、例外をスローします.
はい、Retrofitフレームに関する分析はこれで終わります.
Retrofitはsquareのソースのhttpフレームで、簡単に使いやすくて、okhttpとRxJavaをサポートしています.もしあなたが煩雑なhttp要求を配置するために自分のネット要求フレームを書きたくないなら、この優雅なフレームを試してみてもいいです.
上記のブログでは、プロジェクトでの利用を紹介しました.今日はこの枠組みの実現原理について説明します.
Retrofitの原理
まず、Retrofitの運行原理を説明します.
私たちはフレームに対して全体的な理解を持っているので、フレームに対する理解を深めるために、いくつかの詳細を見てみます.
フレーム
私たちは上の考え方に基づいてプラットフォーム、要求、ブロック、バックの四つの角度からフレームに対して深く分析します.
プラットフォーム
プラットフォームの選択は抽象的なPlatformによって切り替わります.
プラットフォームに関する変数は、Platformで抽象化されています.
/* Platform.java */
// Converter
abstract Converter defaultConverter();
// Client
abstract Client.Provider defaultClient();
// http
abstract Executor defaultHttpExecutor();
// http
abstract Executor defaultCallbackExecutor();
// log
abstract RestAdapter.Log defaultLog();
三つの内部のAndroid、App Engine、Baseはそれぞれ三つの異なるプラットフォームを表す抽象的な方法を実現しました.選択する時、私達も前の二つのプラットフォームが満たされない時は、デフォルトのベースを使います./* Platform$Android.java */
private static class Android extends Platform {
@Override Converter defaultConverter() {
// Gson
return new GsonConverter(new Gson());
}
@Override Client.Provider defaultClient() {
final Client client;
if (hasOkHttpOnClasspath()) {
// okhttp , OkClient
client = OkClientInstantiator.instantiate();
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
//android2.3 , ApacheClient
client = new AndroidApacheClient();
} else {
//android2.3 , HttpURLConnection
client = new UrlConnectionClient();
}
return new Client.Provider() {
@Override public Client get() {
return client;
}
};
}
@Override Executor defaultHttpExecutor() {
//
return Executors.newCachedThreadPool(new ThreadFactory() {
@Override public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
@Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
r.run();
}
}, RestAdapter.IDLE_THREAD_NAME);
}
});
}
@Override Executor defaultCallbackExecutor() {
//
return new MainThreadExecutor();
}
@Override RestAdapter.Log defaultLog() {
return new AndroidLog("Retrofit");
}
}
ActheClientとUrlConnection Cientは直接Clientインターフェースを実現しました.OkClientはUrlConnection Cientを継承しました.ApachClientはapacheのHttp Clientのパッケージであり、UrlConnection CientはjavaのUrlConnection Clientのパッケージであり、OkClientはokhttpフレームのOkHttpClientのパッケージである.
ApachClientとUrlConnection Cientの要求コードを見てみます.
/* ApacheClient.java */
@Override
public Response execute(Request request) throws IOException {
// HttpUriRequest Request uri head,
//Request body , HttpUriRequest Entry
//Request body
HttpUriRequest apacheRequest = createRequest(request);
// http
HttpResponse apacheResponse = execute(client, apacheRequest);
// HttpResponse head entry, Response
// entry , Response body TypedByteArray
return parseResponse(request.getUrl(), apacheResponse);
}
/* UrlConnectionClient.java */
@Override
public Response execute(Request request) throws IOException {
// Client
HttpURLConnection connection = openConnection(request);
// connection Request head, Request
//body , body connection
// body -1,
// -1,
// , , oom
prepareRequest(connection, request);
// connection Response, Response
//body TypedInputStream
return readResponse(connection);
}
上記のプラットフォームの関連コードを見たら、必ず質問があります.Requestのbody値はいつですか?まず、Request Builderという種類を見てみましょう.このクラスは呼び出す方法の解析です.中にはTyped Outputタイプのbodyに対して3つの場所が割り当てられています.
要求
RestAdapter adapter = new RestAdapter.Builder()
.setClient()
.setEndpoint()
.setRequestInterceptor()
.setConverter()
.setLogLevel(LogLevel.FULL)
.build();
RealService real = adapter.create(RealService.class);
私たちはRertAdapter.Buider().build()にいる時、システムは私たちが実現していない構成を設定してくれます.これらのパラメータはすべてPlatformによって生成されたデフォルト値です.Reset Adapter()は、まず代理が必要なのはインターフェースであると判断し、他の種類やインターフェースとの継承や実現の関係がないと判断します.そして、RealServiceは、ReltAdapterの内部クラスのRelt Handler動的エージェントインターフェースを介して行われる.
次のすべての操作は、RestHandler()で行われます.
serviceMethodInfocacheにはRealServiceインターフェースごとに対応するRestMethodInfoキューがキャッシュされています.各MethodはRets MethodInfoに対応しています.
RealServiceインターフェースに対応するキューをデフォルトで作成し、serviceMethodInfoCacheに追加します.
RealServiceがget List()メソッドを呼び出してデータを取得すると、RealServiceがRest Handler()に実行されます.
@Override
public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable {
//
if (method.getDeclaringClass()
== Object.class) {
return method.invoke(this, args);
}
// RealService
// methodDetailsCache ,
// Method RestMethodInfo
// Method
final RestMethodInfo methodInfo = getMethodInfo(methodDetailsCache, method);
// object
if (methodInfo.isSynchronous) {
try {
return invokeRequest(requestInterceptor, methodInfo, args);
} catch (RetrofitError error) {
throw newError;
}
}
// RequestInterceptorTape
final RequestInterceptorTape interceptorTape = new RequestInterceptorTape();
// Callback,
Callback<?> callback = (Callback<?>) args[args.length - 1];
// CallbackRunnable
// 1. obtainResponse()
// ResponseWrapper
// 2. Callback Response
httpExecutor.execute(new
CallbackRunnable(callback,
callbackExecutor) {
@Override
public ResponseWrapper obtainResponse() {
return (ResponseWrapper)
invokeRequest(interceptorTape,
methodInfo, args);
}
});
return null;
}
私たちは最後にRertAdapter(zhi invoke Request)メソッドに呼び出されました.この方法はブロック、http要求の詳細を封入して、Reponseに戻りました.この方法の論理を重点的に見ます.
// RestMethodInfo ,
//
methodInfo.init();
Restit MethodInfo()メソッドのコードを見てみましょう. synchronized void init() {
if(!this.loaded) {
this.parseMethodAnnotations();
this.parseParameters();
this.loaded = true;
}
}
そして、RestMethodInfo()は主に方法の注釈を解析しました.そこで@POST、@GETに対して相対的な経路を注釈する処理を見てみます. private void parsePath(String path) {
// / ,
if (path == null || path.length() == 0 || path.charAt(0) != '/') {
throw methodError("URL path \"%s\" must start with '/'.", path);
}
//url
String url = path;
String query = null;
int question = path.indexOf('?');
// ,
if (question != -1 && question < path.length() - 1) {
url = path.substring(0, question);
query = path.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(query);
// ,
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block.", query);
}
}
//
Set<String> urlParams = parsePathParameters(path);
//requestUrl
requestUrl = url;
//requestUrlParamNames
requestUrlParamNames = urlParams;
//requestQuery :
//' id=1&name="android" '
requestQuery = query;
}
解析パラメータの注釈を担当する方法のRestMethodInfoは、この方法の各パラメータの名称をrequest ParameNames配列に置き、各パラメータの注釈のタイプをrequest ParameUsage配列に置く.この時、この方法の注釈はすでに解析済みで、このRequest Builderを作成します.
// RequestBuilder RestMethodInfo
//queryParams:' ?id=1&name="android" '
RequestBuilder requestBuilder = new
RequestBuilder(serverUrl, methodInfo, converter);
// head、url args ,args null ,
requestBuilder.setArguments(args);
// url
methodInfo.setRelativeUrl(requestBuilder.getRelativeUrl());
methodInfo.setArgs(args);
この時、私たちはもうRequest Builderにrequest時に必要なパラメータを用意しました.遮る
http要求前に、headに公共パラメータを追加したいかもしれません.フレームワークはRequest Interceptorインターフェースを提供してくれます.Request Intercept orを通じて、パラメータを修正できます.
public interface RequestInterceptor {
void intercept(RequestInterceptor.RequestFacade var1);
public interface RequestFacade {
void addHeader(String var1, String var2);
void addPathParam(String var1, String var2);
void addEncodedPathParam(String var1,
String var2);
void addQueryParam(String var1, String var2);
void addEncodedQueryParam(String var1,
String var2);
RestMethodInfo getMethodInfo();
}
}
実際のコードの中でどうやってブロックされているか確認します.//
if (requestInterceptor instanceof
RequestInterceptorTape) {
RequestInterceptorTape interceptorTape =
(RequestInterceptorTape) requestInterceptor;
interceptorTape.setMethodInfo(methodInfo);
// interceptorTape
// RequestInterceptorTape#add*() tape
// Command
RestAdapter.this.requestInterceptor
.intercept(interceptorTape);
// interceptorTape requestBuilder
// RequestInterceptorTape#intercept()
// tape Command requestBuilder
interceptorTape.intercept(requestBuilder);
} else { //
// requestBuilder
requestInterceptor.intercept(requestBuilder);
}
パラメータを追加したら、私たちのブロックのタスクも完了します.戻る
ブロックが終わったら、Request Buiderを通じて、urlのルートと相対パスを合わせて組み付けて、Request BuiderをRequestに転化しました.
Request request = requestBuilder.build();
現在のプラットフォームに従って要求を実行して、Resonseに戻ります.Response response = clientProvider.get().execute(request);
タイプは方法の返却タイプで、これはAPIリターンデータです.Typed InputをPOJOに変換する時、Coverter_from Body(Typed Input、Type)の第二のパラメータで、私達の方法が戻るタイプを表しています.Type type = methodInfo.responseObjectType;
戻り値の状態コードが2 XXでないと、RetrofitErrが異常です.リターンされた状態コードが2 XXである場合、APIの成功を要求することを説明する.
もし戻ってきたのがレスリングだったら、そのまま戻ってきます.
if (type.equals(Response.class)) {
response = Utils.readBodyToBytesIfNecessary(response);
if (methodInfo.isSynchronous) {
return response;
}
return new ResponseWrapper(response, response);
}
もし戻るタイプがResonseではないなら、私達は転換します.TypedInput body = response.getBody();
if (body == null) {
return new ResponseWrapper(response, null);
}
ExceptionCatchingTypedInput wrapped = new ExceptionCatchingTypedInput(body);
try {
// wrapped type
Object convert = converter.fromBody(wrapped, type);
if (methodInfo.isSynchronous) {
return convert;
}
return new ResponseWrapper(response, convert);
} catch (ConversionException e) {
}
Responseに戻ったら同期したら、非同期スレッドに戻り、UIスレッドデータの更新を通知します.非同期であれば、Callbackに通知し、直接UIスレッドでデータを更新すれば良い.使用時の注意点をまとめます.1.非同期の最後のパラメータはCallbackで、戻りのタイプはvoidです.同期パラメータにはCallbackがありません.戻るタイプはObjectです.2.方法によってのみ注釈されている@FormUrlEncoded、@MultiiPadとパラメータ注釈@Bodyが注釈されている場合、パラメータの中にPOJOパラメータがあります.3.requestのPOJOはCoverter_Body(Object)を介してTypedOutputに変換されました.4.レスポンスはCoverter_from Body(Typed Input、Type)を通じてTyped InputをPOJOに変換したものです.5.@GETまたは@POSTの相対パスは/で始まる必要があります.さもなければ異常を投げます.6.私たちが定義したAPIを要求するインターフェースのパラメータにはnull値がないと、例外をスローします.
はい、Retrofitフレームに関する分析はこれで終わります.