中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Retrofit使用方法-創(chuàng)新互聯(lián)

一、整體思路

從使用方法出發(fā),首先是怎么使用,其次是我們使用的功能在內(nèi)部是如何實現(xiàn)的, 實現(xiàn)方案上有什么技巧,有什么范式。全文基本上是對 Retrofit 源碼的一個分析與 導讀,非常建議大家下載 Retrofit 源碼之后,跟著本文,過一遍源碼

成都創(chuàng)新互聯(lián)專注于黑河網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供黑河營銷型網(wǎng)站建設,黑河網(wǎng)站制作、黑河網(wǎng)頁設計、黑河網(wǎng)站官網(wǎng)定制、成都微信小程序服務,打造黑河網(wǎng)絡公司原創(chuàng)品牌,更為您提供黑河網(wǎng)站排名全網(wǎng)營銷落地服務。

二、基本用例

2.1 創(chuàng)建 Retrofit 對象
  Retrofit retrofit = new Retrofit.Builder() 
   .baseUrl("https://api.github.com/") 
   .addConverterFactory(GsonConverterFactory.create()) 
   .build();
2.2 定義 API 并獲取 API 實例
  public interface GitHubService { 
   @GET("users/{user}/repos") 
   Call<List<Repo>> listRepos(@Path("user") String user); 
  }
  GitHubService github = retrofit.create(GitHubService.class);

先看定義,非常簡潔,也沒有什么特別之處,除了兩個注解:@GET@Path 。它們的用處稍后再分析,我們接著看創(chuàng)建 API 實 例: retrofit.create(GitHubService.class) 。這樣就創(chuàng)建了 API 實例了, 就可以調(diào)用 API 的方法發(fā)起 HTTP 網(wǎng)絡請求了,太方便了。 但 create 方法是怎么創(chuàng)建 API 實例的呢?

  public <T> T create(final Class<T> service) { 
   // 省略非關鍵代碼 
   return (T) Proxy.newProxyInstance(service.getClassLoader(), 
    new Class<?>[] { service }, 
    new InvocationHandler() { 
     @Override 
     public Object invoke(Object proxy, Method method, Object ... args) 
       throws Throwable { 
     // 先省略實現(xiàn) 
     } 
    }); 
  }

創(chuàng)建 API 實例使用的是動態(tài)代理技術(shù)。

簡而言之,就是動態(tài)生成接口的實現(xiàn)類(當然生成實現(xiàn)類有緩存機制),并創(chuàng)建其 實例(稱之為代理),代理把對接口的調(diào)用轉(zhuǎn)發(fā)給 InvocationHandler 實例, 而在 InvocationHandler 的實現(xiàn)中,除了執(zhí)行真正的邏輯(例如再次轉(zhuǎn)發(fā)給真 正的實現(xiàn)類對象),我們還可以進行一些有用的操作,例如統(tǒng)計執(zhí)行時間、進行初 始化和清理、對接口調(diào)用進行檢查等。 為什么要用動態(tài)代理?因為對接口的所有方法的調(diào)用都會集中轉(zhuǎn)發(fā)到 InvocationHandler#invoke 函數(shù)中,我們可以集中進行處理,更方便了。你可 能會想,我也可以手寫這樣的代理類,把所有接口的調(diào)用都轉(zhuǎn)發(fā)到 InvocationHandler#invoke 呀,當然可以,但是可靠地自動生成豈不更方便?

2.3 調(diào)用 API 方法

獲取到 API 實例之后,調(diào)用方法和普通的代碼沒有任何區(qū)別:

  Call<List<Repo>> call = github.listRepos("square"); 
  List<Repo> repos = call.execute().body();

這兩行代碼就發(fā)出了 HTTP 請求,并把返回的數(shù)據(jù)轉(zhuǎn)化為了 List&lt;Repo&gt;,太方 便了!

