1. 程式人生 > >Retrofit 2使用要點梳理:小白進階回憶錄

Retrofit 2使用要點梳理:小白進階回憶錄

0X00 寫在前面

相信做過Android網路請求的同學都繞不開Volley,Retrofit,OkHttp這幾座大山,至於他們的前世姻緣以及孰優孰劣,不在本部落格的討論範圍。如題,這篇部落格主要介紹一個小白(其實就是我自己)的Retrofit2進階之路,會結合一個開發例項介紹5節內容:

  • Retrofit2 HTTP請求方法註解的欄位說明
  • Call<T>響應結果的處理問題
  • Retrofit2+RxJava實現開發效率最大化
  • 自定義OkHttp Interceptor實現日誌輸出,儲存和新增Cookie
  • 自定義ResponseConverter,自定義HTTP請求註解

先來回顧一下Retrofit2

在專案中的完整使用流程:建立Bean類 --> 建立介面形式的http請求方法 --> 通過Retrofit.builder()建立介面物件並呼叫http方法請求網路資料 --> 在RxJavaObservable(被觀察者)中非同步處理請求結果!

那麼Retrofit2 Http 請求方法註解有那麼多欄位,都代表什麼含義呢?新增請求頭或者大檔案上傳的請求方法該怎麼寫呢?這將在第二節介紹。另外,Retrofit2基本用法的網路響應結果是一個Call<T> ,那麼怎樣在Android中解析Call<T> 呢?將在第二節介紹。第三節根據Retrofit2使用流程介紹了一個實踐專案是怎樣使用Retrofit2+RxJava

做網路請求。第四節和四五節是Retrofit實現一些複雜需求的必殺技,介紹了自定義OkHttp Interceptor實現日誌輸出,儲存和新增Cookie;自定義ResponseConverter,自定義HTTP請求註解等內容。

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 請求:

  1. addCallAdapterFactory(rxJavaCallAdapterFactory) 方法指定使用RxJava 作為CallAdapter ,需要傳入一個RxJavaCallAdapterFactory物件:CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create()
  2. addConverterFactory(gsonConverterFactory) 方法指定 Gson 作為解析Json資料的ConverterConverter.Factory gsonConverterFactory = GsonConverterFactory.create()
  3. 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 方法請求網路資料 --> 在RxJavaObservable 中非同步處理請求結果!

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 來實現的,重寫了responseBodyConverterrequestBodyConverter 這兩個方法,程式碼只有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請求介面GuokrServicegetGuokrs()方法返回值的泛型型別,如果返回值型別是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面試題不得不懂的斐波那契數列

很長一段時間裡,我都非常疑惑:“我寫的技術文章不差啊,有內容的同時還很有趣,不至於每篇只有區區幾十個人讀啊?為什麼有些內容簡單到只有一行註冊碼的文章瀏覽量反而輕鬆破萬?”這樣的疑惑如鯁在喉啊!寫技術部落格做分享的人,有幾個真心實意的說只寫給自己看的?這無非是寫出來後沒人看的自我安慰(不好意思,我就屬於這種人,