Retrofit2

1.Retrofit2概述

  1. Retrofit框架是Square公司出品的目前非常流行的網(wǎng)絡(luò)框架.
    效率高,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)用注解和動(dòng)態(tài)代理.
    極大簡(jiǎn)化了網(wǎng)絡(luò)請(qǐng)求的繁瑣步驟,非常適合REST ful網(wǎng)絡(luò)請(qǐng)求.
    目前Retofit版本是2

  2. Retrofit其實(shí)我們可以理解為OkHttp的加強(qiáng)版。
    它也是一個(gè)網(wǎng)絡(luò)加載框架。底層是使用OKHttp封裝的。
    準(zhǔn)確來(lái)說(shuō),網(wǎng)絡(luò)請(qǐng)求的工作本質(zhì)上是OkHttp完成,而 Retrofit 僅負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求接口的封裝。
    它的一個(gè)特點(diǎn)是包含了特別多注解,方便簡(jiǎn)化你的代碼量。
    并且還支持很多的開(kāi)源庫(kù)(著名例子:Retrofit + RxJava)

2.Retrofit2的好處

  1. 超級(jí)解耦
    解耦?解什么耦?
    我們?cè)谡?qǐng)求接口數(shù)據(jù)的時(shí)候,API接口定義和API接口使用總是相互影響,什么傳參、回調(diào)等,耦合在一塊。有時(shí)候我們會(huì)考慮一下怎么封裝我們的代碼讓這兩個(gè)東西不那么耦合,這個(gè)就是Retrofit的解耦目標(biāo),也是它的最大的特點(diǎn)。
  2. 可以配置不同HttpClient來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求,如OkHttp、HttpClient...
  3. 支持同步、異步和RxJava
  4. 可以配置不同的反序列化工具來(lái)解析數(shù)據(jù),如json、xml...
  5. 請(qǐng)求速度快,使用非常方便靈活

3.Retrofit2配置

  1. 官網(wǎng)
  2. 依賴:
    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
  3. 添加網(wǎng)絡(luò)權(quán)限:<uses-permission android:name="android.permission.INTERNET" />

4,Retrofit2的使用步驟

  1. 定義接口類(封裝URL地址和數(shù)據(jù)請(qǐng)求)
  2. 實(shí)例化Retrofit
  3. 通過(guò)Retrofit實(shí)例創(chuàng)建接口服務(wù)對(duì)象
  4. 接口服務(wù)對(duì)象調(diào)用接口中的方法,獲取Call對(duì)象
  5. Call對(duì)象執(zhí)行請(qǐng)求(異步、同步請(qǐng)求)

6.Retrofit2發(fā)送GET、POST請(qǐng)求(異步、同步)

  1. Retrofit2發(fā)送GET
//主機(jī)地址
String url = "http://apicloud.mob.com/appstore/health/search?key=1ac78a8602d58&name=轉(zhuǎn)氨酶";
    String BASE_URL = "http://apicloud.mob.com/appstore/health/";//必須以反斜杠結(jié)尾

    //接口服務(wù)
    public interface BigFlyServer {
        
        //GET
        @GET("search?key=1ac78a8602d58&name=轉(zhuǎn)氨酶")
        Call<ResponseBody> getData1();
    
        @GET("search?")
        Call<ResponseBody> getData2(@Query("key") String key,@Query("name") String key);
    
        @GET("search?")
        Call<ResponseBody> getData3(@QueryMap Map<String,Object> map);
    }


