OkHttp 的進(jìn)化 -- Retrofit

代碼示例從github上獲取(不是自己寫的 ):https://github.com/jdsjlzx/RetrofitDemo

先看OKHttp的使用,最簡(jiǎn)單的:

//1.創(chuàng)建OkHttpClient對(duì)象
OkHttpClient okHttpClient = new OkHttpClient();
//2.創(chuàng)建Request對(duì)象,設(shè)置一個(gè)url地址(百度地址),設(shè)置請(qǐng)求方式秕豫。
Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build();
//3.創(chuàng)建一個(gè)call對(duì)象,參數(shù)就是Request請(qǐng)求對(duì)象
Call call = okHttpClient.newCall(request);
//4.請(qǐng)求加入調(diào)度烙肺,重寫回調(diào)方法
    call.enqueue(new Callback() {
  //請(qǐng)求失敗執(zhí)行的方法
  @Override
  public void onFailure(Call call, IOException e) {
  }
  //請(qǐng)求成功執(zhí)行的方法
  @Override
  public void onResponse(Call call, Response response) throws IOException {
  }
});

主要涉及3個(gè)類:

  • OkHttpClient
  • RealCall
  • Request
  public OkHttpClient() {
    this(new Builder());
  }
 ... ...
public static final class Builder {
    Dispatcher dispatcher;
    Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    ProxySelector proxySelector;
    CookieJar cookieJar;
    Cache cache;
    InternalCache internalCache;
    SocketFactory socketFactory;
    SSLSocketFactory sslSocketFactory;
    TrustRootIndex trustRootIndex;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;

 public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }

通過這些代碼我們可以知道OkHttpClient中包含了在HTTP網(wǎng)絡(luò)訪問的時(shí)候需要的一些參數(shù)值,為了使用方便陋葡,有些參數(shù)值由默認(rèn)值設(shè)置:

 socketFactory = SocketFactory.getDefault();

   public static SocketFactory getDefault()
    {
        synchronized (SocketFactory.class) {
            if (theFactory == null) {
                //
                // Different implementations of this method SHOULD
                // work rather differently.  For example, driving
                // this from a system property, or using a different
                // implementation than JavaSoft's.
                //
                theFactory = new DefaultSocketFactory();
            }
        }

        return theFactory;
    }
   dns = Dns.SYSTEM;
   public interface Dns {
  /**
   * A DNS that uses {@link InetAddress#getAllByName} to ask the underlying operating system to
   * lookup IP addresses. Most custom {@link Dns} implementations should delegate to this instance.
   */
  Dns SYSTEM = new Dns() {
    @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException {
      if (hostname == null) throw new UnknownHostException("hostname == null");
      return Arrays.asList(InetAddress.getAllByName(hostname));
    }
  };
...  ...

這段代碼中我們可以看出,Request 類最主要的就是包含HTTP請(qǐng)求所需要的參數(shù)彻采,主要參數(shù)為:
private final HttpUrl url;
private final String method;
private final Headers headers;
private final RequestBody body;
private final Object tag;

為了使用的方便腐缤,Request 的提供了一個(gè)Request .Builder 構(gòu)造類。

public final class Request {
  private final HttpUrl url;
  private final String method;
  private final Headers headers;
  private final RequestBody body;
  private final Object tag;

  ...  ...  ...

  private Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }
  
 
 public static class Builder {
    private HttpUrl url;
    private String method;
    private Headers.Builder headers;
    private RequestBody body;
    private Object tag;

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    private Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }
    
    ... 
    
      public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
    ... ... ... 
   } 
}

在創(chuàng)建好 OkHttpClient 和 Request之后肛响,RealCall開始登場(chǎng)了

final class RealCall implements Call {
  private final OkHttpClient client;
... ... ...
  /** The application's original request unadulterated by redirects or auth headers. */
  Request originalRequest;
  HttpEngine engine;

  protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
  }

緊接著就是執(zhí)行HTTP請(qǐng)求了岭粤,這里提供了兩種方式,立即執(zhí)行 和 加入執(zhí)行隊(duì)列

