1.Retrofit2概述
Retrofit框架是Square公司出品的目前非常流行的網(wǎng)絡框架.
效率高遵湖,實現(xiàn)簡單,運用注解和動態(tài)代理.
極大簡化了網(wǎng)絡請求的繁瑣步驟罗侯,非常適合REST ful網(wǎng)絡請求.
目前Retofit版本是2Retrofit其實我們可以理解為OkHttp的加強版仑撞。
它也是一個網(wǎng)絡加載框架聋袋。底層是使用OKHttp封裝的突倍。
準確來說,網(wǎng)絡請求的工作本質上是OkHttp完成腔稀,而 Retrofit 僅負責網(wǎng)絡請求接口的封裝。
它的一個特點是包含了特別多注解赘方,方便簡化你的代碼量烧颖。
并且還支持很多的開源庫(著名例子:Retrofit + RxJava)
2.Retrofit2的好處
- 超級解耦
解耦弱左?解什么耦窄陡?
我們在請求接口數(shù)據(jù)的時候,API接口定義和API接口使用總是相互影響拆火,什么傳參跳夭、回調(diào)等涂圆,耦合在一塊。有時候我們會考慮一下怎么封裝我們的代碼讓這兩個東西不那么耦合币叹,這個就是Retrofit的解耦目標润歉,也是它的最大的特點。 - 可以配置不同HttpClient來實現(xiàn)網(wǎng)絡請求颈抚,如OkHttp踩衩、HttpClient...
- 支持同步、異步和RxJava
- 可以配置不同的反序列化工具來解析數(shù)據(jù)贩汉,如json驱富、xml...
- 請求速度快,使用非常方便靈活
3.Retrofit2配置
- 官網(wǎng)
- 依賴:
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
- 添加網(wǎng)絡權限:
<uses-permission android:name="android.permission.INTERNET" />
4匹舞,Retrofit2的使用步驟
- 定義接口類(封裝URL地址和數(shù)據(jù)請求)
- 實例化Retrofit
- 通過Retrofit實例創(chuàng)建接口服務對象
- 接口服務對象調(diào)用接口中的方法褐鸥,獲取Call對象
- Call對象執(zhí)行請求(異步偏陪、同步請求)
6.Retrofit2發(fā)送GET锹安、POST請求(異步、同步)
- Retrofit2發(fā)送GET
//主機地址
String url = "http://apicloud.mob.com/appstore/health/search?key=1ac78a8602d58&name=轉氨酶";
String BASE_URL = "http://apicloud.mob.com/appstore/health/";//必須以反斜杠結尾
//接口服務
public interface BigFlyServer {
//GET
@GET("search?key=1ac78a8602d58&name=轉氨酶")
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對象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MyServer.URL)
.build();
//2.獲取MyServer接口服務對象
MyServer myServer = retrofit.create(MyServer.class);
//3.獲取Call對象
//方式一
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對象執(zhí)行請求
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);//默認直接回調(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對象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MyServer.URL)
.build();
//2.獲取MyServer接口服務對象
MyServer myServer = retrofit.create(MyServer.class);
//3.獲取Call對象
final Call<ResponseBody> call = myServer.getData1();
new Thread(){//子線程執(zhí)行
@Override
public void run() {
super.run();
try {
//4.Call對象執(zhí)行請求
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/";//必須以反斜杠結尾
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對象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MyServer.URL)
.build();
//2.獲取MyServer接口服務對象
MyServer myServer = retrofit.create(MyServer.class);
//3.獲取Call對象
//方式一
Call<ResponseBody> call1 = myServer.postData1("908ca46881994ffaa6ca20b31755b675");
//方式二
Map<String,Object> map = new HashMap<>();
map.put("appKey","908ca46881994ffaa6ca20b31755b675");
Call<ResponseBody> call = myServer.postData2(map);
//4.Call對象執(zhí)行請求
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);//默認直接回調(diào)主線程
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("retrofit", "onFailure: "+t.getMessage());
}
});
}
7.Retrofit注解
注解代碼 請求格式
請求方式:
@GET GET請求
@POST POST請求
@DELETE DELETE請求
@HEAD HEAD請求
@OPTIONS OPTIONS請求
@PATCH PATCH請求
請求頭:
@Headers("K:V") 添加請求頭详炬,作用于方法
@Header("K") 添加請求頭姊舵,參數(shù)添加頭
@FormUrlEncoded 用表單數(shù)據(jù)提交晰绎,搭配參數(shù)使用
@Stream 下載
@Multipart 用文件上傳提交 multipart/form-data
請求參數(shù):
@Query 替代參數(shù)值,通常是結合get請求的
@QueryMap 替代參數(shù)值括丁,通常是結合get請求的
@Field 替換參數(shù)值寒匙,是結合post請求的
@FieldMap 替換參數(shù)值,是結合post請求的
請求路徑:
@Path 替換路徑
@Url 路徑拼接
請求體:
@Body(RequestBody) 設置請求體躏将,是結合post請求的
文件處理:
@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/";//必須以反斜杠結尾
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);
//復用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);
//無參形式
RequestBody requestBody3 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded,charset-UTF-8"),"");
Call<ResponseBody> call03 = myServer.getFormData1(requestBody3);
//RequestBody (Form表單,鍵值對參數(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);//默認直接回調(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ù)解析方式
- 使用時需要在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)絡請求適配器(CallAdapter)
- Retrofit支持多種網(wǎ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);//默認直接回調(diào)主線程
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("retrofit", "onFailure: "+t.getMessage());
}
});
}
12.Form表單語法
1掸鹅、Form表單語法
在Form元素的語法中,EncType表明提交數(shù)據(jù)的格式 用 Enctype 屬性指定將數(shù)據(jù)回發(fā)到服務器時瀏覽器使用的編碼類型拦赠。
1巍沙,application/x-www-form-urlencoded: 窗體數(shù)據(jù)被編碼為名稱/值對。這是標準的編碼格式荷鼠。
2句携,multipart/form-data: 窗體數(shù)據(jù)被編碼為一條消息,頁上的每個控件對應消息中的一個部分允乐,這個一般文件上傳時用矮嫉。
3削咆,text/plain: 窗體數(shù)據(jù)以純文本形式進行編碼,其中不含任何控件或格式字符蠢笋。
2拨齐、常用的編碼方式
form的enctype屬性為編碼方式,常用有兩種:
application/x-www-form-urlencoded(默認)
multipart/form-data
1.x-www-form-urlencoded
瀏覽器用x-www-form-urlencoded的編碼方式把form數(shù)據(jù)轉換成一個字串(name1=value1&name2=value2…)
2.multipart/form-data
瀏覽器把form數(shù)據(jù)封裝到http body中昨寞,然后發(fā)送到server瞻惋。如果沒有type=file的控件,用默認的application/x-www-form-urlencoded就可以了援岩。但是如果type=file的話熟史,就要用到multipart/form-data了。
11.Retrofit2及OkHttp3的區(qū)別
Retrofit2使用注解設置請求內(nèi)容
Retrofit2回調(diào)主線程窄俏,OkHttp3回調(diào)子線程
Retrofit2可以做數(shù)據(jù)解析轉換
Retrofit2可以使用在REST ful網(wǎng)絡請求.
12.Retrofit2源碼分析(底層OkHttp3蹂匹,注解了解,反射了解)
構建者構建
動態(tài)代理凹蜈、反射
注解配置請求
底層基于OkHttpCall調(diào)用
源碼解析:
1.點擊builder限寞,平臺選擇(platform)、獲取工廠仰坦、baseURL
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
2.點擊build方法
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//可以選擇客戶端履植,如果傳的callFactory是空,默認創(chuàng)建一個ok
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//線程池悄晃,主要做線程切換
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 添加一個網(wǎng)絡切換器
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// 添加一個數(shù)據(jù)切換工廠
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
//最后添加完工廠返回一個retrofit對象
`
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
- MyService myService = retrofit.create(MyService.class);
用到了動態(tài)代理(Proxy.newProxyInstance)及反射
Proxy.newProxyInstance獲取動態(tài)代理單例玫霎,里邊有三個參數(shù),
第一個是代理對象妈橄,第二個參數(shù)是調(diào)用的方法庶近,第三個是方法的參數(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);
進方法里邊看,RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
工廠模式眷蚓,看工廠是這么build出來的
遍歷注解
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);