Http請求是作Android應用開發工做幾乎必需要用到的東西。作Android開發這幾年,從最開始仿照網上代碼本身使用apache的DefaultHttpClient封裝網絡請求工具類,到後面開始使用GitHub上面的一些http框架,Afinal,xUtils到Volley,AsyncHttpClient等,網上這些http框架大多都還比較易用,可是作實際業務中仍是感受到業務和界面代碼與Http請求的代碼仍是耦合性太高,特別是在服務器接口比較多的時候。因此本身在之前的項目中也一直在嘗試作一些封裝解耦,可是一直感受達不到本身想要的效果,直到看到Retrofit這個類庫。java
在斷斷續續看了幾個月的OkHttpClient和Retrofit源碼,我終於決定嘗試着封裝一個本身的框架:httplitereact
httplite類庫主要實現瞭如下特性git
目前類庫底層的http實現提供了okhttp2.x/okhttp3.x/URLConnection三種可選.github
使用okhttp 2.7.5做爲http實現apache
compile 'alexclin.httplite:httplite-okhttp2:1.1.1' compile 'com.squareup.okhttp:okhttp:2.7.5'
使用okhttp 3.2.0做爲http實現json
compile 'alexclin.httplite:httplite-okhttp3:1.1.1' compile 'com.squareup.okhttp3:okhttp:3.2.0'
使用系統URLConnection做爲http實現api
compile 'alexclin.httplite:httplite-url:1.1.1'
如需Rx擴展則還須要數組
compile 'alexclin.httplite:retrofit-rx:1.1.1' compile 'io.reactivex:rxjava:1.1.1'
或者直接使用releaselib中的jar包服務器
1 okhttp2: httplite1.1.1.jar+httplite-ok2lite1.1.1.jar+okhttp 2.x.x版本jar包網絡
2 okhttp3: httplite1.1.1.jar+httplite-ok3lite1.1.1.jar+okhttp 3.x.x版本jar包
3 url: httplite1.1.1.jar+httplite-urlite1.1.1.jar
4 使用rx擴展:httplite-retrofit-rx1.1.1.jar+rx1.x.x版本jar包
或者也能夠直接使用jar包 首先建立HttpLiteBuilder進行配置,目前有三種HTTP實現可選
//使用OkHttp2.x做爲Http實現 HttpLiteBuilder builder = Ok2Lite.create(); //使用OkHttp3.x做爲Http實現 HttpLiteBuilder builder = Ok3Lite.create(); //使用系統URLConnection做爲http實現 HttpLiteBuilder builder = URLite.create();
對Builder進行配置
builder = builder.setConnectTimeout(10, TimeUnit.SECONDS) //設置鏈接超時 .setWriteTimeout(10, TimeUnit.SECONDS) //設置寫超時 .setReadTimeout(10, TimeUnit.SECONDS) //設置讀超時 .setMaxRetryCount(2) //設置失敗重試次數 .setFollowRedirects(true) //設置是否sFollowRedirects,默認false .setFollowSslRedirects(true) //設置是否setFollowSslRedirects .addResponseParser(new GsonParser()) .baseUrl("http://192.168.99.238:10080/")//BaseUrl .setProxy(...)// .setProxySelector(...)// .setSocketFactory(...)// .setSslSocketFactory(...)// .setHostnameVerifier(..)// .useCookie(...) //設置CookieStore,設置則啓用Cookie,不設置則不啓用 .addCallAdapter(new RxCallAdapter());//添加Rx支持
建立HttpLite實例
HttpLite httpLite = builder.build();
另外提供mock支持,需傳入MockHandler
httpLite = builder.mock(new MockHandler() { @Override public <T> void mock(Request request, Mock<T> mock) throws Exception { //模擬完整的http返回結果輸入流 mock.mock(int code,String msg,Map<String, List<String>> headers, final InputStream stream,MediaType mediaType); //直接模擬結果 mock(T result, Map<String, List<String>> headers); //模擬Json格式的結果 mock.mockJson(....); //以文件內容做爲Http返回結果輸入流 mock.mock(new File("....")); } @Override public boolean needMock(Request request) { //TODO 判斷該請求是否須要Mock return true; } });
發起普通GET請求
mHttpLite.url(url).header("header","not chinese").header("test_header","2016-01-06") .header("double_header","header1").addHeader("double_header","head2") .param("type","json").param("param2","You dog").param("param3", "中文") .get().async(new Callback<Result<List<FileInfo>>>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers,Result<List<FileInfo>> result) { //TODO } @Override public void onFailed(Request req, Exception e) { //TODO } });
發起post請求,監聽進度
//multipart上傳文件 MediaType type = mHttpLite.parse(MediaType.MULTIPART_FORM+";charset=utf-8"); RequestBody body = mHttpLite.createRequestBody(mHttpLite.parse(MediaType.APPLICATION_STREAM),file); mHttpLite.url("/").multipartType(type).multipart("早起早睡","身體好").multipart(info.fileName,info.hash).multipart(info.fileName,info.filePath,body) .onProgress(new ProgressListener() { @Override public void onProgressUpdate(boolean out, long current, long total) { LogUtil.e("是否上傳:"+out+",cur:"+current+",total:"+total); } }) .post().async(new Callback<Result<String>>() { @Override public void onSuccess(Request req,Map<String, List<String>> headers,Result<String> result) { LogUtil.e("Result:"+result); } @Override public void onFailed(Request req, Exception e) { LogUtil.e("onFailed:"+e); e.printStackTrace(); } }); //post json mHttpLite.url("/").post(MediaType.APPLICATION_JSON, JSON.toJSONString(info)).async(new Callback<String>() { @Override public void onSuccess(Request req,Map<String, List<String>> headers,String result) { LogUtil.e("Result:" + result); } @Override public void onFailed(Request req, Exception e) { LogUtil.e("E:" + e.getLocalizedMessage()); e.printStackTrace(); } }); //post form表單 mHttpLite.url("/").form("&test1","name&1").form("幹撒呢","whatfuck").formEncoded(Uri.encode("test&2"),Uri.encode("name&2")).post().async(new Callback<String>() { @Override public void onSuccess(Request req,Map<String, List<String>> headers,String result) { LogUtil.e("Result:" + result); } @Override public void onFailed(Request req, Exception e) { LogUtil.e("E:" + e.getLocalizedMessage()); e.printStackTrace(); } });
下載文件
mHttpLite.url(url).intoFile(dir,name,true,true) .onProgress(new ProgressListener() { @Override public void onProgressUpdate(boolean out, long current, long total) { //TODO } }) .download(new Callback<File>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers, File result) { //TODO } @Override public void onFailed(Request req, Exception e) { //TODO } });
//生成API接口實例 final SampleApi api = mHttplite.retrofit(SampleApi.class); //調用異步方法 api.login("user", "pass", "token", new Callback<Result<UserInfo>>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers, Result<UserInfo> result) { //TODO } @Override public void onFailed(Request req, Exception e) { //TODO } }); //調用異步方法 new Thread(){ @Override public void run() { //獲取知乎主頁數據 try { ZhihuData data = api.syncZhihu(); //TODO } catch (Exception e) { //TODO } } }.start(); //生成Call final Call call = api.zhihuCall(); //異步調用Call call.async(new Callback<ZhihuData>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers, ZhihuData result) { //TODO } @Override public void onFailed(Request req, Exception e) { //TODO } }); //或者同步調用Call new Thread(){ @Override public void run() { //獲取知乎主頁數據 try { ZhihuData data = call.sync(new Clazz<ZhihuData>(){}); //TODO } catch (Exception e) { //TODO } } }.start();
支持RxJava須要在配置HttpLiteBuilder時添加RxCallAdapter
HttpLiteBuilder builder = .... ..... builder.addCallAdapter(new RxCallAdapter()); .....
定義返回Obserable的API函數
@GET("http://news-at.zhihu.com/api/4/news/latest") Observable<ZhihuData> testZhihu();
使用返回的Obserable
Observable<ZhihuData> observable = apiService.testZhihu(); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<ZhihuData>() { @Override public void onCompleted() { LogUtil.e("onCompleted"); } @Override public void onError(Throwable e) { LogUtil.e("Onfailed", e); } @Override public void onNext(ZhihuData zhihuData) { LogUtil.e("Result:" + zhihuData); LogUtil.e("Result:" + (Thread.currentThread()== Looper.getMainLooper().getThread())); } });
自定義註解支持方法註解和參數註解
只需定義本身的註解,在HttpLite的Retrofit實例中添加對應註解的處理器便可
定義註解和註解處理器,此處只列出GsonFieldProcesscor代碼,詳細參考Demo
public class GsonFieldProcesscor implements ParamMiscProcessor { public static final String BODY_TYPE = "gson_json_body"; @Override public void process(Request request, Annotation[][] annotations, List<Pair<Integer, Integer>> list, Object... args) { //處理全部帶有Gson註解的參數,list中存儲的是全部Gson註解的位置 JsonObject object = new JsonObject(); for(Pair<Integer,Integer> pair:list){ int argPos = pair.first; int annotationPos = pair.second; if(args[argPos]==null) continue; GsonField annotation = (GsonField) annotations[argPos][annotationPos]; String key = annotation.value(); if(args[argPos] instanceof String){ object.addProperty(key,(String)args[argPos]); }else if(args[argPos] instanceof JsonElement){ object.add(key,(JsonElement)args[argPos]); }else if(args[argPos] instanceof Boolean){ object.addProperty(key,(Boolean)args[argPos]); }else if(args[argPos] instanceof Number){ object.addProperty(key,(Number)args[argPos]); } } request.body(MediaType.APPLICATION_JSON,object.toString()); } @Override public boolean support(Annotation annotation) { return annotation instanceof GsonField; } @Override public void checkParameters(Method method, Annotation annotation, Type parameterType) throws RuntimeException { //在此函數中檢查參數類型是否認義正確 if(!gsonSupportType(parameterType)){ throw Util.methodError(method,"Annotation @GsonField only support parameter type String/JsonElement/Boolean/Number/int/long/double/short"); }if(TextUtils.isEmpty(((GsonField)annotation).value())){ throw Util.methodError(method,"The annotation {@GsonField(value) value} must not be null"); } } private boolean gsonSupportType(Type type){ return type==String.class || Util.isSubType(type,JsonElement.class) || type == int.class || type == long.class || type == double.class || type == short.class || Util.isSubType(type,Number.class) || type == boolean.class || type == Boolean.class; } }
@BaseURL("http://192.168.99.238:10080/") public interface CustomApi { @GET("/login") void login( @Query("username") String userName, @Query("password") String password, @Query("token") String token, @Tag Object tag, Callback<Result<UserInfo>> callback ); @POST("/test") void testPost(@GsonField("param1") String param1, @GsonField("param1")String param2, Callback<Result<RequestInfo>> callback); }
//添加自定義註解處理器 //普通的參數註解處理ParamterProcessor Retrofit.registerParamterProcessor(new QueryProcessor()); //對個參數組合到一塊兒的參數註解處理ParamMiscProcessor,如將多個參數組合成一個json字符串做爲請求的BODY Retrofit.registerParamMiscProcessor(new GsonFieldProcesscor()); //當註解處理的參數是用做Body時,還須要註冊Body類型 Retrofit.basicAnnotationRule().registerBodyAnnotation(GsonField.class, GsonFieldProcesscor.BODY_TYPE,true);
//建立實例 CustomApi api = mHttplite.retrofit(CustomApi.class); //發起請求 Object tag = new Object(); api.login("user", "pass", "token", tag, new Callback<Result<UserInfo>>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers, Result<UserInfo> result) { //TODO } @Override public void onFailed(Request req, Exception e) { //TODO } }); api.testPost("test1", "test2", new Callback<Result<RequestInfo>>() { @Override public void onSuccess(Request req, Map<String, List<String>> headers, Result<RequestInfo> result) { //TODO LogUtil.e("Result:"+result); } @Override public void onFailed(Request req, Exception e) { //TODO LogUtil.e("onFailed",e); } }); }
HttpLite支持在建立API接口實例時傳入RequestListener和MethodFilter
SampleApi api = mHttplite.retrofit(SampleApi.class,listener,filter);
RequestListener listener = new RequestListener() { @Override public void onRequest(HttpLite lite, Request request, Type resultType) { LogUtil.e("RequestUrl:"+request.rawUrl()); //添加通用參數 request.param("commonParam","1234"); } };
MethodFilter filter = new MethodFilter() { @Override public Object onMethod(HttpLite lite, final MethodInvoker invoker, final Object[] args) throws Throwable { String publicKey = ...... if(TextUtils.isEmpty(publicKey)){ //publicKey是空,則先請求key new Thread(){ @Override public void run() { //獲取key ...... //獲取key成功後再發起真正的請求 invoker.invoke(args); } }.start(); }else{ return invoker.invoke(args); } } };
默認支持String的解析,可是類對象結果的解析須要使用httpLite.addResponseParser()添加支持該類型的解析器ResponseParser,可添加多個以便支持多種不一樣的結果解析
ResponseParser接口定義以下:
public interface ResponseParser { boolean isSupported(Type type); <T> T praseResponse(Response response, Type type) throws Exception; }
能夠經過實現此接口解析Json,XML或者二進制流爲對象的功能
demo模塊app中分別有使用Jackson,FastJson,Gson實現Json解析,經過繼承StringParser實現。
public abstract class StringParser implements ResponseParser{ @Override public final <T> T praseResponse(Response response, Type type) throws Exception{ return praseResponse(HttpCallback.decodeResponseToString(response),type); } public abstract <T> T praseResponse(String content, Type type) throws Exception; }