Android網絡框架httplite使用指南

前言

Http請求是作Android應用開發工做幾乎必需要用到的東西。作Android開發這幾年,從最開始仿照網上代碼本身使用apache的DefaultHttpClient封裝網絡請求工具類,到後面開始使用GitHub上面的一些http框架,Afinal,xUtils到Volley,AsyncHttpClient等,網上這些http框架大多都還比較易用,可是作實際業務中仍是感受到業務和界面代碼與Http請求的代碼仍是耦合性太高,特別是在服務器接口比較多的時候。因此本身在之前的項目中也一直在嘗試作一些封裝解耦,可是一直感受達不到本身想要的效果,直到看到Retrofit這個類庫。java

在斷斷續續看了幾個月的OkHttpClient和Retrofit源碼,我終於決定嘗試着封裝一個本身的框架:httplitereact

類庫主要特性介紹

httplite類庫主要實現瞭如下特性git

  • 1.隔離了底層http實現,http實現可替換 雖然okhttpclient的實現很好,可是有時候也會由於項目包大小等緣由須要使用系統UrlConection來實現
  • 2.建造者模式的流式調用和結果解析的解耦 使用Request.url().param().header().***.async(Callback<T>)方式調用,可自定義多重ResponseParser來實現不一樣的http返回結果(json,protocolbuf等)
  • 3.支持使用相似Retrofit的方式,使用java接口類來定義後後臺API接口,而且支持RxJava,支持自定義註解

目前類庫底層的http實現提供了okhttp2.x/okhttp3.x/URLConnection三種可選.github

類庫使用指南

1、添加依賴

  • Gradle

使用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包

2、類庫初始化

或者也能夠直接使用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;
                      }
            });

2、普通方式發起http請求

發起普通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
                    }
                });

3、使用java接口定義API接口(相似Retrofit的功能)

1.基礎使用

//生成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();

2.RxJava的支持

支持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()));
       }
    });

3.自定義註解的使用

自定義註解支持方法註解參數註解

只需定義本身的註解,在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);
                    }
                });
    }

4.RequestListener和MethodFilter的使用

HttpLite支持在建立API接口實例時傳入RequestListener和MethodFilter

SampleApi api = mHttplite.retrofit(SampleApi.class,listener,filter);
  • RequestListener主要用於監聽接口中的請求,或者爲請求添加一些通用參數
RequestListener listener = new RequestListener() {
            @Override
            public void onRequest(HttpLite lite, Request request, Type resultType) {
                LogUtil.e("RequestUrl:"+request.rawUrl());
                //添加通用參數
                request.param("commonParam","1234");
            }
        };
  • MethodFilter主要用於給某些請求加一些前置操做
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);
                }
            }
        };

4、配置ResponseParser

默認支持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;
}
相關文章
相關標籤/搜索