Retrofit簡單入門

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();
    }
});
發佈了27 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章