美文网首页
基础模块封装 -- 网络请求

基础模块封装 -- 网络请求

作者: TomyZhang | 来源:发表于2020-05-24 15:57 被阅读0次

一、网络请求抽象类

public abstract class AbstractHttpRequest {
    public abstract String url();
    public abstract String method();
    public abstract Map<String, String> headers();
    public abstract String getHeader(String key);
    public abstract long cacheAge();
    public abstract Object tag();
    public abstract void cancel();

    static abstract class AbstractBuilder{
        public abstract AbstractBuilder url(String url);
        public abstract AbstractBuilder method(String method);
        public abstract AbstractBuilder headers(Map<String, String> headers);
        public abstract AbstractBuilder query(String name, String value);
        public abstract AbstractBuilder query(String name, String value, boolean encoded);
        public abstract AbstractBuilder path(String path);
        public abstract AbstractBuilder path(String path, boolean encoded);
        public abstract AbstractBuilder addHeader(String key, String value);
        public abstract AbstractBuilder cacheAge(long cacheAge);
        public abstract AbstractBuilder requestBody(HttpRequestBody body);
        public abstract AbstractBuilder removeHeader(String key);
        public abstract AbstractBuilder tag(Object tag);
        public abstract AbstractHttpRequest build();
    }
}

二、网络请求实现类

public class HttpRequest extends AbstractHttpRequest {

    public static final String GET = "GET";
    public static final String POST = "POST";
    public static final String DEL = "DEL";
    public static final String PUT = "PUT";

    Request request;
    Call call;

