Source code analysis--xutil3 network source code analysis

What we are looking for here is the post method of synchronous request (the method of asynchronous request is also similar, and eventually both synchronous and asynchronous will call the same piece of code), the process is as follows

public <T> T postSync(RequestParams entity, Class<T> resultType) throws Throwable {
    return this.requestSync(HttpMethod.POST, entity, resultType);
}
public <T> T requestSync(HttpMethod method, RequestParams entity, Class<T> resultType) throws Throwable {
    HttpManagerImpl.DefaultSyncCallback<T> callback = new HttpManagerImpl.DefaultSyncCallback(resultType);
    return this.requestSync(method, entity, (TypedCallback)callback);
}

public <T> T requestSync(HttpMethod method, RequestParams entity, TypedCallback<T> callback) throws Throwable {
    entity.setMethod(method);
    HttpTask<T> task = new HttpTask(entity, (Cancelable)null, callback);
    return x.task().startSync(task);
}

Finally, a task is encapsulated to execute the network request task. It is worth noting that although xutil3 includes many functions, most of the functions will use its own task mode, so if you want to understand the source code of xutil, it is recommended to read it first. Take a look at the source code of his own task (task) related functions.

//TaskControllerImpl
public <T> T startSync(AbsTask<T> task) throws Throwable {
    Object result = null;

    try {
        task.onWaiting();
        task.onStarted();
        result = task.doBackground();
        task.onSuccess(result);
    } catch (CancelledException var8) {
        task.onCancelled(var8);
    } catch (Throwable var9) {
        task.onError(var9, false);
        throw var9;
    } finally {
        task.onFinished();
    }

    return result;
}

Mainly look at the doBackground method, corresponding to the HttpTask created in the previous step, look at the source code