現(xiàn)在我們來看看調(diào)用 listRepos 是怎么發(fā)出 HTTP 請求的。上面 Retrofit#create 方法返回時省略的代碼如下:

  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 n ormal invocation. 
       if (method.getDeclaringClass() == Object.class) { 
        return method.invoke(this, args); 
       }
       if (platform.isDefaultMethod(method)) { 
        return platform.invokeDefaultMethod(method, service, p roxy, args); 
       }
       ServiceMethod serviceMethod = loadServiceMethod(method); 
       OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); 
       return serviceMethod.callAdapter.adapt(okHttpCall); 
       } 
  });

如果調(diào)用的是 Object 的方法,例如 equalstoString,那就直接調(diào)用。 如果是 default 方法(Java 8 引入),就調(diào)用 default 方法。這些我們都先不管,因 為我們在安卓平臺調(diào)用 listRepos ,肯定不是這兩種情況,那這次調(diào)用真正干活 的就是這三行代碼了(好好記住這三行代碼,因為接下來很長的篇幅都是在講它們 :) ):

  ServiceMethod serviceMethod = loadServiceMethod(method); 
  OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); 
  return serviceMethod.callAdapter.adapt(okHttpCall);

在繼續(xù)分析這三行代碼之前,先看一個流程圖

這三行代碼基本就是對應于流程圖中軸上部了, ServiceMethod , build OkHttpCall, CallAdapter adapt

2.4 ServiceMethod

ServiceMethod&lt;T&gt; 類的作用正如其 JavaDoc所言:

Adapts an invocation of an interface method into an HTTP call. 把對接口方法 的調(diào)用轉(zhuǎn)為一次 HTTP 調(diào)用。

一個 ServiceMethod 對象對應于一個 API interface 的一個方 法, loadServiceMethod(method) 方法負責加載 ServiceMethod

  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; 
  }

這里實現(xiàn)了緩存邏輯,同一個 API 的同一個方法,只會創(chuàng)建一次。這里由于我們每 次獲取 API 實例都是傳入的 class 對象,而 class 對象是進程內(nèi)單例的,所 以獲取到它的同一個方法 Method 實例也是單例的,所以這里的緩存是有效的。

我們再看看 ServiceMethod 的構(gòu)造函數(shù):

  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;
  }

成員很多,但這里我們重點關注四個成 員: callFactory, callAdapter , responseConverterparameterHandlers 。

  1. callFactory 負責創(chuàng)建 HTTP 請求,HTTP 請求被抽象為了okhttp3.Call 類,它表示一個已經(jīng)準備好,可以隨時執(zhí)行的 HTTP 請求;
  2. callAdapterretrofit2.Call&lt;T&gt;轉(zhuǎn)為 T (注意和 okhttp3.Call 區(qū)分開來,retrofit2.Call&lt;T&gt;表示的是對一個 Retrofit 方法的調(diào)用),這個過程會發(fā)送一個 HTTP 請求,拿到服務器返回的數(shù)據(jù)(通 過 okhttp3.Call實現(xiàn)),并把數(shù)據(jù)轉(zhuǎn)換為聲明的 T 類型對象(通過 Converter&lt;F, T&gt; 實現(xiàn));
  3. responseConverterConverter&lt;ResponseBody, T&gt; 類型,負責把服 務器返回的數(shù)據(jù)(JSON、XML、二進制或者其他格式,由 ResponseBody封裝)轉(zhuǎn)化為 T 類型的對象;
  4. parameterHandlers 則負責解析 API 定義時每個方法的參數(shù),并在構(gòu)造 HTTP 請求時設置參數(shù);

它們的使用稍后再分析,這里先看看它們的創(chuàng)建(代碼比較分散,就不貼太多代碼 了,大多是結(jié)論):

2.4.1 callFactory

this.callFactory = builder.retrofit.callFactory(),所以 callFactory 實際上由 Retrofit 類提供,而我們在構(gòu)造 Retrofit 對象 時,可以指定 callFactory,如果不指定,將默認設置為一個 okhttp3.OkHttpClient。

2.4.2 callAdapter
  private CallAdapter<?> createCallAdapter() { 
   // 省略檢查性代碼 
   Annotation[] annotations = method.getAnnotations(); 
   try { 
    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); 
  }
  }