//Get異步
private void initGetEnqueue() {

    //1.創(chuàng)建Retrofit對(duì)象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MyServer.URL)
                .build();
        
        //2.獲取MyServer接口服務(wù)對(duì)象
        MyServer myServer = retrofit.create(MyServer.class);
        
        //3.獲取Call對(duì)象
        //方式一
        Call<ResponseBody> call1 = myServer.getData1();

        //方式二
        Call<ResponseBody> call2 = myServer.getData2("908ca46881994ffaa6ca20b31755b675");

        //方式三
        Map<String,Object> map = new HashMap<>();
        map.put("appKey","908ca46881994ffaa6ca20b31755b675");
        Call<ResponseBody> call = myServer.getData3(map);

        //4.Call對(duì)象執(zhí)行請(qǐng)求
        call.enqueue(new Callback<ResponseBody>() {

            @Override
            public void onResponse(Call<ResponseBody> call,Response<ResponseBody> response) {

                try {
                    String result = response.body().string();

                    Log.e("retrofit", "onResponse: "+result);
                    tv.setText(result);//默認(rèn)直接回調(diào)主線程
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

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

                Log.e("retrofit", "onFailure: "+t.getMessage());
            }
        });
    }


    //GET同步
    private void initGetExecute() {

        //1.創(chuàng)建Retrofit對(duì)象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MyServer.URL)
                .build();

        //2.獲取MyServer接口服務(wù)對(duì)象
        MyServer myServer = retrofit.create(MyServer.class);

        //3.獲取Call對(duì)象
        final Call<ResponseBody> call = myServer.getData1();

        new Thread(){//子線程執(zhí)行
            @Override
            public void run() {
                super.run();

                try {
        //4.Call對(duì)象執(zhí)行請(qǐng)求
                    Response<ResponseBody> response = call.execute();

                    final String result = response.body().string();

                    Log.e("retrofit", "onResponse: "+result);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv.setText(result);
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }

2.Retrofit2發(fā)送POST

String URL = "http://apicloud.mob.com/appstore/health/";//必須以反斜杠結(jié)尾

public interface MyServer {
    
//POST("search?")    POST("search")相同
    @POST("search?")
    @FormUrlEncoded
    Call<ResponseBody> postData1(@Field("key") String appKey,@Field("name") String appKey);

    @POST("search")  
    @FormUrlEncoded
    Call<ResponseBody> postData2(@FieldMap Map<String,Object> map);
}

//POST異步
private void initPostEnqueue() {

    //1.創(chuàng)建Retrofit對(duì)象
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(MyServer.URL)
            .build();

    //2.獲取MyServer接口服務(wù)對(duì)象
    MyServer myServer = retrofit.create(MyServer.class);

    //3.獲取Call對(duì)象
    //方式一
    Call<ResponseBody> call1 = myServer.postData1("908ca46881994ffaa6ca20b31755b675");

    //方式二
    Map<String,Object> map = new HashMap<>();
    map.put("appKey","908ca46881994ffaa6ca20b31755b675");
    Call<ResponseBody> call = myServer.postData2(map);

    //4.Call對(duì)象執(zhí)行請(qǐng)求
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call,Response<ResponseBody> response) {

            try {
                String result = response.body().string();

                Log.e("retrofit", "onResponse: "+result);
                tv.setText(result);//默認(rèn)直接回調(diào)主線程
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

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

            Log.e("retrofit", "onFailure: "+t.getMessage());
        }
    });
}

7.Retrofit注解

注解代碼       請(qǐng)求格式

請(qǐng)求方式:
    @GET           GET請(qǐng)求
    @POST          POST請(qǐng)求
    @DELETE        DELETE請(qǐng)求
    @HEAD          HEAD請(qǐng)求
    @OPTIONS       OPTIONS請(qǐng)求
    @PATCH         PATCH請(qǐng)求

請(qǐng)求頭:
    @Headers("K:V") 添加請(qǐng)求頭,作用于方法
    @Header("K")    添加請(qǐng)求頭,參數(shù)添加頭
    @FormUrlEncoded 用表單數(shù)據(jù)提交,搭配參數(shù)使用
    @Stream         下載
    @Multipart      用文件上傳提交   multipart/form-data

請(qǐng)求參數(shù):
    @Query          替代參數(shù)值,通常是結(jié)合get請(qǐng)求的
    @QueryMap       替代參數(shù)值,通常是結(jié)合get請(qǐng)求的
    @Field          替換參數(shù)值,是結(jié)合post請(qǐng)求的
    @FieldMap       替換參數(shù)值,是結(jié)合post請(qǐng)求的

請(qǐng)求路徑:
    @Path           替換路徑
    @Url            路徑拼接

請(qǐng)求體:
    @Body(RequestBody)  設(shè)置請(qǐng)求體,是結(jié)合post請(qǐng)求的

文件處理:
    @Part Multipart.Part
    @Part("key") RequestBody requestBody(單參)
    @PartMap Map<String,RequestBody> requestBodyMap(多參)
    @Body RequestBody requestBody(自定義參數(shù))

8.Retrofit2其他常用注解使用

String URL = "http://api.shujuzhihui.cn/api/news/";//必須以反斜杠結(jié)尾

public interface MyServer {
    
    //Path,不能替換參數(shù),必須路徑中字符
    @GET("wages/{wageId}/detail") 
    Call<ResponseBody >getImgData(@Path("wageId") String wageId);

    //Url
            //@Url cannot be used with @GET URL (parameter #1),如果使用url注解,get注解為空
    @GET
    Call<ResponseBody >getImgData(@Url String url);

    @GET
    Call<ResponseBody >getImgData(@Url String url,@Query("appKey") String appkey);

    
    //Json
    @POST("categories")
    @Headers("Content-Type:application/json")
    Call<ResponseBody> getFormDataJson(@Body RequestBody requestBody);

    //Form
    @POST("categories")
    @Headers("Content-Type:application/x-www-form-urlencoded")
    Call<ResponseBody> getFormData1(@Body RequestBody requestBody);

    //復(fù)用URL
    @POST()
    @Headers("Content-Type:application/x-www-form-urlencoded")
    Call<ResponseBody> getFormData2(@Url String url,@Body RequestBody requestBody);

    //通用
    @POST()
    Call<ResponseBody> getFormData3(@Url String url, @Body RequestBody requestBody, @Header("Content-Type") String contentType);
}

//RequestBody參數(shù)拼接
private void initPostBody() {

    Retrofit.Builder builder = new Retrofit.Builder();
    Retrofit retrofit = builder.baseUrl(MyServer.URL)
            .build();

    MyServer myServer = retrofit.create(MyServer.class);

    //Json類型
    String json1 = "{\n" + "    \"appKey\": \"908ca46881994ffaa6ca20b31755b675\"\n" +  "}";
    RequestBody requestBody01 = RequestBody.create(MediaType.parse("application/json,charset-UTF-8"),json1);
    Call<ResponseBody> call01 = myServer.getFormDataJson(requestBody01);

    //String類型
    //有參形式
    RequestBody requestBody02 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded,charset-UTF-8"),"appKey=908ca46881994ffaa6ca20b31755b675");
    Call<ResponseBody> call02 = myServer.getFormData1(requestBody02);

    //無(wú)參形式
    RequestBody requestBody3 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded,charset-UTF-8"),"");
    Call<ResponseBody> call03 = myServer.getFormData1(requestBody3);

    //RequestBody (Form表單,鍵值對(duì)參數(shù)形式)
    //方式一(requestBody)
    FormBody requestBody = new FormBody.Builder()
            .add("appKey","908ca46881994ffaa6ca20b31755b675")
            .build();
    Call<ResponseBody> call1 = myServer.getFormData1(requestBody);

    //方式二(url+requestBody)
    FormBody requestBody = new FormBody.Builder()
            .add("appKey","908ca46881994ffaa6ca20b31755b675")
            .build();
    Call<ResponseBody> call2 = myServer.getFormData2("categories",requestBody);

    //方式三(url+requestBody+header)
    FormBody requestBody = new FormBody.Builder()
            .add("appKey","908ca46881994ffaa6ca20b31755b675")
            .build();
    Call<ResponseBody> call3 = myServer.getFormData3("categories",requestBody,"application/x-www-form-urlencoded");


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

            try {
                String result = response.body().string();

                Log.e("retrofit", "onResponse: "+result);
                tv.setText(result);//默認(rèn)直接回調(diào)主線程
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

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

            Log.e("retrofit", "onFailure: "+t.getMessage());
        }
    });
}

