HttpStack接口及其實(shí)現(xiàn)

HttpStack接口及其實(shí)現(xiàn)

HttpStack

HttpStack是一個(gè)接口鬼佣,定義了實(shí)現(xiàn)網(wǎng)絡(luò)請求的方法:performRequest。
根據(jù)傳入的請求信息以及其他二外的Header信息琳状,實(shí)現(xiàn)網(wǎng)絡(luò)請求燎字,返回一個(gè)org.apache.http.HttpResponse螺句。代碼比較簡單,關(guān)鍵是實(shí)現(xiàn)它的子類(這個(gè)類是將要已經(jīng)被廢棄了梧宫,但是接谨,我看Volley代碼中還是用到了,所以塘匣,還是分析下)脓豪。

/**
 * An HTTP stack abstraction.
 *
 * @deprecated This interface should be avoided as it depends on the deprecated Apache HTTP library.
 *     Use {@link BaseHttpStack} to avoid this dependency. This class may be removed in a future
 *     release of Volley.
 */
@Deprecated
public interface HttpStack {
    /**
     * Performs an HTTP request with the given parameters.
     *
     * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
     * and the Content-Type header is set to request.getPostBodyContentType().
     *
     * @param request the request to perform
     * @param additionalHeaders additional headers to be sent together with {@link
     *     Request#getHeaders()}
     * @return the HTTP response
     */
    HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;
}

HttpStack一共有兩個(gè)實(shí)現(xiàn),一個(gè)是HttpClientStack忌卤,另一個(gè)是BaseHttpStack扫夜。這樣做的原因是因?yàn)椋沧?.0之后驰徊,廢棄了Apache的HttpClient笤闯,轉(zhuǎn)用HttpURLConnection。所以棍厂,為了兼容創(chuàng)建了BaseHttpStack颗味,在BaseHttpStack中另外定義了一個(gè)抽象方法excuteRequest,它重寫的performRequest方法中牺弹,調(diào)用excuteRequest方法獲取網(wǎng)絡(luò)數(shù)據(jù)浦马。

HttpClientStack

顯然,它實(shí)現(xiàn)了HttpStack接口中定義的最重要的方法:performRequest张漂。
它有一個(gè)內(nèi)部類晶默,我們先來看看這個(gè)內(nèi)部類到底是個(gè)啥。

HttpPatch

先看類注釋:The HttpPatch class does not exist in the Android framework, so this has been defined here.額鹃锈,意思就是這個(gè)類本來是HttpClient已有的荤胁,奈何Android架構(gòu)中不包含,所以屎债,自己有重新來定義了一個(gè)。Apache的HttpClient的注釋是這樣的:PATCH方法請求將請求實(shí)體中描述的一組更改應(yīng)用于Request-URI標(biāo)識的資源垢油。 與服務(wù)器處理封閉實(shí)體以修改Request-URI標(biāo)識的資源的方式不同盆驹。 在PUT請求中,封閉的實(shí)體源服務(wù)器和客戶端請求替換存儲的版本滩愁。但是躯喇,使用PATCH,隨附的實(shí)體包含一組指令,這些指令描述了如何修改當(dāng)前駐留在源服務(wù)器上的資源以生成新版本廉丽。簡單說倦微,這個(gè)類的作用就是當(dāng)請求類型為Patch時(shí),用這個(gè)類來封裝請求正压。

構(gòu)造函數(shù)

這個(gè)方法只一個(gè)構(gòu)造函數(shù)欣福,參數(shù)為HttpClient。后面需要根據(jù)這個(gè)來進(jìn)行網(wǎng)絡(luò)請求焦履。

    protected final HttpClient mClient;

    public HttpClientStack(HttpClient client) {
        mClient = client;
    }

實(shí)現(xiàn)performRequest

這個(gè)方法是網(wǎng)絡(luò)請求的關(guān)鍵拓劝。關(guān)鍵內(nèi)容不多,我們直接看源碼嘉裤。

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        setHeaders(httpRequest, additionalHeaders);
        // Request.getHeaders() takes precedence over the given additional (cache) headers) and any
        // headers set by createHttpRequest (like the Content-Type header).
        setHeaders(httpRequest, request.getHeaders());
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        return mClient.execute(httpRequest);
    }