@Override public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  try {
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain(false);
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}
  
void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}

這里我們可以看到一個(gè)關(guān)于管理調(diào)度的標(biāo)志性代碼:client.dispatcher().XXX
我們看看這個(gè)是如何做到的 --- ExecutorService 線程池

public final class Dispatcher {
... ...
 public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
... ...

至于剩下的AsyncCall肯定是 Runnable 特笋。這個(gè)就不貼代碼了

總結(jié)下:OkHttp 的邏輯很簡(jiǎn)單剃浇,

  • OkHttpClient 中包含了網(wǎng)絡(luò)請(qǐng)求中所需要的所有變量參數(shù),例如:DNS,proxy虎囚,connectTimeout 等
  • Request 中包含了 請(qǐng)求是需要的 URL角塑,請(qǐng)求方式(get/Post 等), Header, body
  • OkHttpClient 與 Request 作為 RealCall 的成員變量淘讥,同時(shí)RealCall 作為一個(gè)可執(zhí)行的TASK圃伶,將自身交給調(diào)度器去調(diào)度
  • 調(diào)度器就是 ThreadPoolExecutor 和 SynchronousQueue 組成

如果還有不明白的,可以依據(jù)這4條再回過頭看看前面的代碼

到此為止蒲列,可以開始正題了 :

Retrofit 到底干什么的窒朋?-- 其實(shí)就是OkHttp的一個(gè)封裝庫(kù)
Retrofit 為什么要封裝?
Retrofit 的框架什么樣的蝗岖?
為什要設(shè)計(jì)成這樣炼邀?
我們?cè)诰幋a中有什么可以借鑒的嗎?

Retrofit 使用的最大的兩個(gè)技術(shù)就是: 動(dòng)態(tài)代理 和 注解

動(dòng)態(tài)代理:java.lang.reflect.Proxy:這是 Java 動(dòng)態(tài)代理機(jī)制的主類剪侮,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對(duì)象拭宁。

先看看動(dòng)態(tài)代理的相關(guān)代碼

public interface PostRequest_Interface {
    @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")
    @FormUrlEncoded
     Call<Translation1> getCall(@Field("i") String targetSentence);
}

這里就有了interface getCall

public final class Retrofit {

... ...

public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    eagerlyValidateMethods(service);
  }
   return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, Object... args)
            throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
           if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          Log.v("jar-filter", "method : " + method);
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          Log.v("jar-filter", "args : " + args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

上面這段代碼就是動(dòng)態(tài)代理了,代理了接口 getCall ( @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")這個(gè)也算在里面)

我們?cè)倩仡^仔細(xì)分析下代碼

Retrofit.java

 public <T> T create(final Class<T> service) {
 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
             ... ... ...
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
     }
  • ServiceMethod serviceMethod = loadServiceMethod(method);
    創(chuàng)建了ServiceMethod 的對(duì)象實(shí)例

  • OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
    創(chuàng)建了OkHttpCall 的對(duì)象實(shí)例

  • return serviceMethod.callAdapter.adapt(okHttpCall);
    返回一個(gè)ServiceMethod 的對(duì)象實(shí)例中的callAdapter.adapt

PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);