9.數(shù)據(jù)解析器(Converter)

  • Retrofit支持多種數(shù)據(jù)解析方式
  • 使用時(shí)需要在Gradle添加依賴
    Gson:com.squareup.retrofit2:converter-gson:2.0.2
    Jackson:com.squareup.retrofit2:converter-jackson:2.0.2
Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(MyService.URL)
                .build();

10.網(wǎng)絡(luò)請(qǐng)求適配器(CallAdapter)

  • Retrofit支持多種網(wǎng)絡(luò)請(qǐng)求適配器方式:guava、Java8和rxjava
    guava:com.squareup.retrofit2:adapter-guava:2.0.2
    Java8:com.squareup.retrofit2:adapter-java8:2.0.2
    rxjava:com.squareup.retrofit2:adapter-rxjava:2.0.2

11.Retrofit2文件上傳

String FileUpLoadURL = "http://yun918.cn/study/public/";

public interface MyServer {
    
 @POST("file_upload.php")
    @Multipart
    Call<ResponseBody> upLoadFile1(@Part MultipartBody.Part part,@Part("key") RequestBody requestBody);//單參

    @POST("file_upload.php")
    @Multipart
    Call<ResponseBody> upLoadFile2(@Part MultipartBody.Part part, @PartMap Map<String,RequestBody> requestBodyMap); //多參