//HttpTask
protected ResultType doBackground() throws Throwable {
    if (this.isCancelled()) {
        throw new CancelledException("cancelled before request");
    } else {
        ResultType result = null;
        this.resolveLoadType();
        this.request = this.createNewRequest();
        this.checkDownloadTask();
        boolean retry = true;
        int retryCount = 0;
        Throwable exception = null;
        HttpRetryHandler retryHandler = this.params.getHttpRetryHandler();
        if (retryHandler == null) {
            retryHandler = new HttpRetryHandler();
        }

        retryHandler.setMaxRetryCount(this.params.getMaxRetryCount());
        if (this.isCancelled()) {
            throw new CancelledException("cancelled before request");
        } else {
            Object cacheResult = null;
            if (this.cacheCallback != null && HttpMethod.permitsCache(this.params.getMethod())) {
                try {
                    this.clearRawResult();
                    LogUtil.d("load cache: " + this.request.getRequestUri());
                    this.rawResult = this.request.loadResultFromCache();
                } catch (Throwable var35) {
                    LogUtil.w("load disk cache error", var35);
                }

                if (this.isCancelled()) {
                    this.clearRawResult();
                    throw new CancelledException("cancelled before request");
                }

                if (this.rawResult != null) {
                    if (this.prepareCallback != null) {
                        try {
                            cacheResult = this.prepareCallback.prepare(this.rawResult);
                        } catch (Throwable var33) {
                            cacheResult = null;
                            LogUtil.w("prepare disk cache error", var33);
                        } finally {
                            this.clearRawResult();
                        }
                    } else {
                        cacheResult = this.rawResult;
                    }

                    if (this.isCancelled()) {
                        throw new CancelledException("cancelled before request");
                    }

                    if (cacheResult != null) {
                        this.update(2, new Object[]{cacheResult});

                        while(this.trustCache == null) {
                            Object var7 = this.cacheLock;
                            synchronized(this.cacheLock) {
                                try {
                                    this.cacheLock.wait();
                                } catch (InterruptedException var30) {
                                    throw new CancelledException("cancelled before request");
                                } catch (Throwable var31) {
                                    ;
                                }
                            }
                        }

                        if (this.trustCache) {
                            return null;
                        }
                    }
                }
            }

            if (this.trustCache == null) {
                this.trustCache = false;
            }

            if (cacheResult == null) {
                this.request.clearCacheHeader();
            }

            if (this.callback instanceof ProxyCacheCallback && ((ProxyCacheCallback)this.callback).onlyCache()) {
                return null;
            } else {
                retry = true;

                while(retry) {
                    retry = false;

                    try {
                        if (this.isCancelled()) {
                            throw new CancelledException("cancelled before request");
                        }

                        this.request.close();

                        try {
                            this.clearRawResult();
                            LogUtil.d("load: " + this.request.getRequestUri());
                            this.requestWorker = new HttpTask.RequestWorker((HttpTask.RequestWorker)null);
                            this.requestWorker.request();
                            if (this.requestWorker.ex != null) {
                                throw this.requestWorker.ex;
                            }

                            this.rawResult = this.requestWorker.result;
                        } catch (Throwable var36) {
                            this.clearRawResult();
                            if (this.isCancelled()) {
                                throw new CancelledException("cancelled during request");
                            }

                            throw var36;
                        }

                        if (this.prepareCallback != null) {
                            if (this.isCancelled()) {
                                throw new CancelledException("cancelled before request");
                            }

                            try {
                                result = this.prepareCallback.prepare(this.rawResult);
                            } finally {
                                this.clearRawResult();
                            }
                        } else {
                            result = this.rawResult;
                        }

                        if (this.cacheCallback != null && HttpMethod.permitsCache(this.params.getMethod())) {
                            this.request.save2Cache();
                        }

                        if (this.isCancelled()) {
                            throw new CancelledException("cancelled after request");
                        }
                    } catch (HttpRedirectException var37) {
                        retry = true;
                        LogUtil.w("Http Redirect:" + this.params.getUri());
                    } catch (Throwable var38) {
                        switch(this.request.getResponseCode()) {
                        case 204:
                        case 205:
                        case 304:
                            return null;
                        default:
                            exception = var38;
                            if (this.isCancelled() && !(var38 instanceof CancelledException)) {
                                exception = new CancelledException("canceled by user");
                            }

                            ++retryCount;
                            retry = retryHandler.canRetry(this.request, (Throwable)exception, retryCount);
                        }
                    }
                }

                if (exception != null && result == null && !this.trustCache) {
                    this.hasException = true;
                    throw (Throwable)exception;
                } else {
                    return result;
                }
            }
        }
    }
}

The code is very long, let's look at it step by step. First, initialize the data, and then check the cache, if there is a cache and the cache is available, then assign the member variable rawResult and do not perform subsequent network operations. If no cache is used, execute the following code


    retry = true;

    while(retry) {
        retry = false;

        try {
            if (this.isCancelled()) {
                throw new CancelledException("cancelled before request");
            }

            this.request.close();

            try {
                this.clearRawResult();
                LogUtil.d("load: " + this.request.getRequestUri());
                this.requestWorker = new HttpTask.RequestWorker((HttpTask.RequestWorker)null);
                this.requestWorker.request();
                if (this.requestWorker.ex != null) {
                    throw this.requestWorker.ex;
                }

                this.rawResult = this.requestWorker.result;
            } catch (Throwable var36) {
                this.clearRawResult();
                if (this.isCancelled()) {
                    throw new CancelledException("cancelled during request");
                }

                throw var36;
            }

            if (this.prepareCallback != null) {
                if (this.isCancelled()) {
                    throw new CancelledException("cancelled before request");
                }

                try {
                    result = this.prepareCallback.prepare(this.rawResult);
                } finally {
                    this.clearRawResult();
                }
            } else {
                result = this.rawResult;
            }

            if (this.cacheCallback != null && HttpMethod.permitsCache(this.params.getMethod())) {
                this.request.save2Cache();
            }

            if (this.isCancelled()) {
                throw new CancelledException("cancelled after request");
            }
        } catch (HttpRedirectException var37) {
            retry = true;
            LogUtil.w("Http Redirect:" + this.params.getUri());
        } catch (Throwable var38) {
            switch(this.request.getResponseCode()) {
            case 204:
            case 205:
            case 304:
                return null;
            default:
                exception = var38;
                if (this.isCancelled() && !(var38 instanceof CancelledException)) {
                    exception = new CancelledException("canceled by user");
                }

                ++retryCount;
                retry = retryHandler.canRetry(this.request, (Throwable)exception, retryCount);
            }
        }
    }

    if (exception != null && result == null && !this.trustCache) {
        this.hasException = true;
        throw (Throwable)exception;
    } else {
        return result;
    }