額郑临,感覺沒啥可講的。不熟悉的可以去看看Apache HttpClient如何使用屑宠,我就不當(dāng)搬用工了厢洞。

BaseHttpStack

這個(gè)是HttpStack的一個(gè)基礎(chǔ)實(shí)現(xiàn)。內(nèi)部的網(wǎng)絡(luò)請求使用的是自己抽象的方法executeRequest典奉。這在設(shè)計(jì)模式中躺翻,應(yīng)該叫做裝飾者模式。代碼不長秋柄,直接來分析获枝。

import com.android.volley.AuthFailureError;
import com.android.volley.Header;
import com.android.volley.Request;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;

/** An HTTP stack abstraction. */
@SuppressWarnings("deprecation") // for HttpStack
public abstract class BaseHttpStack implements HttpStack {

    /**
     * Performs an HTTP request with the given parameters.
     *
     * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
     * and the Content-Type header is set to request.getPostBodyContentType().
     *
     * @param request the request to perform
     * @param additionalHeaders additional headers to be sent together with {@link
     *     Request#getHeaders()}
     * @return the {@link HttpResponse}
     * @throws SocketTimeoutException if the request times out
     * @throws IOException if another I/O error occurs during the request
     * @throws AuthFailureError if an authentication failure occurs during the request
     */
    public abstract HttpResponse executeRequest(
            Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;

    /**
     * @deprecated use {@link #executeRequest} instead to avoid a dependency on the deprecated
     *     Apache HTTP library. Nothing in Volley's own source calls this method. However, since
     *     {@link BasicNetwork#mHttpStack} is exposed to subclasses, we provide this implementation
     *     in case legacy client apps are dependent on that field. This method may be removed in a
     *     future release of Volley.
     */
    @Deprecated
    @Override
    public final org.apache.http.HttpResponse performRequest(
            Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        HttpResponse response = executeRequest(request, additionalHeaders);

        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        StatusLine statusLine =
                new BasicStatusLine(
                        protocolVersion, response.getStatusCode(), /* reasonPhrase= */ "");
        BasicHttpResponse apacheResponse = new BasicHttpResponse(statusLine);

        List<org.apache.http.Header> headers = new ArrayList<>();
        for (Header header : response.getHeaders()) {
            headers.add(new BasicHeader(header.getName(), header.getValue()));
        }
        apacheResponse.setHeaders(headers.toArray(new org.apache.http.Header[headers.size()]));

        InputStream responseStream = response.getContent();
        if (responseStream != null) {
            BasicHttpEntity entity = new BasicHttpEntity();
            entity.setContent(responseStream);
            entity.setContentLength(response.getContentLength());
            apacheResponse.setEntity(entity);
        }

        return apacheResponse;
    }
}

兩個(gè)方法,一個(gè)是HttpStack中定義的performRequest骇笔,返回了org.apache.http.HttpResponse省店,另一個(gè)是抽象的executeRequest,返回了com.android.volley.toolbox.HttpResponse笨触。先簡單講一下

HttpResponse

這個(gè)是Volley自帶的對網(wǎng)絡(luò)請求的一個(gè)封裝懦傍,注意與org.apache.http.HttpResponse是不同的。包括:狀態(tài)碼芦劣、Header列表粗俱、內(nèi)容長度和InputStream。

import com.android.volley.Header;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

/** A response from an HTTP server. */
public final class HttpResponse {

    private final int mStatusCode;
    private final List<Header> mHeaders;
    private final int mContentLength;
    private final InputStream mContent;

    /**
     * Construct a new HttpResponse for an empty response body.
     *
     * @param statusCode the HTTP status code of the response
     * @param headers the response headers
     */
    public HttpResponse(int statusCode, List<Header> headers) {
        this(statusCode, headers, /* contentLength= */ -1, /* content= */ null);
    }