可以看到, callAdapter 還是由 Retrofit 類提供。在 Retrofit 類內(nèi)部, 將遍歷一個 CallAdapter.Factory 列表,讓工廠們提供,如果最終沒有工廠能 (根據(jù) returnTypeannotations )提供需要的 CallAdapter ,那將拋出 異常。而這個工廠列表我們可以在構(gòu)造 Retrofit 對象時進行添加。

2.4.3, responseConverter
  private Converter<ResponseBody, T> createResponseConverter() { 
   Annotation[] annotations = method.getAnnotations(); 
   try { 
   return retrofit.responseBodyConverter(responseType, annotati ons); 
   } catch (RuntimeException e) { 
   // Wide exception range because factories are user code. 
   throw methodError(e, "Unable to create converter for %s", re sponseType); 
   } 
  }

同樣, responseConverter 還是由 Retrofit 類提供,而在其內(nèi)部,邏輯和創(chuàng) 建 callAdapter 基本一致,通過遍歷 Converter.Factory列表,看看有沒有 工廠能夠提供需要的 responseBodyConverter。工廠列表同樣可以在構(gòu)造 Retrofit 對象時進行添加。

2.4.4 parameterHandlers

每個參數(shù)都會有一個 ParameterHandler ,由 ServiceMethod#parseParameter 方法負責創(chuàng)建,其主要內(nèi)容就是解析每個參數(shù) 使用的注解類型(諸如 Path , Query , Field 等),對每種類型進行單獨的 處理。構(gòu)造 HTTP 請求時,我們傳遞的參數(shù)都是字符串,那 Retrofit 是如何把我們 傳遞的各種參數(shù)都轉(zhuǎn)化為 String 的呢?還是由 Retrofit 類提供 converter!

Converter.Factory 除了提供上一小節(jié)提到的 responseBodyConverter,還提 供 requestBodyConverterstringConverter,API 方法中除了 @Body@Part 類型的參數(shù),都利用 stringConverter 進行轉(zhuǎn)換,而 @Body@Part 類型的參數(shù)則利用 requestBodyConverter 進行轉(zhuǎn)換。

這三種 converter 都是通過“詢問”工廠列表進行提供,而工廠列表我們可以在構(gòu)造 Retrofit 對象時進行添加。

2.4.5 工廠讓各個模塊得以高度解耦

上面提到了三種工廠: okhttp3.Call.Factory , CallAdapter.FactoryConverter.Factory ,分別負責提供不同的模塊,至于怎么提供、提供何種模 塊,統(tǒng)統(tǒng)交給工廠,Retrofit 完全不摻和,它只負責提供用于決策的信息,例如參 數(shù)/返回值類型、注解等。

這不正是我們苦苦追求的高內(nèi)聚低耦合效果嗎?解耦的第一步就是面向接口編程, 模塊之間、類之間通過接口進行依賴,創(chuàng)建怎樣的實例,則交給工廠負責,工廠同 樣也是接口,添加(Retrofit doc 中使用 install 安裝一詞,非常貼切)怎樣的工 廠,則在最初構(gòu)造 Retrofit 對象時決定,各個模塊之間完全解耦,每個模塊只 專注于自己的職責,全都是套路,值得反復玩味、學習與模仿。

除了上面重點分析的這四個成員, ServiceMethod 中還包含了 API 方法的 url 解 析等邏輯,包含了眾多關于泛型和反射相關的代碼,有類似需求的時候,也非常值 得學習模仿

2.5 OkHttpCall

終于把 ServiceMethod 看了個大概,接下來我們看看 OkHttpCallOkHttpCall 實現(xiàn)了 retrofit2.Call ,我們通常會使用它的 execute()enqueue(Callback&lt;T&gt; callback) 接口。前者用于同步執(zhí)行 HTTP 請求,后者 用于異步執(zhí)行。