You can see the logic of network requests and retries, mainly look at the logic of network requests. The key code is these lines

this.requestWorker = new HttpTask.RequestWorker((HttpTask.RequestWorker)null);
this.requestWorker.request();
if (this.requestWorker.ex != null) {
    throw this.requestWorker.ex;
}

this.rawResult = this.requestWorker.result;

You can see that a RequestWork internal class is created and then the request() method is executed, follow up and take a look

public void request() {
    try {
        boolean interrupted = false;
        if (File.class == HttpTask.this.loadType) {
            while(true) {
                if (HttpTask.sCurrFileLoadCount.get() >= 3 && !HttpTask.this.isCancelled()) {
                    synchronized(HttpTask.sCurrFileLoadCount) {
                        try {
                            HttpTask.sCurrFileLoadCount.wait(10L);
                            continue;
                        } catch (InterruptedException var21) {
                            interrupted = true;
                        } catch (Throwable var22) {
                            continue;
                        }
                    }
                }

                HttpTask.sCurrFileLoadCount.incrementAndGet();
                break;
            }
        }

        if (interrupted || HttpTask.this.isCancelled()) {
            throw new CancelledException("cancelled before request" + (interrupted ? "(interrupted)" : ""));
        }

        try {
            HttpTask.this.request.setRequestInterceptListener(HttpTask.this.requestInterceptListener);
            this.result = HttpTask.this.request.loadResult();
        } catch (Throwable var20) {
            this.ex = var20;
        }

        if (this.ex != null) {
            throw this.ex;
        }
    } catch (Throwable var24) {
        this.ex = var24;
        if (var24 instanceof HttpException) {
            HttpException httpEx = (HttpException)var24;
            int errorCode = httpEx.getCode();
            if (errorCode == 301 || errorCode == 302) {
                RedirectHandler redirectHandler = HttpTask.this.params.getRedirectHandler();
                if (redirectHandler != null) {
                    try {
                        RequestParams redirectParams = redirectHandler.getRedirectParams(HttpTask.this.request);
                        if (redirectParams != null) {
                            if (redirectParams.getMethod() == null) {
                                redirectParams.setMethod(HttpTask.this.params.getMethod());
                            }

                            HttpTask.this.params = redirectParams;
                            HttpTask.this.request = HttpTask.this.createNewRequest();
                            this.ex = new HttpRedirectException(errorCode, httpEx.getMessage(), httpEx.getResult());
                        }
                    } catch (Throwable var19) {
                        this.ex = var24;
                    }
                }
            }
        }
    } finally {
        if (File.class == HttpTask.this.loadType) {
            synchronized(HttpTask.sCurrFileLoadCount) {
                HttpTask.sCurrFileLoadCount.decrementAndGet();
                HttpTask.sCurrFileLoadCount.notifyAll();
            }
        }

    }

}

The core code is

this.result = HttpTask.this.request.loadResult();

This request is created in doBackGround

