Retrofit
1.簡介
一個適用於Android和Java的網絡請求庫,據說網絡請求非常的快
2.先來一個案例熟悉一下
案例:獲取http://www.tngou.net/api/food/list
的數據,返回的是Json格式
添加依賴和權限
// build.gradle
compile 'com.squareup.retrofit:retrofit:2.0.1-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
// AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
/**
* 獲取`http://www.tngou.net/api/food/list`的數據
*/
//1.創建一個接口(將http接口轉化爲Java接口)
public interface ICallFoodService {
/**
* GET:表示請求方式,(values)表示請求網址中的子目錄,直接訪問主站可以寫成("/")
* 在後面創建Retrofit對象時會添加一個baseUrl(http://www.tngou.net),所以請求的地址爲:baseUrl + "/api/{type}/list"
*
* 當請求動態的url時候可以使用{},利於我們使用不同參數訪問同一個 Web API 接口
* {type} 類似於佔位符的作用,具體類型由@Path("type") String type指定,這裏表示 {type} 將是一段字符串
*/
@GET("/api/{type}/list")
/**
*Call<FoodInfo> 是一個請求對象,<FoodInfo>返回的響應體的類型(一個JSONInfoBean)
*/
Call<FoodInfo> callFoodInfo(@Path("type") String type, @Query("page") int page, @Query("rows") int rows, @Query("id") int id);
}
----
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//對xlog進行初始化
XLog.init(LogLevel.ALL, new LogConfiguration.Builder().b().build());
//2.進行通信:創建一個retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.tngou.net")//請求的網址
.addConverterFactory(GsonConverterFactory.create())//添加一個json轉換器 將從服務器拿到的數據轉換爲JSON格式
.build();
//3.通過反射拿到我們創建的接口,並與retrofit建立關聯
ICallFoodService service = retrofit.create(ICallFoodService.class);
//4.調用接口的方法,獲得一個請求對象
Call<FoodInfo> call = service.callFoodInfo("food", 1, 20, 0);
//5.發送一個異步請求
call.enqueue(new Callback<FoodInfo>() {
@Override
public void onResponse(Call<FoodInfo> call, Response<FoodInfo> response) {
List<FoodInfo.TngouBean> tngou = response.body().getTngou();
}
@Override
public void onFailure(Call<FoodInfo> call, Throwable t) {
}
});
}
}
POST請求只需要把接口中的GET改爲POST即可,當需要表單提交數據時,使用@FormUrlEncoded和@Field
簡單JSON和XML解析邏輯一樣,就是轉換器不一樣而已
如果Retrofit提供的轉化器沒有你需要的,你需要創建一個繼承自Converter.Factory的類並且在構建適配器的時候加入到實例裏面。
3.Retrofit的常用註解
Retrofit 中有許多用到註解的地方,先來了解他們的用法和作用
請求方法相關:
@GET、@POST、
分別是get和post請求。括號裏面的value值與上面.baseUrl組成完整的路徑
@Headers、@Header
當設置網絡請求的 Header 參數:
1.靜態配置,直接在接口中指定 Header 參數:
@Headers({
"User-Agent: Retrofit-Sample-App"
})
2.動態配置:
@GET("/user")
Call<TestModel> getUser(@Header("Authorization") String authorization)
@PUT、
@DELETA、
@PATCH,
參數相關:
GET 請求參數設置(參數是放在url當中)
@Query
請求參數。無論是GET或POST的參數都可以用它來實現
Call<TestModel> one(@Query("username") String username);
@QueryMap
請求參數使用Map集合。可以傳遞一個map集合對象
Call<TestModel> many(@QueryMap Map<String, String> params);
POST 請求參數設置(POST 請求的參數是放在請求體內的)
@Body
實體請求參數。顧名思義可以傳遞一個實體對象來作爲請求的參數,不過實體屬性要與參數名一致
Call<TestModel> post(@Body User user);
這裏的 User 類型是需要我們去自定義的:
public class User {
public String username;
public String password;
public User(String username,String password){
this.username = username;
this.password = password;
}
最後在獲取請求對象時:
User user = new User("lgd","123456");
Call<TestModel> model = service.post(user);
@Path、
動態的URL訪問。eg:get請求中的{user}可以把它當做一個佔位符,通過@Path("user")標註的參數進行替換
通過表單來提交數據(表單編碼和多part)
@FormUrlEncoded和@Field
簡單的表單鍵值對。兩個需要結合使用,使用如下:
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("username") String username,@Field("password") String password);
4.文件下載
靜態地址
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();//ResponseBody作爲返回類型
動態地址
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
代碼:
private void downFile() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://10.254.8.146:8080")
.build();
ICallDownFileService service = retrofit.create(ICallDownFileService.class);
Call<ResponseBody> call = service.callDownFile();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//將文件存儲到本地
serializeFile(response.body());
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
/**
* 讀取這個responsebody字節流,並存儲到本地
*
* @param response
*/
private void serializeFile(ResponseBody response) {
File file = new File(Environment.getExternalStorageDirectory(), "qq.exe");
OutputStream outputStream = null;
InputStream is = null;
try {
//拿到body的字節流
is = response.byteStream();
outputStream = new FileOutputStream(file);
int len = 0;
byte[] buf = new byte[1024];
while ((len = is.read(buf))!=-1){
outputStream.write(buf,0,len);
}
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
當你需要獲取下載進度的時候,自己需要進行封裝
注意:默認情況下,Retrofit在處理結果前會將整個Response讀進內存,這在JSON或者XML等表現還算良好,但如果是一個非常大的文件,就可能造成OutofMemory異常。
如果你的應用需要下載略大的文件,請使用@Streaming
@Streaming//立刻傳遞字節碼,而不需要把整個文件讀進內存
@GET
Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);
當使用@Streaming註解時,如果在主線程中進行存儲文件,將會拋出android.os.NetworkOnMainThreadException異常。
因此,最後一步就是把這些操作放進一個單獨的工作線程中
請用Okhttp實現下載
5.文件上傳
1.單文件上傳攜帶參數(@Multipart和@Part)
//接口
@Multipart//@MultiPart允許使用多個part
@POST("UploadServlet")
/**
* 第一個我們準備上傳個文件,使用了MultipartBody.Part類型,其餘兩個均爲簡單的鍵值對。
*/
Call<ResponseBody> uploadfile(@Part MultipartBody.Part photo, @Part("username") RequestBody username, @Part("password") RequestBody password);
//Activity
Retrofit retrofitUpload = new Retrofit.Builder()
.baseUrl("http://192.168.1.8:8080/UploadFile/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofitUpload.create(GitHubService.class);
File file = new File(Environment.getExternalStorageDirectory()+"/Pictures", "xuezhiqian.png");
//設置Content-Type:application/octet-stream
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
//設置Content-Disposition:form-data; name="photo"; filename="xuezhiqian.png"
MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);
//添加參數用戶名和密碼,並且是文本類型
RequestBody userName = RequestBody.create(MediaType.parse("text/plain"), "abc");
RequestBody passWord = RequestBody.create(MediaType.parse("text/plain"), "123");
Call<ResponseBody> loadCall = service.uploadfile(photo, userName,passWord);
loadCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.e("APP", response.body().source().toString());
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});
2.多文件上傳攜帶參數(使用註解@PartMap和@Part)
@Multipart
@POST("UploadServlet")
Call<ResponseBody> uploadfile(@PartMap Map<String, RequestBody> params, @Part("password") RequestBody password);
Retrofit retrofitUpload = new Retrofit.Builder()
.baseUrl("http://192.168.1.8:8080/UploadFile/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofitUpload.create(GitHubService.class);
File file = new File(Environment.getExternalStorageDirectory()+"/Pictures", "xuezhiqian.png");
File file2 = new File(Environment.getExternalStorageDirectory()+"/Pictures", "xuezhiqian2.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file2);
RequestBody userName = RequestBody.create(MediaType.parse("text/plain"), "abc");
RequestBody passWord = RequestBody.create(MediaType.parse("text/plain"), "123");
Map<String,RequestBody> photos = new HashMap<>();
//添加文件一
photos.put("photos\"; filename=\""+file.getName(), photoRequestBody);
//添加文件二
photos.put("photos\"; filename=\""+file2.getName(), photoRequestBody2);
//添加用戶名參數
photos.put("username", userName);
Call<ResponseBody> loadCall = service.uploadfile(photos, passWord);
loadCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.e("APP", response.body().source().toString());
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});