2.5.1,先看 execute()
  @Override 
  public Response<T> execute() throws IOException { 
   okhttp3.Call call; 
   synchronized (this) { 
   // 省略部分檢查代碼 

   call = rawCall;
   if (call == null) { 
    try { 
     call = rawCall = createRawCall(); 
    } catch (IOException | RuntimeException e) { 
     creationFailure = e; 
     throw e; 
   } 
   } 
  }
  return parseResponse(call.execute()); 
  ......
 }

主要包括三步:

  1. 創(chuàng)建 okhttp3.Call ,包括構(gòu)造參數(shù);
  2. 執(zhí)行網(wǎng)絡請求;
  3. 解析網(wǎng)絡請求返回的數(shù)據(jù);

createRawCall() 函數(shù)中,我們調(diào)用了 serviceMethod.toRequest(args) 來創(chuàng)建 okhttp3.Request ,而在后者中,我們之前準備好的 parameterHandlers 就派上了用場。

然后我們再調(diào)用 serviceMethod.callFactory.newCall(request) 來創(chuàng)建 okhttp3.Call ,這里之前準備好的 callFactory 同樣也派上了用場,由于工 廠在構(gòu)造 Retrofit 對象時可以指定,所以我們也可以指定其他的工廠(例如使 用過時的 HttpURLConnection 的工廠),來使用其它的底層 HttpClient 實現(xiàn)。

我們調(diào)用 okhttp3.Call#execute() 來執(zhí)行網(wǎng)絡請求,這個方法是阻塞的,執(zhí)行 完畢之后將返回收到的響應數(shù)據(jù)。收到響應數(shù)據(jù)之后,我們進行了狀態(tài)碼的檢查, 通過檢查之后我們調(diào)用了 serviceMethod.toResponse(catchingBody) 來把響 應數(shù)據(jù)轉(zhuǎn)化為了我們需要的數(shù)據(jù)類型對象。在 toResponse 函數(shù)中,我們之前準 備好的 responseConverter 也派上了用場。

好了,之前準備好的東西都派上了用場,還好沒有白費 :)

2.5.2 再看 enqueue(Callback&lt;T&gt; callback)

這里的異步交給了 okhttp3.Call#enqueue(Callback responseCallback) 來 實現(xiàn),并在它的 callback 中調(diào)用 parseResponse 解析響應數(shù)據(jù),并轉(zhuǎn)發(fā)給傳入 的 callback。

2.6 CallAdapter

終于到了最后一步了, CallAdapter&lt;T&gt;#adapt(Call&lt;R&gt; call) 函數(shù)負責把 retrofit2.Call&lt;R&gt; 轉(zhuǎn)為 T 。這里 T 當然可以就是 retrofit2.Call&lt;R&gt; ,這時我們直接返回參數(shù)就可以了,實際上這正是 DefaultCallAdapterFactory創(chuàng)建的 CallAdapter 的行為。至于其他類型的 工廠返回的 CallAdapter 的行為,這里暫且不表,后面再單獨分析。

至此,一次對 API 方法的調(diào)用是如何構(gòu)造并發(fā)起網(wǎng)絡請求、以及解析返回數(shù)據(jù),這 整個過程大致是分析完畢了。對整個流程的概覽非常重要,結(jié)合 stay 畫的流程圖, 應該能夠比較輕松地看清整個流程了。

雖然我們還沒分析完,不過也相當于到了萬里長征的遵義,終于可以舒一口氣了 :)

三、retrofit-adapters 模塊

retrofit 模塊內(nèi)置了 DefaultCallAdapterFactoryExecutorCallAdapterFactory ,它們都適用于 API 方法得到的類型為 retrofit2.Call 的情形,前者生產(chǎn)的 adapter 啥也不做,直接把參數(shù)返回,后 者生產(chǎn)的 adapter 則會在異步調(diào)用時在指定的 Executor 上執(zhí)行回調(diào)。

retrofit-adapters 的各個子模塊則實現(xiàn)了更多的工 廠: GuavaCallAdapterFactoryJava8CallAdapterFactoryRxJavaCallAdapterFactory 。這里我主要分析 RxJavaCallAdapterFactory ,下面的內(nèi)容就需要一些 RxJava 的知識了,不過 我想使用 Retrofit 的你,肯定也在使用RxJava :)