//對(duì) 發(fā)送請(qǐng)求 進(jìn)行封裝(設(shè)置需要翻譯的內(nèi)容)
Call<Translation1> call = request.getCall("I love you");
//步驟6:發(fā)送網(wǎng)絡(luò)請(qǐng)求(異步)
call.enqueue(new Callback<Translation1>() {

Proxy.newProxyInstance 函數(shù)瓣俯,是在request.getCall的時(shí)候觸發(fā)杰标。
也就是說 return serviceMethod.callAdapter.adapt(okHttpCall);返回的就是Call<Translation1> call。

我拿到call后就直接enqueue彩匕。這個(gè)就說明call具有網(wǎng)絡(luò)HTTP的功能腔剂。

loadServiceMethod

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
     //ServiceMethod 有一個(gè)Builder,實(shí)例的創(chuàng)建是通過創(chuàng)建者模式
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

從ServiceMethod的成員變量驼仪,可以看出ServiceMethod

  • 提供了了call的工廠 ---- okhttp3.Call.Factory callFactory;
  • 請(qǐng)求URL ---- HttpUrl baseUrl;
  • 請(qǐng)求頭信息 --- Headers headers;
final class ServiceMethod<T> {
  // Upper and lower characters, digits, underscores, and hyphens, starting with a character.
  static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
  static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
  static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);

  final okhttp3.Call.Factory callFactory;
  final CallAdapter<?> callAdapter;

  private final HttpUrl baseUrl;
  private final Converter<ResponseBody, T> responseConverter;
  private final String httpMethod;
  private final String relativeUrl;
  private final Headers headers;
  private final MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

//從代碼名稱我們知道這個(gè)是注解的解釋
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      
      ... ...
      
      
     //這里注解 POST GET 等注解
    private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } else if (annotation instanceof PATCH) {
         parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
        
        
        ... ...
        
      //可以將 注解轉(zhuǎn)化為 HTTP請(qǐng)求時(shí)候需要的參數(shù)  httpMethod  hasBodyBuilder 等
     private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }

      this.httpMethod = httpMethod;
      this.hasBodyBuilder = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
       if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }
     ... ...  

從上述代碼我們大致的可以猜測(cè)掸犬,ServiceMethod 是為HTTP強(qiáng)求前期準(zhǔn)備資源的一個(gè)類。既包括 HTTP 資源參數(shù)绪爸,也包括 callFactory湾碎。
但是到目前為止,我們還沒有與OkHttp 徹底的關(guān)聯(lián)上奠货。

new OkHttpCall<>(serviceMethod, args)

final class OkHttpCall<T> implements Call<T> {
  private final ServiceMethod<T> serviceMethod;
  private final Object[] args;

  private volatile boolean canceled;

    //注意這個(gè)介褥,rawCall應(yīng)該最終可以調(diào)用OkHttp 的 RealCall
  private okhttp3.Call rawCall;
  private Throwable creationFailure; // Either a RuntimeException or IOException.
  private boolean executed;

  /構(gòu)造函數(shù)
  OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }
     

serviceMethod.callAdapter.adapt(okHttpCall)

首先要找到serviceMethod.callAdapter是什么
通過下面這一堆代碼,我們可以知道 callAdapter 的 類就是 ExecutorCallAdapterFactory.ExecutorCallbackCall

// ServiceMethod.Builder 
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      
// ServiceMethod.Builder      
private CallAdapter<?> createCallAdapter() {
    ... ...
    try {
      //繼續(xù)跟蹤下去
      return retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(e, "Unable to create call adapter for %s", returnType);
    }
}



//retrofit.class
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
  return nextCallAdapter(null, returnType, annotations);
}
  