    /**
     * Construct a new HttpResponse.
     *
     * @param statusCode the HTTP status code of the response
     * @param headers the response headers
     * @param contentLength the length of the response content. Ignored if there is no content.
     * @param content an {@link InputStream} of the response content. May be null to indicate that
     *     the response has no content.
     */
    public HttpResponse(
            int statusCode, List<Header> headers, int contentLength, InputStream content) {
        mStatusCode = statusCode;
        mHeaders = headers;
        mContentLength = contentLength;
        mContent = content;
    }

    /** Returns the HTTP status code of the response. */
    public final int getStatusCode() {
        return mStatusCode;
    }

    /** Returns the response headers. Must not be mutated directly. */
    public final List<Header> getHeaders() {
        return Collections.unmodifiableList(mHeaders);
    }

    /** Returns the length of the content. Only valid if {@link #getContent} is non-null. */
    public final int getContentLength() {
        return mContentLength;
    }

    /**
     * Returns an {@link InputStream} of the response content. May be null to indicate that the
     * response has no content.
     */
    public final InputStream getContent() {
        return mContent;
    }
}

代碼比較簡單虚吟,一看即懂寸认,不詳細(xì)講了。

  • executeRequest
    Performs an HTTP request with the given parameters.簡單說就是完成Http請求的方法串慰。由子類實(shí)現(xiàn)偏塞。
    /**
       * Performs an HTTP request with the given parameters.
       *
       * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
       * and the Content-Type header is set to request.getPostBodyContentType().
       *
       * @param request the request to perform
       * @param additionalHeaders additional headers to be sent together with {@link
       *     Request#getHeaders()}
       * @return the {@link HttpResponse}
       * @throws SocketTimeoutException if the request times out
       * @throws IOException if another I/O error occurs during the request
       * @throws AuthFailureError if an authentication failure occurs during the request
       */
      public abstract HttpResponse executeRequest(
              Request<?> request, Map<String, String> additionalHeaders)
              throws IOException, AuthFailureError;
    
    注意返回的HttpResponse是com.android.volley.toolbox.HttpResponse。
  • performRequest
    /**
       * @deprecated use {@link #executeRequest} instead to avoid a dependency on the deprecated
       *     Apache HTTP library. Nothing in Volley's own source calls this method. However, since
       *     {@link BasicNetwork#mHttpStack} is exposed to subclasses, we provide this implementation
       *     in case legacy client apps are dependent on that field. This method may be removed in a
       *     future release of Volley.
       */
      @Deprecated
      @Override
      public final org.apache.http.HttpResponse performRequest(
              Request<?> request, Map<String, String> additionalHeaders)
              throws IOException, AuthFailureError {
          HttpResponse response = executeRequest(request, additionalHeaders);
    
          ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
          StatusLine statusLine =
                  new BasicStatusLine(
                          protocolVersion, response.getStatusCode(), /* reasonPhrase= */ "");
          BasicHttpResponse apacheResponse = new BasicHttpResponse(statusLine);
    
          List<org.apache.http.Header> headers = new ArrayList<>();
          for (Header header : response.getHeaders()) {
              headers.add(new BasicHeader(header.getName(), header.getValue()));
          }
          apacheResponse.setHeaders(headers.toArray(new org.apache.http.Header[headers.size()]));
    
          InputStream responseStream = response.getContent();
          if (responseStream != null) {
              BasicHttpEntity entity = new BasicHttpEntity();
              entity.setContent(responseStream);
              entity.setContentLength(response.getContentLength());
              apacheResponse.setEntity(entity);
          }
    
          return apacheResponse;
      }
    
    在這個(gè)方法中實(shí)際上是調(diào)用了executeRequest()方法邦鲫,然后將得到的com.android.volley.toolbox.HttpResponse轉(zhuǎn)換成org.apache.http.HttpResponse灸叼。額神汹,簡單,粗暴古今,有效屁魏。

HurlStack

Hurl是HttpUrlConnection的縮寫。顧名思義捉腥,它是使用了HttpURLConnection實(shí)現(xiàn)網(wǎng)絡(luò)請求的氓拼。它是BaseHttpStack的子類。上面講過但狭,BaseHttpStack有一個(gè)抽象方法-executeRequest披诗。我們的HurlStack實(shí)現(xiàn)了這個(gè)方法。
我們先不急著看這個(gè)方法的代碼立磁,按照套路呈队,看完繼承之后,再看這個(gè)類是否有什么內(nèi)部類唱歧。果然宪摧,它有兩個(gè)內(nèi)部類。一個(gè)是:

  • UrlRewriter接口
  • UrlConnectionInputStream類

UrlRewriter

它是一個(gè)接口颅崩。

    /** An interface for transforming URLs before use. */
    public interface UrlRewriter {
        /**
         * Returns a URL to use instead of the provided one, or null to indicate this URL should not
         * be used at all.
         */
        String rewriteUrl(String originalUrl);
    }

代碼注釋說几于,它是用于在使用前轉(zhuǎn)換URL的一個(gè)接口。有一個(gè)方法 rewriteUrl沿后,入?yún)riginalUrl沿彭,出參是轉(zhuǎn)換后的url。返回一個(gè)URL來使用而不是使用提供的URL尖滚,或返回null以指示不應(yīng)使用此URL喉刘。額,感覺不是很清楚啥意思漆弄。我們接著看代碼吧睦裳,說不定后面更清楚一些。

UrlConnectionInputStream

直接看代碼撼唾。

    /**
     * Wrapper for a {@link HttpURLConnection}'s InputStream which disconnects the connection on
     * stream close.
     */
    static class UrlConnectionInputStream extends FilterInputStream {
        private final HttpURLConnection mConnection;

        UrlConnectionInputStream(HttpURLConnection connection) {
            super(inputStreamFromConnection(connection));
            mConnection = connection;
        }

        @Override
        public void close() throws IOException {
            super.close();
            mConnection.disconnect();
        }
    }
    
    /**
     * Initializes an {@link InputStream} from the given {@link HttpURLConnection}.
     *
     * @param connection
     * @return an HttpEntity populated with data from <code>connection</code>.
     */
    private static InputStream inputStreamFromConnection(HttpURLConnection connection) {
        InputStream inputStream;
        try {
            inputStream = connection.getInputStream();
        } catch (IOException ioe) {
            inputStream = connection.getErrorStream();
        }
        return inputStream;
    }

從注釋我們知道廉邑,這個(gè)類的作用是:它是HttpURLConnection的InputStream的裝飾類,用于關(guān)閉連接倒谷。

構(gòu)造方法

現(xiàn)在我們看看構(gòu)造方法

    private final UrlRewriter mUrlRewriter;
    private final SSLSocketFactory mSslSocketFactory;

    public HurlStack() {
        this(/* urlRewriter = */ null);
    }

    /** @param urlRewriter Rewriter to use for request URLs */
    public HurlStack(UrlRewriter urlRewriter) {
        this(urlRewriter, /* sslSocketFactory = */ null);
    }

    /**
     * @param urlRewriter Rewriter to use for request URLs
     * @param sslSocketFactory SSL factory to use for HTTPS connections
     */
    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
        mUrlRewriter = urlRewriter;
        mSslSocketFactory = sslSocketFactory;
    }

它有三個(gè)構(gòu)造方法蛛蒙。用于設(shè)置UrlRewriter和SSLSocketFactory。UrlRewriter的作用是渤愁,對傳入的url進(jìn)行加工宇驾。SSLSocketFactory的作用是,當(dāng)請求協(xié)議是Https猴伶,可以使用這樣一個(gè)自定義的SSLSocketFactory。

executeRequest()方法