    @StringDef({GET,POST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface HttpMethod{}

    private HttpRequest(@NonNull Request request){
        this.request = request;
    }

    @Override
    public String url() {
        if(request.url() != null){
            return request.url().toString();
        }else{
            return "";
        }
    }

    @Override
    public String method() {
        return request.method();
    }

    @Override
    public Map<String, String> headers() {
        ArrayMap<String, String> headers = new ArrayMap<>();
        if(request.headers() != null){
            Set<String> keys = request.headers().names();
            if(keys != null){
                for(String key : keys){
                    headers.put(key, request.headers().get(key));
                }
            }
        }
        return headers;
    }

    @Override
    public String getHeader(String key) {
        if(!TextUtils.isEmpty(key) && request.headers() != null){
            return request.headers().get(key);
        }else{
            return "";
        }
    }

    @Override
    public long cacheAge() {
        CacheControl cacheControl = request.cacheControl();
        if(cacheControl != null){
            return cacheControl.maxAgeSeconds() * 1000L;
        }
        return 0;
    }

    @Override
    public Object tag() {
        return request.tag();
    }

    @Override
    public void cancel() {
        if(call != null && !call.isCanceled()){
            call.cancel();
        }
    }

    public static class Builder extends AbstractBuilder{
        Request.Builder builder;
        String method = GET;
        HttpUrl.Builder urlBuilder;
        RequestBody requestBody = null;

        public Builder(){
            builder = new Request.Builder();
            urlBuilder = new HttpUrl.Builder();
        }

        @Override
        public Builder url(String url) {
            HttpUrl httpUrl = HttpUrl.parse(url);
            if(httpUrl != null){
                urlBuilder = httpUrl.newBuilder();
            }
            return this;
        }

        @Override
        public Builder method(@HttpMethod String method) {
            this.method = method;
            return this;
        }

        @Override
        public Builder headers(Map<String, String> headers) {
            if(headers != null){
                Set<String> keys = headers.keySet();
                for(String key : keys){
                    String value = headers.get(key);
                    if(value != null){
                        builder.addHeader(key, value);
                    }
                }
            }
            return this;
        }

        @Override
        public Builder query(String name, String value) {
            return query(name, value, true);
        }

        @Override
        public Builder query(String name, String value, boolean encoded) {
            if(encoded){
                urlBuilder.addEncodedQueryParameter(name, value);
            }else{
                urlBuilder.addQueryParameter(name, value);
            }
            return this;
        }

        @Override
        public Builder path(String path) {
            return path(path, false);
        }

        @Override
        public Builder path(String path, boolean encoded) {
            if(encoded){
                urlBuilder.addEncodedPathSegment(path);
            }else{
                urlBuilder.addPathSegment(path);
            }
            return this;
        }

        @Override
        public Builder addHeader(String key, String value) {
            if(!TextUtils.isEmpty(key) && value != null){
                builder.addHeader(key, value);
            }
            return this;
        }

        @Override
        public Builder cacheAge(long cacheAge) {
            if (cacheAge > 0) {
                CacheControl cacheControl = new CacheControl.Builder()
                        .maxAge((int)cacheAge, TimeUnit.MILLISECONDS)
                        .build();
                builder.cacheControl(cacheControl);
            }
            return this;
        }

        @Override
        public Builder requestBody(HttpRequestBody body) {
            if(body == null){
                return this;
            }
            switch (body.getType()){
                case HttpRequestBody.TYPE_JSON:
                    String realBody = body.getContents().get("com.samsung.android.app.sreminder.json");
                    if(!TextUtils.isEmpty(realBody)){
                        MediaType JSON = MediaType.parse("application/json; charset="
                                + (TextUtils.isEmpty(body.getCharset()) ? "utf-8" : body.getCharset()));
                        requestBody = RequestBody.create(JSON, realBody);
                    }
                    break;
                case HttpRequestBody.TYPE_FORM:
                    requestBody = RequestBodyUtils.getFormBody(body.getContents());
                    break;
                default:break;
            }

            return this;
        }

        @Override
        public Builder removeHeader(String key) {
            if(!TextUtils.isEmpty(key)){
                builder.removeHeader(key);
            }
            return this;
        }

        @Override
        public Builder tag(Object tag) {
            builder.tag(tag);
            return this;
        }

        @Override
        public HttpRequest build() {

            if(requestBody != null){
                switch (method){
                    case GET:
                    case POST:
                        builder.post(requestBody);
                        break;
                    case DEL:
                        builder.delete(requestBody);
                        break;
                    case PUT:
                        builder.put(requestBody);
                        break;
                    default:
                        builder.post(requestBody);
                        break;
                }
            }else{
                switch (method){
                    case DEL:
                        builder.delete();
                        break;
                    default:
                        builder.get();
                        break;
                }
            }

            return new HttpRequest(builder.url(urlBuilder.build()).build());
        }
    }
}

三、网络请求实体类

public class HttpRequestBody {
    public static final int TYPE_JSON = 1;
    public static final int TYPE_FORM = 2;

    int type;
    String charset;
    Map<String, String> contents;

    private HttpRequestBody(int type, String charset){
        contents = new ArrayMap<>();
        this.type = type;
        this.charset = charset;
    }

    public static HttpRequestBody json(String json, String charset){
        HttpRequestBody requestBody = new HttpRequestBody(TYPE_JSON, charset);
        requestBody.add("com.samsung.android.app.sreminder.json", json);
        return requestBody;
    }

    public static HttpRequestBody json(Object obj, String charset){
        HttpRequestBody requestBody = new HttpRequestBody(TYPE_JSON, charset);
        if(obj != null){
            requestBody.add("com.samsung.android.app.sreminder.json", new Gson().toJson(obj));
        }
        return requestBody;
    }

    public static HttpRequestBody form(){
        return new HttpRequestBody(TYPE_FORM, null);
    }

    public void add(String key, String val){
        contents.put(key, val);
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public Map<String, String> getContents() {
        return contents;
    }

    public void setContents(Map<String, String> contents) {
        this.contents = contents;
    }
}

四、网络请求实体工具类

public class RequestBodyUtils {

    public static RequestBody getFormBody(Map<String, String> params){
        FormBody.Builder builder = new FormBody.Builder();
        if(params != null){
            Set<String> keys = params.keySet();
            for(String key : keys){
                builder.add(key, params.get(key));
            }
        }
        return builder.build();
    }
}

五、网络响应头信息类

public class ResponseInfo {

    //response's headers
    private int statusCode;

    private boolean isCache;

    private Map<String, List<String>> headers;

    private HttpRequest httpRequest;

    ResponseInfo(HttpRequest httpRequest){
        this.httpRequest = httpRequest;
    }

    void setStatusCode(int code) {
        this.statusCode = code;
    }

    void setCache(boolean isCache){
        this.isCache = isCache;
    }

    void setHeaders(Map<String, List<String>> headers){
        this.headers = headers;
    }

    public @Nullable HttpRequest getHttpRequest(){
        return httpRequest;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public boolean isCache() {
        return isCache;
    }

    public Map<String, List<String>> getHeaders(){
        return headers;
    }
}

六、网络响应总信息类

public class HttpResponse<T> {
    private T body;
    private ResponseInfo responseInfo;

    void setBody(T body) {
        this.body = body;
    }

    void setResponseInfo(ResponseInfo responseInfo) {
        this.responseInfo = responseInfo;
    }

    public ResponseInfo getResponseInfo() {
        return responseInfo;
    }

    public T getBody() {
        return body;
    }
}

七、SSLSocketFactory类

public class SSLSocketFactoryCompat extends SSLSocketFactory {
    private SSLSocketFactory defaultFactory;
    // Android 5.0+ (API level21) provides reasonable default settings
    // but it still allows SSLv3
    // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
    static String protocols[] = null, cipherSuites[] = null;
    static {
        try {
            SSLSocket socket = (SSLSocket)SSLSocketFactory.getDefault().createSocket();
            if (socket != null) {
                /* set reasonable protocol versions */
                // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
                // - remove all SSL versions (especially SSLv3) because they're insecure now
                List<String> protocols = new LinkedList<>();
                for (String protocol : socket.getSupportedProtocols())
                    if (!protocol.toUpperCase().contains("SSL"))
                        protocols.add(protocol);
                SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]);
                /* set up reasonable cipher suites */
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    // choose known secure cipher suites
                    List<String> allowedCiphers = Arrays.asList(
                            // TLS 1.2
                            "TLS_RSA_WITH_AES_256_GCM_SHA384",
                            "TLS_RSA_WITH_AES_128_GCM_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                            "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
                            // maximum interoperability
                            "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_RSA_WITH_AES_128_CBC_SHA",
                            // additionally
                            "TLS_RSA_WITH_AES_256_CBC_SHA",
                            "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                            "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
                    List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
                    // take all allowed ciphers that are available and put them into preferredCiphers
                    HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
                    preferredCiphers.retainAll(availableCiphers);
                    /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
                     * ciphers which are enabled by default, but have become unsecure), but I guess for
                     * the security level of DAVdroid and maximum compatibility, disabling of insecure
                     * ciphers should be a server-side task */
                    // add preferred ciphers to enabled ciphers
                    HashSet<String> enabledCiphers = preferredCiphers;
                    enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
                    SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public SSLSocketFactoryCompat(X509TrustManager tm) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, (tm != null) ? new X509TrustManager[] { tm } : null, null);
            defaultFactory = sslContext.getSocketFactory();
        } catch (GeneralSecurityException e) {
            throw new AssertionError(); // The system has no TLS. Just give up.
        }
    }
    private void upgradeTLS(SSLSocket ssl) {
        // Android 5.0+ (API level21) provides reasonable default settings
        // but it still allows SSLv3
        // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
        if (protocols != null) {
            ssl.setEnabledProtocols(protocols);
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) {
            ssl.setEnabledCipherSuites(cipherSuites);
        }
    }
    @Override
    public String[] getDefaultCipherSuites() {
        return cipherSuites;
    }
    @Override
    public String[] getSupportedCipherSuites() {
        return cipherSuites;
    }
    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        Socket ssl = defaultFactory.createSocket(s, host, port, autoClose);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket)ssl);
        return ssl;
    }
    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        Socket ssl = defaultFactory.createSocket(host, port);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket)ssl);
        return ssl;
    }
    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket)ssl);
        return ssl;
    }
    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        Socket ssl = defaultFactory.createSocket(host, port);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket)ssl);
        return ssl;
    }
    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket)ssl);
        return ssl;
    }
}