//retrofit.class  
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
    Annotation[] annotations) {
  checkNotNull(returnType, "returnType == null");
  checkNotNull(annotations, "annotations == null");

  int start = adapterFactories.indexOf(skipPast) + 1;
  for (int i = start, count = adapterFactories.size(); i < count; i++) {
    //從一個(gè)數(shù)組或者列表中獲取的递惋, CallAdapter 可以有多個(gè)
    CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }
  
  
  public Retrofit build() {
     
  ... ...
  // 在Retrofit創(chuàng)建的時(shí)候會(huì)給一個(gè)默認(rèn)的 platform.defaultCallAdapterFactory(callbackExecutor)
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}

//就是這個(gè)
 CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;
  
  ... ...
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;
  
  ... ...
  
   @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

            //返回的是ExecutorCallAdapterFactory.ExecutorCallbackCall
      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  ... ...

最后一步

call.enqueue(new Callback<Translation1>()
也就是
ExecutorCallAdapterFactory.ExecutorCallbackCall.enqueue(new Callback<Translation1>()

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
  }

  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    delegate.enqueue(new Callback<T>() {
    
    ... ...
      


  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
        //
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
 
   //這里就是真的 realcall.enqueue
    call.enqueue(new okhttp3.Callback() {      
      
      
private okhttp3.Call createRawCall() throws IOException {
//創(chuàng)建出 OkHttp  的request 
  Request request = serviceMethod.toRequest(args);
 //請(qǐng)參考  okhttp3.Call.Factory callFactory
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}


 okhttp3.Call.Factory callFactory = this.callFactoryBuilder;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

總結(jié)

  • Retrofit 到底干什么的柔滔?
    其實(shí)就是OkHttp的一個(gè)封裝庫(kù),更準(zhǔn)確說是“HTTP控件庫(kù)們”的封裝庫(kù)萍虽,但是還沒有和OKHTTP 完全脫藕
  • Retrofit 為什么要封裝睛廊?
    因?yàn)槭荋TTP控件們的封裝庫(kù),可以支持多個(gè)HTTP庫(kù)杉编,都有封裝脫藕
  • Retrofit 的框架什么樣的超全?為什要設(shè)計(jì)成這樣咆霜?
    注解 @GET @POST 等,來代替請(qǐng)求模式和參數(shù)
    ServiceMethod 是服務(wù)類卵迂,OkHttpCall 就是 OKHTTP庫(kù)的實(shí)現(xiàn)類
  • 我們?cè)诰幋a中有什么可以借鑒的嗎?
    其實(shí)注解功能可以更好的脫藕绒净,但是代價(jià)比較大见咒,解析代碼太費(fèi)勁了、
    在只有Okhttp一個(gè)庫(kù)的情況下挂疆,沒有很好的實(shí)際意義改览,如果同時(shí)指出 volley 等多個(gè)HTTP庫(kù)的時(shí)候,就比較有意義了缤言。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宝当,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胆萧,更是在濱河造成了極大的恐慌庆揩,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跌穗,死亡現(xiàn)場(chǎng)離奇詭異订晌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蚌吸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門锈拨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吕座,你說我怎么就攤上這事盹舞⌒沤危” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵缝彬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我哺眯,道長(zhǎng)跌造,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任族购,我火速辦了婚禮壳贪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寝杖。我一直安慰自己违施,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布瑟幕。 她就那樣靜靜地躺著磕蒲,像睡著了一般留潦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辣往,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天兔院,我揣著相機(jī)與錄音,去河邊找鬼站削。 笑死坊萝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的许起。 我是一名探鬼主播十偶,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼园细!你這毒婦竟也來了惦积?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤猛频,失蹤者是張志新(化名)和其女友劉穎狮崩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹿寻,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厉亏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烈和。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爱只。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖招刹,靈堂內(nèi)的尸體忽然破棺而出恬试,到底是詐尸還是另有隱情,我是刑警寧澤疯暑,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布训柴,位于F島的核電站,受9級(jí)特大地震影響妇拯,放射性物質(zhì)發(fā)生泄漏幻馁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一越锈、第九天 我趴在偏房一處隱蔽的房頂上張望仗嗦。 院中可真熱鬧,春花似錦甘凭、人聲如沸稀拐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)德撬。三九已至铲咨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜓洪,已是汗流浹背纤勒。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隆檀,地道東北人摇天。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像刚操,于是被迫代替她去往敵國(guó)和親闸翅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子再芋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,322評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理菊霜,服務(wù)發(fā)現(xiàn),斷路器济赎,智...
    卡卡羅2017閱讀 134,715評(píng)論 18 139
  • 參考Android網(wǎng)絡(luò)請(qǐng)求心路歷程Android Http接地氣網(wǎng)絡(luò)請(qǐng)求(HttpURLConnection) 一...
    合肥黑閱讀 21,292評(píng)論 7 63
  • 你是否發(fā)現(xiàn)過鉴逞,生活中總有那么一個(gè)隨身帶著耳機(jī),所有人都在開心的交談吹牛逼時(shí)司训,總有那么一個(gè)人构捡。他默默的躲在角落里,獨(dú)...
    一只小小航閱讀 369評(píng)論 0 0
  • 跑步鍛煉暫時(shí)停了壳猜,可能鍛煉過度腿受傷勾徽,練字,練練吉他自己摸索了玩的统扳。
    下雨了收衣服閱讀 92評(píng)論 1 1