這個(gè)是重寫B(tài)aseHttpStack的抽象方法。

    @Override
    public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap<String, String> map = new HashMap<>();
        map.putAll(additionalHeaders);
        // Request.getHeaders() takes precedence over the given additional (cache) headers).
        map.putAll(request.getHeaders());
        if (mUrlRewriter != null) {
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
        HttpURLConnection connection = openConnection(parsedUrl, request);
        boolean keepConnectionOpen = false;
        try {
            for (String headerName : map.keySet()) {
                connection.setRequestProperty(headerName, map.get(headerName));
            }
            setConnectionParametersForRequest(connection, request);
            // Initialize HttpResponse with data from the HttpURLConnection.
            int responseCode = connection.getResponseCode();
            if (responseCode == -1) {
                // -1 is returned by getResponseCode() if the response code could not be retrieved.
                // Signal to the caller that something was wrong with the connection.
                throw new IOException("Could not retrieve response code from HttpUrlConnection.");
            }

            if (!hasResponseBody(request.getMethod(), responseCode)) {
                return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
            }

            // Need to keep the connection open until the stream is consumed by the caller. Wrap the
            // stream such that close() will disconnect the connection.
            keepConnectionOpen = true;
            return new HttpResponse(
                    responseCode,
                    convertHeaders(connection.getHeaderFields()),
                    connection.getContentLength(),
                    new UrlConnectionInputStream(connection));
        } finally {
            if (!keepConnectionOpen) {
                connection.disconnect();
            }
        }
    }

過程簡單他挎,首先判斷是否設(shè)置了個(gè)性化UrlRewriter筝尾,如果設(shè)置了,可以在這里調(diào)用自定義方法办桨,對Url進(jìn)行修飾筹淫。這個(gè)有啥作用呢?可能可以用來做Mocker吧呢撞。再創(chuàng)建HttpURLConnection损姜,調(diào)用了openConnection()方法。在這個(gè)方法中殊霞,我們判斷了Url的協(xié)議是不是https摧阅,如果是,就調(diào)用我們在構(gòu)造方法中設(shè)置的SSLSocketFactory绷蹲。然后調(diào)用connection.setRequestProperty()方法設(shè)置請求參數(shù)棒卷。然后就是更具Request來設(shè)置請求類型(POST、GET等)祝钢。
調(diào)用connection.getResponseCode()觸發(fā)請求比规,得到響應(yīng)碼。然后判斷響應(yīng)怕是否異常拦英,如果是蜒什,就拋出IO異常。接著調(diào)用hasResponseBody()方法疤估,來判斷是否得到了body灾常。根據(jù)請求結(jié)果創(chuàng)建了一個(gè)com.android.volley.toolbox.HttpResponse,并將它返回做裙。執(zhí)行結(jié)束岗憋。至此,這個(gè)類的分析完畢了锚贱。

AdaptedHttpStack

這個(gè)的作用的是為了兼容性仔戈,添加的一個(gè)接口。

import com.android.volley.AuthFailureError;
import com.android.volley.Header;
import com.android.volley.Request;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.conn.ConnectTimeoutException;

/**
 * {@link BaseHttpStack} implementation wrapping a {@link HttpStack}.
 *
 * <p>{@link BasicNetwork} uses this if it is provided a {@link HttpStack} at construction time,
 * allowing it to have one implementation based atop {@link BaseHttpStack}.
 */
@SuppressWarnings("deprecation")
class AdaptedHttpStack extends BaseHttpStack {

    private final HttpStack mHttpStack;

    AdaptedHttpStack(HttpStack httpStack) {
        mHttpStack = httpStack;
    }

    @Override
    public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        org.apache.http.HttpResponse apacheResp;
        try {
            apacheResp = mHttpStack.performRequest(request, additionalHeaders);
        } catch (ConnectTimeoutException e) {
            // BasicNetwork won't know that this exception should be retried like a timeout, since
            // it's an Apache-specific error, so wrap it in a standard timeout exception.
            throw new SocketTimeoutException(e.getMessage());
        }

        int statusCode = apacheResp.getStatusLine().getStatusCode();

        org.apache.http.Header[] headers = apacheResp.getAllHeaders();
        List<Header> headerList = new ArrayList<>(headers.length);
        for (org.apache.http.Header header : headers) {
            headerList.add(new Header(header.getName(), header.getValue()));
        }

        if (apacheResp.getEntity() == null) {
            return new HttpResponse(statusCode, headerList);
        }

        long contentLength = apacheResp.getEntity().getContentLength();
        if ((int) contentLength != contentLength) {
            throw new IOException("Response too large: " + contentLength);
        }

        return new HttpResponse(
                statusCode,
                headerList,
                (int) apacheResp.getEntity().getContentLength(),
                apacheResp.getEntity().getContent());
    }
}

MockHttpStack

