Retrofit 源碼分析

我在半年前才開(kāi)始接觸 Retrofit艘蹋,在那個(gè)時(shí)候這個(gè)框架已經(jīng)很火了。作為入門(mén)學(xué)習(xí)票灰,簡(jiǎn)單梳理了一下寫(xiě)成一篇文章女阀。有不對(duì)的地方,望指正屑迂。
好浸策,開(kāi)始進(jìn)攻。
Retrofit 的創(chuàng)建
使用 Retrofit 的時(shí)候屈糊,首先通過(guò)建造者模式來(lái)創(chuàng)建 Retrofit 對(duì)象的榛,創(chuàng)建姿勢(shì)是這樣的:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .build();

先看到 Builder 的構(gòu)造方法,

public Builder() {
  this(Platform.get());
}
private static final Platform PLATFORM = findPlatform();

static Platform get() {
  return PLATFORM;
}

獲取運(yùn)行的平臺(tái)逻锐,至于怎么獲确蛏巍雕薪?通過(guò) Class.forName() 的方式來(lái)查找。如下:

private static Platform findPlatform() {
  try {
    Class.forName("android.os.Build");
    if (Build.VERSION.SDK_INT != 0) {
      return new Android();
    }
  } catch (ClassNotFoundException ignored) {
  }
  try {
    Class.forName("java.util.Optional");
    return new Java8();
  } catch (ClassNotFoundException ignored) {
  }
  try {
    Class.forName("org.robovm.apple.foundation.NSObject");
    return new IOS();
  } catch (ClassNotFoundException ignored) {
  }
  return new Platform();
}

有三種晓淀,Android所袁,Java8,iOS凶掰。至此燥爷,我們知道了 Retrofit 有一個(gè) Platform 對(duì)象叫 android
繼續(xù)看到 build 方法懦窘。

public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

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

  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  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);
}

可以看到 baseUrl 是一定要設(shè)置的前翎。不然拋出異常就 GG 了。因?yàn)槭堑谝淮握{(diào)用 build 方法來(lái)創(chuàng)建 Retrofit畅涂,很多參數(shù)都是還沒(méi)有配置的港华。
比如:

if (callbackExecutor == null) {
  callbackExecutor = platform.defaultCallbackExecutor();
}

剛才已經(jīng)知道了 Retrofit 有一個(gè) Platform 對(duì)象叫 Android。所以這時(shí)我們跟進(jìn)到 Android 的defaultCallbackExecutor方法午衰。如下:

static class Android extends Platform {
  @Override public Executor defaultCallbackExecutor() {
    return new MainThreadExecutor();
  }

  @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }

  static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      handler.post(r);
    }
  }
}

返回了一個(gè) MainThreadExecutor立宜,好了,從這里我們可以得出結(jié)論臊岸,那個(gè) callbackExecutor 就是 MainThreadExecutor橙数,當(dāng) callbackExecutor 調(diào)用 execute 方法時(shí),就切換到了主線程帅戒。
繼續(xù)前進(jìn)灯帮,來(lái)到

adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

這是一行很神奇的代碼,首先蜘澜,還是調(diào)用 Platform 的方法施流,將剛才得到的 callbackExecutor,也就是 MainThreadExecutor鄙信,又傳進(jìn)了自己的defaultCallAdapterFactory 方法瞪醋,這個(gè) defaultCallAdapterFactory 內(nèi)部生成了一個(gè) ExecutorCallAdapterFactory。
好了装诡,上面那一行神奇的代碼银受,其實(shí)就是將一個(gè) ExecutorCallAdapterFactory 添加進(jìn)一個(gè)集合。 我們?cè)趧?chuàng)建 Retrofit 的時(shí)候鸦采,調(diào)用了 addCallAdapterFactory 配置了RxJavaCallAdapterFactory宾巍。這個(gè) CallAdapter.Factory 也添加進(jìn)了剛才的集合

public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
  adapterFactories.add(checkNotNull(factory, "factory == null"));
  return this;
}

ExecutorCallAdapterFactory 和 RxJavaCallAdapterFactory 都是繼承自 CallAdapter.Factory 的。我們猜渔伯,這兩個(gè) CallAdapter.Factory 是干什么用的顶霞?是用來(lái)將結(jié)果適配的。
接著繼續(xù),同理选浑,

List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

