在上一篇《實(shí)現(xiàn)一個(gè)簡單的Retrofit(一)》中使用 java 的動(dòng)態(tài)代理和注解實(shí)現(xiàn)的 Retrofit 的大體框架,可以實(shí)現(xiàn)一個(gè)基本的 url 的 get請求,如下:
interface SimpleRequest{
@GET
String goBaidu(@Url String url);
}
SimpleRequest simpleRequest = new Retro.
Builder().
build().
create(SimpleRequest.class);
println(simpleRequest.goBaidu("http://www.baidu.com"));
在這篇文章中我們需要實(shí)現(xiàn) Retrofit 的特性 CallAdapter。
在構(gòu)建 Retrofit 的實(shí)例的時(shí)候我們通常會調(diào)用 addCallAdapterFactory 方法,這個(gè)方法是什么作用我這里不多贅述,可以參考:http://square.github.io/retrofit/ 。
HttpCall
在上一篇中我們簡單的將 http 請求放在了 ServiceMethod 的 call 方法中,這樣做顯然是不妥當(dāng)?shù)?。這里我們?yōu)?http 請求抽象出一個(gè)名為 HttpCall 的類,代碼如下:
public class HttpCall {
private ServiceMethod mServiceMethod ;
interface Callback{
void onResponse(Response response);
}
public HttpCall(ServiceMethod serviceMethod){
mServiceMethod = serviceMethod;
}
public Response execute(){
HttpURLConnection connection;
Response response ;
try {
String urlStr = mServiceMethod.getUrl();
URL url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
//set http method
connection.setRequestMethod(mServiceMethod.getHttpMethod());
//set header
mServiceMethod.applyHeader(connection);
response = new Response(connection);
return response;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void submit(final Callback callback){
Response response = execute();
if(callback != null){
callback.onResponse(response);
}
}
}
此類比較簡單,主要有 execute 和 submit 兩個(gè)方法,前者直接執(zhí)行 http 請求并返回 Response,后者使用 Callback 的方式來傳遞 Response。
這里出現(xiàn)了一個(gè) Response 對象,在上一節(jié)中我們只是簡單的在 call 方法中返回了一個(gè)表示返回內(nèi)容的 String 對象,這樣也是不合理的,如果返回內(nèi)容是一個(gè)加密的字節(jié)數(shù)組或者發(fā)生了異常,顯然單單 String 是無法滿足的,所以這里還需要給 http 響應(yīng)做一個(gè)簡單封裝。
public class Response {
private int code = -1;
private HttpURLConnection connection;
private String errorMsg = "";
public Response(HttpURLConnection httpURLConnection) throws IOException {
connection = httpURLConnection;
code = connection.getResponseCode();
}
@Override
public String toString() {
return " response code : " + code + " \n errorMsg : " + errorMsg;
}
public boolean isError() {
return code >= 400;
}
public int getResponseCode() {
return code;
}
public String getResponseBodyAsString() {
if (code >= 400) {
throw new RuntimeException("response error , code = " + code);
}
InputStream inputStream = null;
ByteArrayOutputStream bos = null;
try {
inputStream = connection.getInputStream();
bos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
String body = new String(bos.toByteArray());
return body;
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();
}
return "";
}
public String getResponseErrorMsg(){
if (code < 400) {
throw new RuntimeException("not error , code = " + code);
}
InputStream inputStream = null;
ByteArrayOutputStream bos = null;
try {
inputStream = connection.getErrorStream();
bos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
String body = new String(bos.toByteArray());
return body;
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();
}
return "";
}
}
Response 類可以通過 isError 方法判斷請求是否出錯(cuò),然后通過 getResponseBodyAsString 和 getResponseErrorMsg 方法獲得對應(yīng)數(shù)據(jù),如果此時(shí)需要 InputStream 的話也可再擴(kuò)展這個(gè)類,這里為了簡單起見沒有進(jìn)行擴(kuò)展。
此時(shí)將 ServiceMethod 中的方法改為:
public Object call(){
return new HttpCall(this);
}
將接口改為:
interface SimpleRequest {
@GET
HttpCall go(@Url String url);
}
測試代碼調(diào)整為如下:
@Test
public void testAnnotation_Url() {
SimpleRequest simpleRequest = new Retro.
Builder().
build().
create(SimpleRequest.class);
HttpCall httpCall = simpleRequest.go("http://www.baidu.com");
Response response = httpCall.execute();
if(! response.isError()){
println(response.getResponseBodyAsString());
}
}
運(yùn)行接口OK。
到這里一個(gè)封裝相對較好的 http 請求庫已經(jīng)有了,但看著總覺得復(fù)雜了些,這里明明可以直接返回給我們 Response 對象,為什么還要多此一舉先返回個(gè) HttpCall 呢?對了,有這想法是正確的,畢竟我們要追求代碼的簡潔性,但在確保簡潔性的同時(shí)有不能丟失了它的擴(kuò)展性,所以這里對 HttpCall 的處理就交個(gè) CallAdapter 。
CallAdapter
CallAdapter 的責(zé)任就是將原本要返回的 HttpCall 對象做一次適配,將其轉(zhuǎn)換成其他相對適合你使用的類,CallAdapter 定義如下:
public interface CallAdapter<T> {
T adapter(HttpCall httpCall);
}
很簡單,只是一個(gè)接口,這里的泛型 T 就是目標(biāo)類型。假設(shè)我們需要在 Http 請求上使用觀察者模式,當(dāng)有請求得到響應(yīng)后將 Response 通知給所有觀察者。我們定義一個(gè)名為 ObservableCallAdapter 的類:
public class ObservableCallAdapter implements CallAdapter<Observable> {
@Override
public Observable adapter(final HttpCall httpCall) {
return new Observable(){
@Override
public synchronized void addObserver(Observer observer) {
super.addObserver(observer);
httpCall.submit(new HttpCall.Callback() {
@Override
public void onResponse(Response response) {
setChanged();
notifyObservers(response);
}
});
}
};
}
}
然后將 ServiceCall 的 call 方法修改成如下:
public Object call(HttpCall httpCall){
CallAdapter callAdapter = mCallAdapters.get(0);
return callAdapter.adapter(httpCall);
}
此時(shí)調(diào)用 ObservableCallAdapter 的 adapter 方法,返回的是一個(gè)被觀察對象,在 ObservableCallAdapter 中可以發(fā)現(xiàn)我們重寫了 Observable 的 addObserver 方法,當(dāng)有觀察者被添加進(jìn)來的時(shí)候就去進(jìn)行 http 請求并在得到 Response 后通知給觀察者。
我們來對我們的 SimpleRequest 做一個(gè)修改:
interface SimpleRequest {
@GET
Observable go(@Url String url);
}
測試代碼調(diào)整如下:
@Test
public void testAnnotation_Url() {
SimpleRequest simpleRequest = new Retro.
Builder().callAdapter(new ObservableCallAdapter()).
build().
create(SimpleRequest.class);
simpleRequest.go("http://www.baidu.com").addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
if(arg instanceof Response){
if(((Response) arg).isError()){
println(((Response) arg).getResponseBodyAsString());
}
}
}
});
}
這里總結(jié)一下 CallAdapter 的作用,如果你不滿足與 HttpCall 提供的方法,可以使用 CallAdapter 進(jìn)行封裝,然后提供更具靈活性的 http 響應(yīng)回調(diào)。
總結(jié)
CallAdapter 用途廣泛,在 Retrofit 中有大家熟知的 RxJava2CallAdapter,可以將請求響應(yīng)轉(zhuǎn)換成 RxJava 中的 Observable 對象并提供響應(yīng)的訂閱功能。
另外提一下,在下一篇中我們將實(shí)現(xiàn) Retrofit 中一個(gè)比較牛的特性:Converter 。