這個(gè)是用來測試的拧廊。代碼比較簡單监徘,不分析了。

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.toolbox.BaseHttpStack;
import com.android.volley.toolbox.HttpResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MockHttpStack extends BaseHttpStack {

    private HttpResponse mResponseToReturn;

    private IOException mExceptionToThrow;

    private String mLastUrl;

    private Map<String, String> mLastHeaders;

    private byte[] mLastPostBody;

    public String getLastUrl() {
        return mLastUrl;
    }

    public Map<String, String> getLastHeaders() {
        return mLastHeaders;
    }

    public byte[] getLastPostBody() {
        return mLastPostBody;
    }

    public void setResponseToReturn(HttpResponse response) {
        mResponseToReturn = response;
    }

    public void setExceptionToThrow(IOException exception) {
        mExceptionToThrow = exception;
    }

    @Override
    public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        if (mExceptionToThrow != null) {
            throw mExceptionToThrow;
        }
        mLastUrl = request.getUrl();
        mLastHeaders = new HashMap<>();
        if (request.getHeaders() != null) {
            mLastHeaders.putAll(request.getHeaders());
        }
        if (additionalHeaders != null) {
            mLastHeaders.putAll(additionalHeaders);
        }
        try {
            mLastPostBody = request.getBody();
        } catch (AuthFailureError e) {
            mLastPostBody = null;
        }
        return mResponseToReturn;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吧碾,一起剝皮案震驚了整個(gè)濱河市凰盔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌倦春,老刑警劉巖户敬,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件落剪,死亡現(xiàn)場離奇詭異,居然都是意外死亡尿庐,警方通過查閱死者的電腦和手機(jī)忠怖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抄瑟,“玉大人凡泣,你說我怎么就攤上這事∑ぜ伲” “怎么了鞋拟?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惹资。 經(jīng)常有香客問我贺纲,道長,這世上最難降的妖魔是什么布轿? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任哮笆,我火速辦了婚禮,結(jié)果婚禮上汰扭,老公的妹妹穿的比我還像新娘稠肘。我一直安慰自己,他們只是感情好萝毛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布项阴。 她就那樣靜靜地躺著,像睡著了一般笆包。 火紅的嫁衣襯著肌膚如雪环揽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天庵佣,我揣著相機(jī)與錄音歉胶,去河邊找鬼。 笑死巴粪,一個(gè)胖子當(dāng)著我的面吹牛通今,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肛根,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼辫塌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了派哲?” 一聲冷哼從身側(cè)響起臼氨,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芭届,沒想到半個(gè)月后储矩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體感耙,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年椰苟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抑月。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舆蝴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出题诵,到底是詐尸還是另有隱情洁仗,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布性锭,位于F島的核電站赠潦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏草冈。R本人自食惡果不足惜她奥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怎棱。 院中可真熱鬧哩俭,春花似錦、人聲如沸拳恋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谬运。三九已至隙赁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梆暖,已是汗流浹背伞访。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轰驳,地道東北人厚掷。 一個(gè)月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像滑废,于是被迫代替她去往敵國和親蝗肪。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. 功能介紹 1.1. Volley Volley 是 Google 推出的 Android 異步網(wǎng)絡(luò)請求框架和...
    愛碼士平頭哥閱讀 1,825評論 0 9
  • 注:本文轉(zhuǎn)自http://codekk.com/open-source-project-analysis/deta...
    Ten_Minutes閱讀 1,296評論 1 16
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蠕趁,服務(wù)發(fā)現(xiàn)薛闪,斷路器,智...
    卡卡羅2017閱讀 134,664評論 18 139
  • 我們再來看看volley是怎么工作的俺陋。首先還是要帶著重點(diǎn)去看源碼豁延,我們要關(guān)注的地方除了最核心的工作流程之外昙篙,還有一...
  • 現(xiàn)在多的是柏油馬路,飛馳的汽車诱咏,匆匆忙忙的行人苔可。我懷戀的還是塵土飛揚(yáng)的小路,嘎吱嘎吱響的自行車袋狞,那是一條怎樣崎嶇的...
    柯KAI閱讀 338評論 0 0