這是將配置的 GsonConverterFactory 添加進(jìn)來(lái)一個(gè)集合蓝厌。
到這里,創(chuàng)建Retrofit對(duì)象完畢古徒⊥靥幔總結(jié)一下,其實(shí)就是配置參數(shù)嘛隧膘。
創(chuàng)建接口對(duì)象
創(chuàng)建了 Retrofit 對(duì)象代态,調(diào)用 create 方法來(lái)創(chuàng)建接口對(duì)象,比如:

IUserBiz userBiz = retrofit.create(IUserBiz.class);

使用了動(dòng)態(tài)代理來(lái)創(chuàng)建接口對(duì)象疹吃,跟進(jìn) create 揭開(kāi)謎底蹦疑。

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);
          }
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

通過(guò) create 方法拿到了接口對(duì)象后,接口對(duì)象肯定需要調(diào)用接口方法吧萨驶,
調(diào)用接口方法

Call<List<User>> call = userBiz.getUsers();

當(dāng)調(diào)用接口方法的時(shí)候必尼,就會(huì)調(diào)用到 InvocationHandler 的 invoke 方法,執(zhí)行其內(nèi)部的邏輯篡撵。
我們看到,調(diào)用了接口方法返回的了一個(gè) Call 對(duì)象豆挽,如果設(shè)置了RxJavaCallAdapterFactory育谬,返回的就是一個(gè) Observable 對(duì)象。
那么結(jié)論我們已經(jīng)知道了帮哈,invoke 方法內(nèi)部的邏輯就變的很神秘膛檀,他到底是怎么返回一個(gè) Call 對(duì)象或 Observable 對(duì)象的呢?
看到 loadServiceMethod 方法娘侍,這里傳進(jìn)了一個(gè) Method 對(duì)象咖刃,即我們調(diào)用的接口方法。(方法也是對(duì)象憾筏,是 Method 類(lèi)的對(duì)象)

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

從 Cache 這個(gè)字眼可以猜測(cè)是先從緩存中查找嚎杨,一個(gè) method 對(duì)象對(duì)應(yīng)一個(gè) ServiceMethod 對(duì)象。 如果緩存不存在氧腰,使用建造者模式來(lái)創(chuàng)建 ServiceMethod 對(duì)象枫浙,之后在添加進(jìn)緩存。好古拴,看下怎么創(chuàng)建 ServiceMethod 對(duì)象箩帚。同樣先是看 Builder 的構(gòu)造方法,

public Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

這里做了一些初始化工作黄痪,取出了接口方法的注解、參數(shù)類(lèi)型减牺、參數(shù)的注解愈案。
好的叠纷,回到build方法。

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();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }
  }

  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if (isFormEncoded && !gotField) {
    throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if (isMultipart && !gotPart) {
    throw methodError("Multipart method must contain at least one @Part.");
  }

  return new ServiceMethod<>(this);
}

首先 createCallAdapter() 獲取的是創(chuàng)建 Retrofit 時(shí)設(shè)置的 CallAdapterFactory崇众,一開(kāi)始我們?cè)O(shè)置了 RxJavaCallAdapterFactory。同理 createResponseConverter() 方法獲取的是 GsonConverterFactory航厚。
接著,就是從接口方法注解數(shù)組中取出注解幔睬,并調(diào)用 parseMethodAnnotation 解析眯漩。如果沒(méi)有設(shè)置請(qǐng)求方法的注解等,比如@GET麻顶、@POST赦抖,會(huì)拋出異常的。最后是解析方法參數(shù)的注解辅肾。創(chuàng)建了 ServiceMethod队萤。

ServiceMethod(Builder<T> builder) {
  this.callFactory = builder.retrofit.callFactory();
  this.callAdapter = builder.callAdapter;
  this.baseUrl = builder.retrofit.baseUrl();
  this.responseConverter = builder.responseConverter;
  this.httpMethod = builder.httpMethod;
  this.relativeUrl = builder.relativeUrl;
  this.headers = builder.headers;
  this.contentType = builder.contentType;
  this.hasBody = builder.hasBody;
  this.isFormEncoded = builder.isFormEncoded;
  this.isMultipart = builder.isMultipart;
  this.parameterHandlers = builder.parameterHandlers;
}

將參數(shù)全部塞給 ServiceMethod。
有了 ServiceMethod 后矫钓,回到 invoke 方法要尔,會(huì)創(chuàng)建 OkHttpCall,

OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
  this.serviceMethod = serviceMethod;
  this.args = args;
}

只是進(jìn)行了一些賦值新娜。
最后赵辕,關(guān)鍵來(lái)了

return serviceMethod.callAdapter.adapt(okHttpCall);

剛才我們提到,在 serviceMethod 的創(chuàng)建過(guò)程概龄,會(huì)拿到創(chuàng)建 Retrofit 時(shí)設(shè)置的 callAdapter还惠,所以這里的 serviceMethod.callAdapter,就是RxJavaCallAdapterFactory旁钧,那么直接看到 RxJavaCallAdapterFactory 的 adapt 方法即可吸重。

@Override public <R> Observable<Response<R>> adapt(Call<R> call) {
  Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));
  if (scheduler != null) {
    return observable.subscribeOn(scheduler);
  }
  return observable;
}

返回了一個(gè) observable,之后就可以把復(fù)雜邏輯都能穿成一條線了歪今。
如果嚎幸,我并不想使用 RxJava,即沒(méi)有設(shè)置 RxJavaCallAdapterFactory 呢寄猩? 這時(shí) serviceMethod.callAdapter 又是什么嫉晶?當(dāng)然是 ExecutorCallAdapterFactory。如下:

@Override public <R> Call<R> adapt(Call<R> call) {
  return new ExecutorCallbackCall<>(callbackExecutor, call);
}

創(chuàng)建了 ExecutorCallbackCall,并將 MainThreadExecutor 和 OkHttpCall 傳進(jìn)替废。ExecutorCallbackCall 當(dāng)然是繼承自Call的箍铭。也就是返回給我們的Call對(duì)象。
那么現(xiàn)在看到 ExecutorCallbackCall 就好了椎镣。注意诈火,這里傳進(jìn)的 call 是 OkHttpCall。好状答,繼續(xù)前進(jìn)冷守。看到構(gòu)造方法

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

喔惊科,OkHttpCall 變成了 delegate拍摇。到這里就好了。
執(zhí)行Call
之后馆截,如果我們拿到的是Call對(duì)象充活,使用姿勢(shì)是這樣的: 異步:

call.enqueue(new Callback<List<User>>() {
    @Override
    public void onResponse(Call<List<User>> call, Response<List<User>> response) {

    }

    @Override
    public void onFailure(Call<List<User>> call, Throwable t) {

    }
});

同步:

call.execute();

ok,那我們現(xiàn)在回來(lái)看蜡娶,這個(gè) call 調(diào)用 enqueue或execute混卵,實(shí)際上調(diào)用的是ExecutorCallbackCall 的 enqueue 或 execute 方法。我們分析 enqueue 方法即可窖张。如下:

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

  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          if (delegate.isCanceled()) {
            // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
          } else {
            callback.onResponse(ExecutorCallbackCall.this, response);
          }
        }
      });
    }

    @Override public void onFailure(Call<T> call, final Throwable t) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

可以看到淮菠,把請(qǐng)求的任務(wù)轉(zhuǎn)交給了 delegate,也就是 OkHttpCall荤堪。 而 callbackExecutor澄阳,也就是 MainThreadExecutor碎赢,調(diào)用了 execute 方法速梗,在這里完成了線程的切換,具體如下:

static class MainThreadExecutor implements Executor {
  private final Handler handler = new Handler(Looper.getMainLooper());

  @Override public void execute(Runnable r) {
    handler.post(r);
  }
}

ok枕赵,那么現(xiàn)在只需要關(guān)注到 OkHttpCall 即可拷窜。同樣看到入隊(duì)方法,如下:

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

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
        throws IOException {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }
      callSuccess(response);
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }

    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }

    private void callSuccess(Response<T> response) {
      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  });
}

可以看到,OkHttpCall只是包裝了Okhttp懊昨,咦,其實(shí)這個(gè)從名字也能猜出來(lái)的嫉你。底層實(shí)現(xiàn)還是Okhttp均抽。createRawCall 方法會(huì)調(diào)用 serviceMethod.toRequest 生成一個(gè) Request 其掂,這時(shí)那些通過(guò)解析注解獲取的httpMethod終于派上用場(chǎng)了,toRequest 大概是這樣的款熬,可以腦補(bǔ)一下:

 RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