八、网络请求管理核心类

public class SAHttpClient {
    private static final String TAG = "SAHttpClient";
    private static final String CACHE_FILE_NAME = "SA_HTTP_CACHE";
    private static final int CACHE_SIZE = 10 * 1024 * 1024; // 10 MiB

    OkHttpClient httpClient;
    private Cache cache;

    private SAHttpClient() {
        initOkHttpClient();
    }

    public <T> T requestSync(@NonNull HttpRequest request, Class<T> clazz) throws IOException {
        HttpResponse<T> httpResponse = request(request, clazz);
        return httpResponse.getBody();
    }

    public <T> HttpResponse<T> request(@NonNull HttpRequest request, Class<T> clazz) throws IOException {
        Response response = httpClient.newCall(request.request).execute();
        HttpResponse<T> httpResponse = new HttpResponse<>();
        if (response != null) {
            ResponseInfo responseInfo = new ResponseInfo(request);
            responseInfo.setStatusCode(response.code());
            responseInfo.setCache(response.cacheResponse() != null);
            if(response.headers() != null){
                responseInfo.setHeaders(response.headers().toMultimap());
            }
            httpResponse.setResponseInfo(responseInfo);
            String url = "";
            if(response.request() != null && response.request().url() != null){
                url = response.request().url().toString();
            }
            SAappLog.dTag(TAG, "%1$s's response code: %2$d, message:%3$s",url, response.code(), response.message());
            try {
                T res;
                String resString = response.body().string();
                if (clazz != String.class) {
                    res = new Gson().fromJson(resString, clazz);
                } else {
                    res = (T) resString;
                }
                httpResponse.setBody(res);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return httpResponse;
    }

    public <T> void request(@NonNull final HttpRequest request, final Class<T> clazz, final HttpClientListener<T> httpClientListener){
        Call call = httpClient.newCall(request.request);
        request.call = call;
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                SAappLog.dTag(TAG, "onFailure :" + e.getMessage());

                if(httpClientListener != null){
                    httpClientListener.onFailure(e, new ResponseInfo(request));
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null) {
                    String url = "";
                    if(response.request() != null && response.request().url() != null){
                        url = getPrintableUrl(response.request().url());
                    }
                    SAappLog.dTag(TAG, "%1$s's response code: %2$d, message:%3$s",url, response.code(), response.message());
                    if(httpClientListener == null){
                        return;
                    }
                    ResponseInfo responseInfo = new ResponseInfo(request);
                    responseInfo.setStatusCode(response.code());
                    responseInfo.setCache(response.cacheResponse() != null);
                    if(response.headers() != null){
                        responseInfo.setHeaders(response.headers().toMultimap());
                    }
                    if (response.isSuccessful()) {
                        try {
                            String resString = response.body().string();
                            T res;
                            if (clazz != String.class) {
                                res = new Gson().fromJson(resString, clazz);
                            } else {
                                res = (T) resString;
                            }
                            httpClientListener.onResponse(res, responseInfo);
                        } catch (Exception e) {
                            e.printStackTrace();
                            httpClientListener.onFailure(e, responseInfo);
                        }
                    } else {
                        String message = response.message();
                        response.close();
                        httpClientListener.onFailure(new IllegalAccessException(message), responseInfo);
                    }
                } else {
                    if(httpClientListener != null){
                        httpClientListener.onFailure(new IllegalAccessException("null response"), new ResponseInfo(request));
                    }
                }
            }
        });
    }

    /** 根据Tag取消请求 */
    public void cancelTag(Object tag) {
        if (tag == null) return;
        SAappLog.dTag(TAG, "cancel tag:"  + tag.toString());
        for (Call call : httpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : httpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    /** 取消所有请求请求 */
    public void cancelAll() {
        SAappLog.dTag(TAG, "cancel all");
        if (httpClient == null) return;
        for (Call call : httpClient.dispatcher().queuedCalls()) {
            call.cancel();
        }
        for (Call call : httpClient.dispatcher().runningCalls()) {
            call.cancel();
        }
    }

    private void initOkHttpClient() {
        try {
            if (httpClient == null) {
                File httpCacheDirectory = new File(SReminderApp.getInstance().getCacheDir(), CACHE_FILE_NAME);
                cache = new Cache(httpCacheDirectory, CACHE_SIZE);

                OkHttpClient.Builder builder = new OkHttpClient.Builder()
                        .cache(cache)
                        .connectTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(10, TimeUnit.SECONDS)
                        .writeTimeout(10, TimeUnit.SECONDS)
                        .addInterceptor(new ApplicationInterceptor())
                        .addNetworkInterceptor(new NetworkInterceptor());

                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    X509TrustManager trustAllCert = new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {

                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {

                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    };
                    SSLSocketFactory sslSocketFactory = new SSLSocketFactoryCompat(trustAllCert);
                    builder.sslSocketFactory(sslSocketFactory, trustAllCert);
                }

                if (BuildConfig.DEBUG) {
                    //when DEBUG , show the request body
                    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
                    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                    builder.addInterceptor(loggingInterceptor);
                }else if (Build.TYPE.equalsIgnoreCase("eng")){
                    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
                    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
                    builder.addInterceptor(loggingInterceptor);
                }

                httpClient = builder.build();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static final SAHttpClient getInstance() {
        return SAHttpClientHolder.INSTANCE;
    }

    public OkHttpClient getHttpClient() {
        initOkHttpClient();
        return httpClient;
    }

    private static class ApplicationInterceptor implements Interceptor {


        ApplicationInterceptor() {
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            if (chain != null && chain.request() != null) {
                if (chain.request().url() != null) {
                    SAappLog.dTag(TAG, "ApplicationInterceptor.intercept " + getPrintableUrl(chain.request().url()));
                }
                return chain.proceed(chain.request());
            } else {
                return new Response.Builder().build();
            }
        }
    }

    static String getPrintableUrl(HttpUrl httpUrl){
        if(BuildConfig.DEBUG || Build.TYPE.equalsIgnoreCase("eng")){
            return httpUrl.toString();
        }else{
            if (httpUrl.fragment() != null) {
                return httpUrl.fragment();
            } else if (httpUrl.host() != null) {
                return httpUrl.host();
            } else {
                return "dummy_url";
            }
        }
    }

    private static class NetworkInterceptor implements Interceptor {

        NetworkInterceptor() {
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            if (chain.request().url() != null) {
                SAappLog.dTag(TAG, "NetworkInterceptor.intercept " + getPrintableUrl(chain.request().url()));
            }
            return chain.proceed(chain.request());
        }

    }

    private static class SAHttpClientHolder {
        private static final SAHttpClient INSTANCE = new SAHttpClient();
    }

    public interface HttpClientListener<T> {
        void onResponse(T response, ResponseInfo responseInfo);

        void onFailure(Exception e, ResponseInfo responseInfo);
    }
}

相关文章

  • 组件化方案实践总结

    1.模块分离 1.基础模块基础模块主要封装网络请求,日志框架,路由设置等信息 业务模块业务模块依赖基础模块,实现不...

  • 基础模块封装 -- 网络请求

    一、网络请求抽象类 二、网络请求实现类 三、网络请求实体类 四、网络请求实体工具类 五、网络响应头信息类 六、网络...

  • WeiPeiYang阅读笔记(2)GPA2&tjuLibrary

    (网络请求的封装,在本模块中主要是Retrofit和RxJava还有OkHttp的配合。由于基础知识不够,有些原理...

  • AFNetworking的组成

    AFNetworking是封装的NSURLSession的网络请求,由五个模块组成: NSURLSession: ...

  • iOS AFN实现原理

    AFNetworking是封装的NSURLSession的网络请求 AFNetworking由五个模块组成: 分别...

  • iOS -AFN实现原理&&面试

    AFNetworking是封装的NSURLSession的网络请求。 AFNetworking由五个模块组成:分别...

  • 部分第三方实现原理

    AFNetworking AFNetworking是封装的NSURLSession的网络请求由五个模块组成: NS...

  • 记录Swift网络请求模块封装

    在使用Swift开发时,网络请求大多使用Alamofire,但使用时不是很方便,于是就计划封装下网络请求模块.实现...

  • AFNetworking 源码分析

    AFNetworking是封装的NSURLSession的网络请求,由五个模块组成:分别由NSURLSession...

  • AFNetworking源码解析

    AFNetworking是封装的NSURLSession的网络请求,由五个模块组成:分别由NSURLSession...

网友评论

      本文标题:基础模块封装 -- 网络请求

      本文链接:https://www.haomeiwen.com/subject/vbljahtx.html