    @POST("file_upload.php")
    @Multipart
    Call<ResponseBody> upLoadFile3(@Body RequestBody requestBody);  //自定義
}

//文件上傳
private void initPostFile() {

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(MyServer.FileUpLoadURL)
            .build();

    MyServer myServer = retrofit.create(MyServer.class);

    Call<ResponseBody> call = null;

    //上傳文件路徑
    File file = new File("/sdcard/Pictures/Screenshorts/dd.png");
    if(file.exists()){

        //方式一
        RequestBody requestBody1 = RequestBody.create(MediaType.parse("image/png"),file);

        MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), requestBody1);

        RequestBody responseBody2 = RequestBody.create(MediaType.parse("multipart/form-data"),"abc");

        call = myServer.upLoadFile1(part, responseBody2);

        //方式二
        MultipartBody.Builder builder1 = new MultipartBody.Builder().setType(MultipartBody.FORM);

        MultipartBody body = builder1.addFormDataPart("key", "abc")
                .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/png"),file))
                .build();

        call = myServer.upLoadFile3(body);

        //方式三
        MultipartBody.Part part2 = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/png"),file));

        Map<String, RequestBody> map = new HashMap<>();
        RequestBody responseBody3 = RequestBody.create(MediaType.parse("multipart/form-data"),"abc");
        map.put("key",responseBody3);

        call = myServer.upLoadFile2(part2, map);

    }

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                String result = response.body().string();

                Log.e("retrofit", "onResponse: "+result);
                tv.setText(result);//默認(rèn)直接回調(diào)主線程
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("retrofit", "onFailure: "+t.getMessage());
        }
    });
}

12.Form表單語(yǔ)法

1、Form表單語(yǔ)法

在Form元素的語(yǔ)法中,EncType表明提交數(shù)據(jù)的格式 用 Enctype 屬性指定將數(shù)據(jù)回發(fā)到服務(wù)器時(shí)瀏覽器使用的編碼類型。
1,application/x-www-form-urlencoded: 窗體數(shù)據(jù)被編碼為名稱/值對(duì)。這是標(biāo)準(zhǔn)的編碼格式。
2,multipart/form-data: 窗體數(shù)據(jù)被編碼為一條消息,頁(yè)上的每個(gè)控件對(duì)應(yīng)消息中的一個(gè)部分,這個(gè)一般文件上傳時(shí)用。
3,text/plain: 窗體數(shù)據(jù)以純文本形式進(jìn)行編碼,其中不含任何控件或格式字符。

2、常用的編碼方式