你可能會(huì)疑惑贤牛,GsonConverterFactory 為什么沒(méi)有用到?其實(shí)是有的闰集,需要繼續(xù)跟進(jìn)一下,當(dāng) Okhttp 請(qǐng)求成功武鲁,會(huì)調(diào)用 parseResponse 方法來(lái)解析結(jié)果沐鼠,看到這個(gè)方法沒(méi)叹谁?看到了 在 parseResponse 方法中, serviceMethod 會(huì)調(diào)用 toResponse 方法焰檩,對(duì)結(jié)果進(jìn)行轉(zhuǎn)換析苫,使用的是 Gson布蔗。
好了纵揍,相信同步調(diào)用也可以分析了。當(dāng)然泽谨,如果是使用 observable吧雹,和 call 對(duì)象調(diào)用是同理的涂身。
全文完畢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丁鹉,一起剝皮案震驚了整個(gè)濱河市揣钦,隨后出現(xiàn)的幾起案子漠酿,更是在濱河造成了極大的恐慌,老刑警劉巖宇姚,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件空凸,死亡現(xiàn)場(chǎng)離奇詭異寸痢,居然都是意外死亡啼止,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卖词,“玉大人吏夯,你說(shuō)我怎么就攤上這事噪生《逅裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵桨嫁,是天一觀的道長(zhǎng)璃吧。 經(jīng)常有香客問(wèn)我废境,道長(zhǎng),這世上最難降的妖魔是什么朦促? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任务冕,我火速辦了婚禮幻赚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘落恼。我一直安慰自己佳谦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布啥刻。 她就那樣靜靜地躺著可帽,像睡著了一般窗怒。 火紅的嫁衣襯著肌膚如雪蓄拣。 梳的紋絲不亂的頭發(fā)上球恤,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天碎捺,我揣著相機(jī)與錄音,去河邊找鬼收厨。 笑死诵叁,一個(gè)胖子當(dāng)著我的面吹牛拧额,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侥锦,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼恭垦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼番挺!你這毒婦竟也來(lái)了玄柏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绍坝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灾挨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年劳澄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了地技。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莫矗。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡作谚,死狀恐怖庵芭,靈堂內(nèi)的尸體忽然破棺而出妹懒,到底是詐尸還是另有隱情,我是刑警寧澤双吆,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布眨唬,位于F島的核電站,受9級(jí)特大地震影響好乐,放射性物質(zhì)發(fā)生泄漏匾竿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一蔚万、第九天 我趴在偏房一處隱蔽的房頂上張望岭妖。 院中可真熱鬧,春花似錦笛坦、人聲如沸区转。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)废离。三九已至,卻和暖如春礁芦,著一層夾襖步出監(jiān)牢的瞬間蜻韭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泡仗,地道東北人娩怎。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓崩瓤,卻偏偏與公主長(zhǎng)得像谷遂,于是被迫代替她去往敵國(guó)和親蛋逾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偷拔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 最近非常流行 Retrofit+RxJava+OkHttp 這一整套的網(wǎng)絡(luò)請(qǐng)求和異步操作的開(kāi)源框架蛤签,從 Jake ...
    慌不要慌閱讀 1,967評(píng)論 1 7
  • 1.基本使用 參照官方網(wǎng)站實(shí)例:(1) 創(chuàng)建一個(gè)java接口用來(lái)作為http請(qǐng)求 (2) Retrofit類(lèi)會(huì)自動(dòng)...
    helloKai閱讀 156評(píng)論 0 0
  • 前言 在上一周學(xué)習(xí)了一下 Retrofit 的執(zhí)行流程痴柔。接下來(lái)的文章要更為深入地學(xué)習(xí) Retrofit 的各個(gè)類(lèi)豪嚎,...
    野生西瓜閱讀 1,664評(píng)論 0 10
  • 本文主要講 Retrofit 對(duì)象的創(chuàng)建及其 .create 方法妄荔。基本包括了這個(gè)類(lèi)的全部?jī)?nèi)容篷角。 Retrofit...
    CHSmile閱讀 405評(píng)論 0 0
  • 美麗秘籍 無(wú)論男女老少俩滥,無(wú)論遠(yuǎn)古還是現(xiàn)代,愛(ài)美之心人皆有之以清。那么我們究竟怎樣才能讓自己青春永駐婚脱,貌美如花错森,優(yōu)雅大方...
    bae73facdd8e閱讀 98評(píng)論 0 1