RxJavaCallAdapterFactory#get 方法中對返回值的類型進行了檢查,只支持 rx.Single , rx.Completablerx.Observable ,這里我主要關注對 rx.Observable 的支持。

RxJavaCallAdapterFactory#getCallAdapter 方法中對返回值的泛型類型進行 了進一步檢查,例如我們聲明的返回值類型為 Observable&lt;List&lt;Repo&gt;&gt;,泛型 類型就是 List&lt;Repo&gt; ,這里對 retrofit2.Responseretrofit2.adapter.rxjava.Result 進行了特殊處理,有單獨的 adapter 負責 進行轉(zhuǎn)換,其他所有類型都由 SimpleCallAdapter負責轉(zhuǎn)換。

那我們就來看看 SimpleCallAdapter#adapt

  @Override 
  public <R> Observable<R> adapt(Call<R> call) { 
   Observable<R> observable = Observable.create(new CallOnSubscri be<>(call)) 
    .lift(OperatorMapResponseToBodyOrError.<R>instance()); if (scheduler != null) { 
    return observable.subscribeOn(scheduler); 
   }
   return observable; 
  }

這里創(chuàng)建了一個 Observable ,它的邏輯由 CallOnSubscribe 類實現(xiàn),同時使 用了一個 OperatorMapResponseToBodyOrError 操作符,用來把 retrofit2.Response 轉(zhuǎn)為我們聲明的類型,或者錯誤異常類型。

我們接著看 CallOnSubscribe#call

  @Override 
  public void call(final Subscriber<? super Response<T>> subscribe r) {
   // Since Call is a one-shot type, clone it for each new subscr iber. 
   Call<T> call = originalCall.clone(); 
  // Wrap the call in a helper which handles both unsubscription and backpressure. 
  RequestArbiter<T> requestArbiter = new  RequestArbiter<>(call, subscriber); 
  subscriber.add(requestArbiter); 
  subscriber.setProducer(requestArbiter); 
  }

代碼很簡短,只干了三件事:

  1. clone 了原來的 call,因為 okhttp3.Call 是只能用一次的,所以每次都是 新 clone 一個進行網(wǎng)絡請求;
  2. 創(chuàng)建了一個叫做 RequestArbiterproducer,別被它的名字嚇懵了,它就 是個 producer;
  3. 把這個 producer設置給 subscriber;

