Retrofit 2使用要點梳理:小白進階回憶錄
0X00 寫在前面
相信做過Android網路請求的同學都繞不開Volley,Retrofit,OkHttp這幾座大山,至於他們的前世姻緣以及孰優孰劣,不在本部落格的討論範圍。如題,這篇部落格主要介紹一個小白(其實就是我自己)的Retrofit2進階之路,會結合一個開發例項介紹5節內容:
- Retrofit2 HTTP請求方法註解的欄位說明
Call<T>
響應結果的處理問題- Retrofit2+RxJava實現開發效率最大化
- 自定義OkHttp Interceptor實現日誌輸出,儲存和新增Cookie
- 自定義ResponseConverter,自定義HTTP請求註解
先來回顧一下Retrofit2
http
請求方法 --> 通過Retrofit.builder()
建立介面物件並呼叫http
方法請求網路資料 --> 在RxJava
的Observable
(被觀察者)中非同步處理請求結果!
那麼Retrofit2 Http 請求方法註解有那麼多欄位,都代表什麼含義呢?新增請求頭或者大檔案上傳的請求方法該怎麼寫呢?這將在第二節介紹。另外,Retrofit2基本用法的網路響應結果是一個Call<T>
,那麼怎樣在Android中解析Call<T>
呢?將在第二節介紹。第三節根據Retrofit2使用流程介紹了一個實踐專案是怎樣使用Retrofit2+RxJava
0X01 Retrofit2 HTTP請求方法註解的欄位說明
從Retrofit2的官方文件來看,Retrofit2 進行網路請求的URL分為兩部分:BaseURL和relativeURL。BaseURL需要以/
結尾, 一般不需要變化直接定義即可,當然在特殊的情況下,比如後一次網路訪問URL需要從前一次訪問結果中獲取相關引數,那麼就需要動態的操作URL,這種用法會在第五節進行介紹;relativeURL與每次請求的引數相關,所以每個request 方法都需要 http annotation 來提供請求的relativeURL,Retrofit2內建的註解有五個:GET, POST, PUT, DELETE, and HEAD.
可以看到,有URL請求引數,Query
引數這些簡單網路請求引數;同時還支援用@Header
新增請求頭;POST
請求中常用@FormUrlEncoded
提交表單,並用@Field
定義表單域;@MultiPart
檔案上傳並用@Part
定義請求體。來看一個具體的例子(摘自Retrofit2官方文件):
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit2把網路請求定義成介面的形式,如上是一個GET請求,@Path
表示一個佔位符,@Path
中的變數必須與@GET
變數中{
和 }
中間的部分一致。下面是一個POST請求,@FormUrlEncoded
用於提交一個表單,@Field
定義了表單的name和value。更多詳細的用法詳見Retrofit2官方文件API Declaration ,另外Retrofit請求引數註解欄位說明 這篇部落格介紹的比較詳細可作參考:
public interface GitHubService {
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
}
0X02 Call<T>
響應結果的處理
細心的你有木有發現,發現官方文件中給出的請求方法示例,返回結果都是 Call<List<User>>
這種形式?沒錯!這就是Retrofit2最原始的網路請求用法,官方文件上介紹的很簡潔,可以在 call<T>
響應物件上做非同步或者同步的操作,每個 call<T>
物件只能用一次,要想多次使用可以呼叫 clone()
方法來克隆出多個 call
物件以供更多操作使用。因為Retrofit2 是一個型別安全的Java和Android網路請求庫,所以以上的操作對 Java 網路請求也是適用的。
針對JVM而言,網路請求和結果處理會放在同一個執行緒中執行,那麼在Android中,我們怎樣處理請求結果物件 call
呢?官方文件也給出了答案,我們都知道Android中網路請求這類耗時操作都是放在工作執行緒(即worker thread)來執行的,然後在主執行緒(也即 UI thread)處理網路請求結果,自然Retrofit2也不例外,由於Retrofit2拋棄了飽受詬病的Apache HttpClient底層只依賴OkHttp3.0,網路訪問層的操作都會交由OkHttp來完成,而OkHttp不僅擁有自動維護的socket連線池,減少握手次數,而且還擁有佇列執行緒池,可以輕鬆寫併發,同時還支援socket自動選擇最好路線,並支援自動重連,OkHttp的優點遠不止於此,所以Retrofit2選擇用OkHttp作為網路請求執行器是一個再明智不過的決定。
如果你想非同步的執行網路請求,最簡單的就是在Activity或者Fragment中View控制元件的監聽器中進行網路訪問,並通過call.enqueue()
處理請求結果,並更新UI,下面是一個小demo:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
final Call<List<Contributor>> call =
gitHubService.repoContributors("square", "retrofit");
call.enqueue(new Callback<List<Contributor>>() {
@Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(response.body().toString());
}
@Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("Something went wrong: " + t.getMessage());
}
});
}
});
如果你需要在工作執行緒中執行網路請求,而不是在一個Activity或者一個Fragment中去執行,那麼也就意味著,你可以在同一個執行緒中同步的去執行網路請求,使用call.execute()
方法來處理請求結果即可,程式碼如下:
try {
Response<User> response = call.execute();
} catch (IOException e ){
// handle error
}
0X03 Retrofit2+RxJava實現開發效率最大化
Retorfit是支援RxJava,Guava,Java8 等等一系列擴充套件的,關於RxJava這個網紅我就不做介紹了,RactiveX專案對 JVM 的擴充套件,你可以把它當做一個超級強大的非同步事件處理庫,可他的NB之處遠不止於此,至少做Android的都應該聽過他的鼎鼎大名,不熟悉的可以去看看RxJava Wiki!!而這裡Retrofit2+RxJava
組合就可以實現開發效率的大幅提升,至於怎樣提升的?對比一下你以前寫的網路請求的程式碼量就知道了!結合一個實踐專案的原始碼來分析,這裡是請求果殼網最新的100條文章資料,返回結果為Json,首先build.gradle
新增依賴:
compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
compile 'io.reactivex:rxjava:1.1.0' // 推薦同時新增RxJava
compile 'com.squareup.retrofit2:retrofit:2.1.0' // Retrofit網路處理
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' // Retrofit的rx解析庫
compile 'com.squareup.retrofit2:converter-gson:2.1.0' // Retrofit的gson庫
compile 'com.squareup.okhttp3:okhttp:3.2.0' // OkHttp3
第一步,定義伺服器Json資料對應的POJO類,這裡我們可以偷一下懶可以直接通過jsonschema2pojo 這個網站自動生成POJO類,就不用我們手動去寫了,然後copy到專案目錄的bean包下。接著便是定義HTTP請求方法了,以介面的形式定義,如下:
// 伺服器資料對應的實體類
public class Guokr {
// 定義序列化後的名字
public @SerializedName("ok") Boolean response_ok;
// 定義序列化後的名字
public @SerializedName("result") List<GuokrResult> response_results;
public static class GuokrResult {
public int id;
public String title;
public String headline_img_tb; // 用於文章列表頁小圖
public String headline_img; // 用於文章內容頁大圖
public String link;
public String author;
public String summary;
}
}
// HTTP請求方法
public interface GuokrService {
@GET("handpick/article.json")
Observable<Guokr> getGuokrs(@Query("retrieve_type") String type,
@Query("category") String category,
@Query("limit") int limit,
@Query("ad") int ad);
}
其中 Observable<Guokr>
是RxJava中的被觀察者,對應請求結果Call<T>
。只是因為Retrofit提供了非常強大的CallAdapterFactory
完美相容了RxJava
這個超級大網紅,才導致我們平常看到的寫法是這樣的。第二步, 需要通過Retrofit.builder()
建立 GuokrService
介面物件,通過介面物件執行 getGuokrs
方法進行網路訪問,程式碼如下:
// 封裝 GuokrService 請求
public static GuokrService getGuokrService() {
if (guokrService == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(mClient)
.baseUrl("http://apis.guokr.com/")
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.addConverterFactory(gsonConverterFactory)
.build();
guokrService = retrofit.create(GuokrService.class);
}
return guokrService;
}
// 預設載入最新的100條資料
subscription = RetrofitClient.getGuokrService().getGuokrs("by_since", "all", 100, 1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Guokr>() {
@Override
public void onCompleted() {
Log.e(TAG, "--------completed-------");
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "--------error-------");
Log.e(TAG, e.getMessage());
}
@Override
public void onNext(Guokr guokr) {
if (guokr.response_ok) {
List<Guokr.GuokrResult> guokrResults = guokr.response_results;
List<GuokrItem> guokrItems = new ArrayList<>(guokrResults.size());
for (Guokr.GuokrResult result : guokrResults) {
GuokrItem item = new GuokrItem();
item.headline_img_tb = result.headline_img_tb;
item.title = result.title;
item.id = result.id;
item.headline_img = result.headline_img;
item.summary = result.summary;
guokrItems.add(item);
}
mAdapter.addAll(guokrItems);
mAdapter.notifyDataSetChanged();
});
注意到封裝 GuokrService 請求:
addCallAdapterFactory(rxJavaCallAdapterFactory)
方法指定使用RxJava
作為CallAdapter
,需要傳入一個RxJavaCallAdapterFactory
物件:CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create()
;addConverterFactory(gsonConverterFactory)
方法指定Gson
作為解析Json資料的Converter
:Converter.Factory gsonConverterFactory = GsonConverterFactory.create()
;client(mClient)
方法指定網路執行器為OkHttp
如下建立一個預設的OkHttp物件傳入即可:OkHttpClient mClient = new OkHttpClient()
;
而載入網路資料這個鏈式呼叫就是RxJava最大的特色,用在這裡邏輯就是,被觀察者Observable<Guokr>
訂閱觀察者Observer<Guokr>
,當伺服器一有response,觀察者就會立即處理response result。因為RxJava
最大的亮點就是非同步,可以很方便的切換當前任務所在的執行緒,並能對事件流進行各種Map變換,比如壓合、轉換、快取等操作。這裡是最基本的用法,被觀察者直接把事件流訂閱到觀察者,中間沒有做轉換處理。
到此網路訪問就完成了,是不是很簡潔?簡潔就對了,那是因為太多東西Retrofit2和RxJava甚至是OkHttp都幫我們做好了!再回顧一下整個網路訪問流程:建立Bean類 --> 建立介面形式的http
請求方法 --> 通過Retrofit.builder()
建立介面物件並呼叫http
方法請求網路資料 --> 在RxJava
的Observable
中非同步處理請求結果!
0X04 自定義OkHttp Interceptor實現日誌輸出,儲存和新增Cookie
在Retrofit2做網路請求的第二步,我們需要通過Retrofit.builder()
方法來建立Retrofit
物件,其中client(mClient)
這個方法指定一個OkHttpClient
客戶端作為請求的執行器,需要傳入一個OkHttpClient
物件作為引數,那麼在這裡,我們就可以進行一些OkHttp相關的操作,比如自定義Interceptor
,通過自定義Interceptor
可以實現網路請求日誌的分級輸出,可以實現儲存和新增Cookie這些功能,當然,這些功能的實現都是基於OkHttp,所以要對OkHttp有一定的瞭解才能靈活運用。
Retrofit使用指南-->OkHttp配合Retrofit使用 這篇部落格在OkHttp配合Retrofit使用這一節,關於OkHttpClient新增HttpLoggingInterceptor
進行日誌輸出,以及如何設定SslSocketFactory做了詳細的說明,有興趣的同學可以參考!值得注意的是,如果後一次請求的URL,需要從前一次請求結果資料中獲取,這時候就需要動態的改變BaseURL,也可通過自定義Interceptor
來實現這一需求,在BaseURL改變時,只需要setHost()
就可以讓下次請求的BaseURL改變,程式碼如下:
public class DynamicBaseUrlInterceptor implements Interceptor {
private volatile String host;
public void setHost(String host) {
this.host = host;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!TextUtils.isEmpty(host)) {
HttpUrl newUrl = originalRequest.url().newBuilder()
.host(host)
.build();
originalRequest = originalRequest.newBuilder()
.url(newUrl)
.build();
}
return chain.proceed(originalRequest);
}
}
那麼怎樣在通過OkHttp儲存和新增Cookie呢?其實實現原理和上面新增日誌攔截器差不多,只是新增的Intercepter
不同而已,其實就是自定義了一個Interceptor
介面實現類,接收和儲存返回結果中的Cookie,或者新增Cookie,最後,在建立OkHttp例項的時候,傳入以上Interceptor
實現類的物件即可。Retrofit使用OkHttp儲存和新增cookie這篇部落格講的很好,可以作為參考!
簡而言之,以上這Retorfit2些高階運用都是基於定製化OkHttp來實現的,如果想玩得很溜就必須對OkHttp瞭解一二,推薦看這篇部落格OkHttp3原始碼分析綜述!最起碼需要弄清楚OkHttpClient自定義Interceptor這一塊內容,推薦看OkHttp Github Wiki --> Interceptors!
0X05 自定義ResponseConverter,自定義HTTP請求註解
預設情況下,Retrofit會把HTTP響應體反序列化到OkHttp的ResponseBody
中,加入Converter可以將返回的資料直接格式化成你需要的樣子,Retrofit提供瞭如下6個Converter可以直接使用,使用前需要加上相應的Gradle依賴:
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
在前面Retrofit2+RxJava例項中,我們指定GsonConverterFactory作為解析Json資料的Converter,當面對更復雜的需求時,仍然可以通過繼承Converter.Factory
來自定義Converter,只需要重寫以下這兩個方法即可:
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//your own implements
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//your own implements
}
我們不妨來看看GsonConverterFactory
原始碼,果然GsonConverterFactory
也是繼承Converter.Factory
來實現的,重寫了responseBodyConverter
和 requestBodyConverter
這兩個方法,程式碼只有70多行還是很簡潔的,如下:
public final class GsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static GsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static GsonConverterFactory create(Gson gson) {
return new GsonConverterFactory(gson);
}
private final Gson gson;
private GsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
這裡需要詳細解釋一下TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type))
中的TypeAdapter<?>
,TypeAdapte
是Gson提供的自定義Json解析器,Type就是HTTP請求介面GuokrService
中getGuokrs()
方法返回值的泛型型別,如果返回值型別是Call<T>
,那麼這裡的Type就是泛型型別 T ,如果返回值型別是Observable<List<Guokr>>
,那麼Type就是List<Guokr>
;關於Gson的詳細用法可以參考:你真的會用Gson嗎?Gson使用指南(四)。
我們看到responseBodyConverter
方法返回的是一個GsonResponseBodyConverter
物件,跟進去看一下GsonResponseBodyConverter
原始碼,也很簡單,原始碼 如下:
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, 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 IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
我們看到GsonResponseBodyConverter<T>
實現了Converter<ResponseBody, T>
,重寫了convert(ResponseBody value)
方法,這就給我們提供了一個思路:自定義Converter關鍵一步就是要實現Converter<ResponseBody, T>
介面並且重寫convert(ResponseBody value)
方法,具體重寫的程式碼我就不貼出來了,可以參考如何使用Retrofit請求非Restful API 這篇部落格自定義Converter的做法!
另外,如果需求更復雜,需要我們自定義HTTP請求方法的註解,又該怎麼做呢?我們還注意到GsonConverterFactory
類的重寫方法responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit)
中的Annotation[] methodAnnotations
這個引數,對的,或許你已經猜到了,這就是我們在HTTP請求介面方法中定義的註解,先看 @GET
註解的原始碼,如下:
/** Make a GET request. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
那我們自定義註解的思路也就有了,模仿上面 @GET
註解寫一個 @WONDERTWO
註解即可。這裡我點到即止,主要是提供一種思路,具體實現仍然可以參考上面提到的 如何使用Retrofit請求非Restful API 這篇部落格自定義HTTP請求註解的做法!
0X06 寫在後面
有一個結論說的是在網路上,只有 1% 的使用者貢獻了內容,10% 的使用者比較活躍,會評論和點贊,剩下的都是網路透明人,他們只是默默地在看,既不貢獻內容,也不點贊。這篇文章希望能讓你成為網路上貢獻內容的 TOP 1%。如果暫時做不到,那就先點個贊吧,成為活躍的 10%。
參考文獻
相關推薦
Retrofit 2使用要點梳理:小白進階回憶錄
0X00 寫在前面 相信做過Android網路請求的同學都繞不開Volley,Retrofit,OkHttp這幾座大山,至於他們的前世姻緣以及孰優孰劣,不在本部落格的討論範圍。如題,這篇部落格主要介紹一個小白(其實就是我自己)的Retrofit2進階之路,會結合一個開發例項介紹5節內容: Retrof
JAVA專案實戰練習-----小白進階之路2
今天接著昨天的內容,繼續來完成繪畫板這個專案昨天完成了滑鼠畫筆功能,那麼我們現在實現工具欄的功能,java中的工具欄使用javax.swing.JToolBar類表示,下面把新程式碼插入到DrawPictureFrame.java類中新增完工具欄之後就可以來一步步實現裡面的功
python小白進階三:主成分分析(PCA)
主成分分析(Principal Component Analysis,PCA)是最常用的一種降維方法,通常用於高維資料集的探索與視覺化,還可以用作資料壓縮和預處理等。矩陣的主成分就是其協方差矩陣對應的特徵向量,按照對應的特徵值大小進行排序,最大的特徵值就是第一主成
財會小白進階之路:如何在數字化財務時代成為行業大牛?
包括 alt mark 應該 設備 批量 認知 人員 活動 隨著雲技術、流程機器人、認知計算等創新型技術向財務領域不斷的深入,其在帶來組織架構、人員及管理變革的同時,也不禁引起了財會、稅務、審計等人員的困惑,在以機器人技術為牽引的數字化財務時代下,財會人員該如何獲得核心競爭
<linux小白進階> sed指令的基本用法
linux指令本文內容較簡潔,適合linux有點基礎的菜鳥。。。功能介紹之後都有例子便於理解,希望對大家能起到幫助作用sed 是一種在線編輯器,它一次處理一行內容。處理時,把當前處理的行存儲在臨時緩沖區中,稱為“模式空間”(pattern space),接著用sed命令處理緩沖區中的內容,處理完成後,把緩沖區
<linux小白進階> find的基本用法
linux指令本文內容較簡潔,適合linux有點基礎的菜鳥。。。看完希望對大家能起到幫助作用文件查找指令find的基本用法:Find:實時精確查找,遍歷指定目錄中的所有文件,相對於locate來書速度較慢-name:以文件名查找 EG: find /etc -name passwd-iname:以文件名查找不
STM32小白進階之路----按鍵的模組化,低延時,高靈敏
兩種按鍵查詢方法,兩種方式處理 第一種方法:掃描查詢法 優點:無延遲,高靈敏,無中斷 思想:也是別人的程式碼總結出來的,當按鍵連續讀到10個或者20(這個看你的按鍵多敏感,我的10就夠了)低電平,就算按鍵按下了 //需要迴圈掃描 u8 IsKey0Down()
Java小白進階之路(一)
1、字串操作:replace方法(舊字串,新子串)。 Scanner scanner = new Scanner(System.in); System.out.println("請輸入"); // 接收使用者的輸入的字串 String s1 = scanner.next(); // 接受整型 i
Python小白進階——TypeError: replaceSpace() missing 1 required positional argument: 'self'
# -*- coding:utf-8 -*- class Solution: # s 源字串 def replaceSpace(self, s): # write code here s = list(s) k =
Python小白進階——bisect函數了解一下
""" bisect 為可排序序列提供二分查詢演算法 """ import bisect #使用bisect函式前需要對列表進行排序,否則雖然可以輸出數值,但沒有意義 a = [1, 5, 6, 10, 9] a.sort() print("最初的列表:",
【Android】小白進階之WeakReference弱引用基礎淺析
作為一枚 android 應用開發小白,工作中凡是遇到不懂的點都要做一番總結,希望對你有益。 1、弱引用定義 弱引用,與強引用相對,GC 在回收時會忽略掉弱引用物件(忽略掉這種引用關係)。 即,就算弱引用指向了某個物件,但只要該物件沒有被強引用指向,該物件也會被GC檢查
【FreeRTOS】小白進階之任務如何共用FreeRTOS軟體定時器回撥函式(二)
介紹兩個定時器任務如何通過定時器 handle 共用一個回撥函式。 1、標頭檔案宣告和函式定義 #include "FreeRTOS.h" #include "task.h" #include "t
java常用類解析一,小白進階必備!
Random 讓系統產生隨機數使用 0~9的隨機數 (int)(Math.random()*10); 0~999的隨機數 (int)(Math.random()*1000); a~b的隨機數 (int)(Math.random()*(b-a)); 其實Math的random方法用的就是
【Android】小白進階之單例模式淺析
1、基礎簡介 由於最近專案需求使用到了IO操作,特意花費一定的時間研究了下單例模式,希望對你有用。 定義: 確保某個類只有一個例項,而且自行例項化提供給外部使用。 使用場景: 某個型別的物件只應該有且只有一個,或者避免建立多個物件消耗過多的資源時。 例如: 訪問
Retrofit 2使用要點梳理:淺析POST檔案/表單上傳
實習期的第一個任務就是為專案組預研FACE++智慧人臉識別這一新功能。呼叫曠視FACE++人臉識別介面,進行人臉識別有兩種方式:一是通過先上傳圖片到雲端儲存網站(網盤,雲盤,七牛雲等)獲得圖片檔案對應的URL引數,通過圖片的網路URL引數呼叫FACE++介面;二是在手機客戶端直接上傳檔案呼叫FACE++介
python函數(2):函數進階
int splay 基本 源文件 tuple [0 執行 內容 理念 昨天說了函數的一些最基本的定義,今天我們繼續研究函數。今天主要研究的是函數的命名空間、作用域、函數名的本質、閉包等等 預習: 1、寫函數,用戶傳入修改的文件名,與要修改的內容,執行函數,完成整個文件
數字貨幣時代:小白如何投資數字貨幣期貨
許多投資者和朋友都知道,在近期市場上最火爆的投資專案就是數字貨幣。許多投資者還沒有做過投資都開始準備投資數字貨幣了。那麼,投資者是如何購買數字貨幣的呢?今天,我將向您展示如何購買數字貨幣! 數字貨幣是一場能引發燎原之火的金融革命。這也是由民間發起的政府逐漸理解和默許,然後積極推動的國際潮
有什麼方法可以把WPS轉為Word:小白教你一招搞定
有什麼方法可以把WPS轉為Word?怎樣把WPS轉Word?我們在工作中經常會問這樣的問題,因為檔案的格式是多種多樣的。有時因為工作的需要,所以要轉換檔案格式。迅捷PDF轉換器幫你解決,下面就跟小編一起來學習一下把!WPS轉Word準備工作WPS轉Word我們需要藉助一款,簡單使用的轉換軟體來操作,進入迅捷P
有什麽方法可以把WPS轉為Word:小白教你一招搞定
迅捷pdf轉換器 mage 文件的 一起 操作 準備 軟件 ref https 有什麽方法可以把WPS轉為Word?怎樣把WPS轉Word?我們在工作中經常會問這樣的問題,因為文件的格式是多種多樣的。有時因為工作的需要,所以要轉換文件格式。迅捷PDF轉換器幫你解決,下面就跟
Java面試題:小白不得不懂的斐波那契數列
很長一段時間裡,我都非常疑惑:“我寫的技術文章不差啊,有內容的同時還很有趣,不至於每篇只有區區幾十個人讀啊?為什麼有些內容簡單到只有一行註冊碼的文章瀏覽量反而輕鬆破萬?”這樣的疑惑如鯁在喉啊!寫技術部落格做分享的人,有幾個真心實意的說只寫給自己看的?這無非是寫出來後沒人看的自我安慰(不好意思,我就屬於這種人,