form的enctype屬性為編碼方式,常用有兩種:
application/x-www-form-urlencoded(默認(rèn))
multipart/form-data
1.x-www-form-urlencoded
瀏覽器用x-www-form-urlencoded的編碼方式把form數(shù)據(jù)轉(zhuǎn)換成一個(gè)字串(name1=value1&name2=value2…)
2.multipart/form-data
瀏覽器把form數(shù)據(jù)封裝到http body中,然后發(fā)送到server。如果沒(méi)有type=file的控件,用默認(rèn)的application/x-www-form-urlencoded就可以了。但是如果type=file的話,就要用到multipart/form-data了。

11.Retrofit2及OkHttp3的區(qū)別

Retrofit2使用注解設(shè)置請(qǐng)求內(nèi)容
Retrofit2回調(diào)主線程,OkHttp3回調(diào)子線程
Retrofit2可以做數(shù)據(jù)解析轉(zhuǎn)換
Retrofit2可以使用在REST ful網(wǎng)絡(luò)請(qǐng)求.

12.Retrofit2源碼分析(底層OkHttp3,注解了解,反射了解)

構(gòu)建者構(gòu)建
動(dòng)態(tài)代理、反射
注解配置請(qǐng)求
底層基于OkHttpCall調(diào)用

源碼解析:
1.點(diǎn)擊builder,平臺(tái)選擇(platform)、獲取工廠、baseURL

platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;

2.點(diǎn)擊build方法

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

//可以選擇客戶端,如果傳的callFactory是空,默認(rèn)創(chuàng)建一個(gè)ok  
okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

//線程池,主要做線程切換
Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

 // 添加一個(gè)網(wǎng)絡(luò)切換器
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

// 添加一個(gè)數(shù)據(jù)切換工廠
List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

//最后添加完工廠返回一個(gè)retrofit對(duì)象
`
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
  1. MyService myService = retrofit.create(MyService.class);
    用到了動(dòng)態(tài)代理(Proxy.newProxyInstance)及反射

Proxy.newProxyInstance獲取動(dòng)態(tài)代理單例,里邊有三個(gè)參數(shù),
第一個(gè)是代理對(duì)象,第二個(gè)參數(shù)是調(diào)用的方法,第三個(gè)是方法的參數(shù)
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable 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);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });


 ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }


result = ServiceMethod.parseAnnotations(this, method);
進(jìn)方法里邊看,RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
工廠模式,看工廠是這么build出來(lái)的
遍歷注解


ServiceMethod中返回的東西

HttpServiceMethod

 @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }


OkHttpCall----enqueue-- response = parseResponse(rawResponse);

T body = responseConverter.convert(catchingBody);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本博客為作者原創(chuàng),如需轉(zhuǎn)載請(qǐng)注明原博客出處:WONDER'TWO 0X00 寫在前面 相信做過(guò)And...
    一只酸奶牛哇閱讀 4,510評(píng)論 9 34
  • 前言 如果看Retrofit的源碼會(huì)發(fā)現(xiàn)其實(shí)質(zhì)上就是對(duì)okHttp的封裝,使用面向接口的方式進(jìn)行網(wǎng)絡(luò)請(qǐng)求,利用動(dòng)態(tài)...
    李某人吖閱讀 2,254評(píng)論 0 0
  • 一.Retrofit中Builder模式完成初始化工作 Retrofit現(xiàn)在已經(jīng)是各種網(wǎng)絡(luò)請(qǐng)求類APP的標(biāo)配了,我...
    Geeks_Liu閱讀 2,115評(píng)論 0 9
  • 本文將順著構(gòu)建請(qǐng)求對(duì)象->構(gòu)建請(qǐng)求接口->發(fā)起同步/異步請(qǐng)求的流程,分析Retrofit是如何實(shí)現(xiàn)的。 開(kāi)始之前,...
    zhuhf閱讀 1,687評(píng)論 0 10
  • 前言 注解式的框架非?;?,注解以其輕量,簡(jiǎn)潔等特性被人們所喜愛(ài)者,關(guān)鍵是它解藕。網(wǎng)絡(luò)請(qǐng)求的框架非常多,比較受歡迎的...
    薩達(dá)哈魯醬閱讀 630評(píng)論 0 5

友情鏈接更多精彩內(nèi)容