簡言之,大部分情況下 Subscriber 都是被動接受 Observable push 過來的數(shù)據(jù), 但要是 Observable 發(fā)得太快,Subscriber 處理不過來,那就有問題了,所以就有 了一種 Subscriber 主動 pull 的機制,而這種機制就是通過 Producer 實現(xiàn)的。給 Subscriber 設置 Producer 之后(通過 Subscriber#setProducer 方法), Subscriber 就會通過 Producer 向上游根據(jù)自己的能力請求數(shù)據(jù)(通過 Producer#request 方法),而 Producer 收到請求之后(通常都是 Observable 管理 Producer,所以“相當于”就是 Observable 收到了請求),再根據(jù)請求的量給 Subscriber 發(fā)數(shù)據(jù)。

那我們就看看 RequestArbiter#request

  @Override 
  public void request(long n) { 
   if (n < 0) throw new IllegalArgumentException("n < 0: " + n); 
   if (n == 0) return; // Nothing to do when requesting 0. 
   if (!compareAndSet(false, true)) return; // Request was alread y triggered. 
   try { 
    Response<T> response = call.execute(); 
    if (!subscriber.isUnsubscribed()) { 
     subscriber.onNext(response); 
    } 
   } catch (Throwable t) { 
    Exceptions.throwIfFatal(t); 
    if (!subscriber.isUnsubscribed()) { 
     subscriber.onError(t); 
    }
    return; 
   }

   if (!subscriber.isUnsubscribed()) { 
    subscriber.onCompleted(); 
   } 
  }

producer 相關的邏輯非常簡單,這里就不在贅述。實際干活的邏輯就是執(zhí)行 call.execute() ,并把返回值發(fā)送給下游。

OperatorMapResponseToBodyOrError#call 也相當簡短:

  @Override 
  public Subscriber<? super Response<T>> call(final Subscriber<? s uper T> child) { 
   return new Subscriber<Response<T>>(child) { 
    @Override 
    public void onNext(Response<T> response) { 
     if (response.isSuccessful()) { 
      child.onNext(response.body()); 
     } else { 
      child.onError(new HttpException(response)); 
     } 
    }

    @Override 
    public void onCompleted() { 
     child.onCompleted(); 
    }

    @Override
    public void onError(Throwable e) { 
     child.onError(e); 
    } 
   }; 
  }

關鍵就是調(diào)用了 response.body() 并發(fā)送給下游。這里, body() 返回的就是 我們聲明的泛型類型了,至于 Retrofit 是怎么把服務器返回的數(shù)據(jù)轉(zhuǎn)為我們聲明的 類型的,這就是 responseConverter 的事了,還記得嗎?

最后看一張返回 Observable 時的調(diào)用棧:

執(zhí)行路徑就是:

  1. Observable.subscribe ,觸發(fā) API 調(diào)用的執(zhí)行;
  2. CallOnSubscribe#callclone call,創(chuàng)建并設置 producer
  3. RequestArbiter#request ,subscriber 被設置了 producer 之后最終調(diào)用 request,在 request 中發(fā)起請求,把結(jié)果發(fā)給下游;
  4. OperatorMapResponseToBodyOrError$1#onNext ,把 response的 body 發(fā) 給下游;
  5. 最終就到了我們subscribe 時傳入的回調(diào)里面了;

四、retrofit-converters 模塊

retrofit 模塊內(nèi)置了 BuiltInConverters ,只能處理 ResponseBody , RequestBody 和 String 類型的轉(zhuǎn)化(實際上不需要轉(zhuǎn))。而 retrofit- converters 中的子模塊則提供了 JSON,XML,ProtoBuf 等類型數(shù)據(jù)的轉(zhuǎn)換功能, 而且還有多種轉(zhuǎn)換方式可以選擇。這里我主要關注 GsonConverterFactory 。

代碼非常簡單:

  @Override 
  public Converter<ResponseBody, ?> responseBodyConverter(Type typ e, Annotation[] annotations, Retrofit retrofit) { 
   TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); 
   return new GsonResponseBodyConverter<>(gson, adapter); 
  }

  final class GsonResponseBodyConverter<T> implements Converter<Re sponseBody, T> { 
   private final Gson gson; 
   private final TypeAdapter<T> adapter; 

   GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) { 
    this.gson = gson; 
    this.adapter = adapter; 
   }

   @Override public T convert(ResponseBody value) throws IOExcept ion {
    JsonReader jsonReader = gson.newJsonReader(value.charStream( )); 
    try { 
     return adapter.read(jsonReader); } finally { 
      value.close(); 
      } 
    } 
  }

根據(jù)目標類型,利用 Gson#getAdapter 獲取相應的 adapter,轉(zhuǎn)換時利用 Gson 的 API 即可。

另外有需要云服務器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

當前文章:Retrofit使用方法-創(chuàng)新互聯(lián)
網(wǎng)頁URL:http://www.rwnh.cn/article28/dscscp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供自適應網(wǎng)站、域名注冊、移動網(wǎng)站建設、靜態(tài)網(wǎng)站標簽優(yōu)化、網(wǎng)站建設

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
新野县| 南华县| 石景山区| 师宗县| 揭西县| 宜兰县| 汪清县| 五河县| 龙陵县| 扶风县| 阿尔山市| 兰西县| 福鼎市| 特克斯县| 醴陵市| 崇信县| 益阳市| 鄱阳县| 长子县| 大英县| 富蕴县| 雅江县| 张家港市| 靖安县| 普兰县| 马鞍山市| 醴陵市| 昌平区| 鸡东县| 额敏县| 资讯| 奉节县| 伊川县| 南部县| 鹤山市| 沙雅县| 隆林| 沈阳市| 莱阳市| 当涂县| 图木舒克市|