protected ResultType doBackground() throws Throwable {
    if (this.isCancelled()) {
        throw new CancelledException("cancelled before request");
    } else {
        ResultType result = null;
        this.resolveLoadType();
        this.request = this.createNewRequest();
        ...
}

continue following

//HttpTask
private UriRequest createNewRequest() throws Throwable {
    this.params.init();
    UriRequest result = UriRequestFactory.getUriRequest(this.params, this.loadType);
    result.setCallingClassLoader(this.callback.getClass().getClassLoader());
    result.setProgressHandler(this);
    this.loadingUpdateMaxTimeSpan = (long)this.params.getLoadingUpdateMaxTimeSpan();
    this.update(1, new Object[]{result});
    return result;
}

//UriRequestFactory
public static UriRequest getUriRequest(RequestParams params, Type loadType) throws Throwable {
    String scheme = null;
    String uri = params.getUri();
    int index = uri.indexOf(":");
    if (index > 0) {
        scheme = uri.substring(0, index);
    } else if (uri.startsWith("/")) {
        scheme = "file";
    }

    if (!TextUtils.isEmpty(scheme)) {
        Class<? extends UriRequest> cls = (Class)SCHEME_CLS_MAP.get(scheme);
        if (cls != null) {
            Constructor<? extends UriRequest> constructor = cls.getConstructor(RequestParams.class, Class.class);
            return (UriRequest)constructor.newInstance(params, loadType);
        } else if (scheme.startsWith("http")) {
            return new HttpRequest(params, loadType);
        } else if (scheme.equals("assets")) {
            return new AssetsRequest(params, loadType);
        } else if (scheme.equals("file")) {
            return new LocalFileRequest(params, loadType);
        } else {
            throw new IllegalArgumentException("The url not be support: " + uri);
        }
    } else {
        throw new IllegalArgumentException("The url not be support: " + uri);
    }
}

Since the parameter starts with "http", the HttpRequest class is returned here. Take a look at the loadResult method inside.

//HttpRequest
public Object loadResult() throws Throwable {
    this.isLoading = true;
    return super.loadResult();
}
//HttpRequest的父类UriRequest
public Object loadResult() throws Throwable {
    return this.loader.load(this);
}

So now the problem is to find the loader, which is initialized in the URIRequest constructor

//UriRequest
UriRequest(RequestParams params, Type loadType) throws Throwable {
    this.params = params;
    this.queryUrl = this.buildQueryUrl(params);
    this.loader = LoaderFactory.getLoader(loadType, params);
}

//LoaderFactory
public static Loader<?> getLoader(Type type, RequestParams params) {
    Loader<?> result = (Loader)converterHashMap.get(type);
    Object result;
    if (result == null) {
        result = new ObjectLoader(type);
    } else {
        result = result.newInstance();
    }

    ((Loader)result).setParams(params);
    return (Loader)result;
}

You can see that the ObjectLoader class is created, take a look at the load method of this class


public Object load(InputStream in) throws Throwable {
    Object result;
    if (this.parser instanceof InputStreamResponseParser) {
        result = ((InputStreamResponseParser)this.parser).parse(this.objectType, this.objectClass, in);
    } else {
        this.resultStr = IOUtil.readStr(in, this.charset);
        result = this.parser.parse(this.objectType, this.objectClass, this.resultStr);
    }

    return result;
}

public Object load(UriRequest request) throws Throwable {
    try {
        request.sendRequest();
    } finally {
        this.parser.checkResponse(request);
    }

    return this.load(request.getInputStream());
}

Since the incoming parameter is URIRequest, the following load method is used here. Let's go back and look at the sendRequest method of HttPRequest.

//HttpRequest
@TargetApi(19)
public void sendRequest() throws Throwable {
    this.isLoading = false;
    this.responseCode = 0;
    URL url = new URL(this.queryUrl);
    Proxy proxy = this.params.getProxy();
    if (proxy != null) {
        this.connection = (HttpURLConnection)url.openConnection(proxy);
    } else {
        this.connection = (HttpURLConnection)url.openConnection();
    }

    if (VERSION.SDK_INT < 19) {
        this.connection.setRequestProperty("Connection", "close");
    }

    this.connection.setReadTimeout(this.params.getConnectTimeout());
    this.connection.setConnectTimeout(this.params.getConnectTimeout());
    this.connection.setInstanceFollowRedirects(this.params.getRedirectHandler() == null);
    if (this.connection instanceof HttpsURLConnection) {
        SSLSocketFactory sslSocketFactory = this.params.getSslSocketFactory();
        if (sslSocketFactory != null) {
            ((HttpsURLConnection)this.connection).setSSLSocketFactory(sslSocketFactory);
        }
    }

    Map headers;
    if (this.params.isUseCookie()) {
        try {
            headers = COOKIE_MANAGER.get(url.toURI(), new HashMap(0));
            List<String> cookies = (List)headers.get("Cookie");
            if (cookies != null) {
                this.connection.setRequestProperty("Cookie", TextUtils.join(";", cookies));
            }
        } catch (Throwable var11) {
            LogUtil.e(var11.getMessage(), var11);
        }
    }

    List<Header> headers = this.params.getHeaders();
    if (headers != null) {
        Iterator var4 = headers.iterator();

        while(var4.hasNext()) {
            Header header = (Header)var4.next();
            String name = header.key;
            String value = header.getValueStr();
            if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
                if (header.setHeader) {
                    this.connection.setRequestProperty(name, value);
                } else {
                    this.connection.addRequestProperty(name, value);
                }
            }
        }
    }

    if (this.requestInterceptListener != null) {
        this.requestInterceptListener.beforeRequest(this);
    }

    HttpMethod method = this.params.getMethod();

    try {
        this.connection.setRequestMethod(method.toString());
    } catch (ProtocolException var10) {
        try {
            Field methodField = HttpURLConnection.class.getDeclaredField("method");
            methodField.setAccessible(true);
            methodField.set(this.connection, method.toString());
        } catch (Throwable var9) {
            throw var10;
        }
    }

    if (HttpMethod.permitsRequestBody(method)) {
        RequestBody body = this.params.getRequestBody();
        if (body != null) {
            if (body instanceof ProgressBody) {
                ((ProgressBody)body).setProgressHandler(this.progressHandler);
            }

            String contentType = body.getContentType();
            if (!TextUtils.isEmpty(contentType)) {
                this.connection.setRequestProperty("Content-Type", contentType);
            }

            long contentLength = body.getContentLength();
            if (contentLength < 0L) {
                this.connection.setChunkedStreamingMode(262144);
            } else if (contentLength < 2147483647L) {
                this.connection.setFixedLengthStreamingMode((int)contentLength);
            } else if (VERSION.SDK_INT >= 19) {
                this.connection.setFixedLengthStreamingMode(contentLength);
            } else {
                this.connection.setChunkedStreamingMode(262144);
            }

            this.connection.setRequestProperty("Content-Length", String.valueOf(contentLength));
            this.connection.setDoOutput(true);
            body.writeTo(this.connection.getOutputStream());
        }
    }

    if (this.params.isUseCookie()) {
        try {
            headers = this.connection.getHeaderFields();
            if (headers != null) {
                COOKIE_MANAGER.put(url.toURI(), headers);
            }
        } catch (Throwable var8) {
            LogUtil.e(var8.getMessage(), var8);
        }
    }

    this.responseCode = this.connection.getResponseCode();
    if (this.requestInterceptListener != null) {
        this.requestInterceptListener.afterRequest(this);
    }

    if (this.responseCode != 204 && this.responseCode != 205) {
        if (this.responseCode >= 300) {
            HttpException httpException = new HttpException(this.responseCode, this.getResponseMessage());

            try {
                httpException.setResult(IOUtil.readStr(this.getInputStream(), this.params.getCharset()));
            } catch (Throwable var7) {
                ;
            }

            LogUtil.e(httpException.toString() + ", url: " + this.queryUrl);
            throw httpException;
        } else {
            this.isLoading = true;
        }
    } else {
        throw new HttpException(this.responseCode, this.getResponseMessage());
    }
}

Here we can see the method of network request. The HttpURLConnection used here can see the network request here, and it also supports SSL. Finally, the response is obtained in this line

body.writeTo(this.connection.getOutputStream());

The following codes are to process cookies, and then throw some connection error response codes in the form of Exceptions, and return normal results.

 

 

 

Guess you like

Origin blog.csdn.net/just_hu/article/details/105770496