Retrofit 基本使用教程

原文地址:

https://futurestud.io/blog/retrofit-getting-started-and-android-client#

部分內容,根據自己的理解添加或者修改

URL拼接

Retrofit 在初始化的時候,需要指定一個baseUrl:

 private static Retrofit.Builder mBuilder = new Retrofit.Builder()
            .baseUrl("http://192.168.0.102/")
            .addConverterFactory(GsonConverterFactory.create());

在我們定義請求的接口時,會傳入具體的接口的地址(Endpoint):

 @GET("index.php?c=User&m=getUser")
    Call<List<User>> getUser();

Retrofit會幫我們完成拼接,最後的URL是:

http://192.168.0.102/index.php?c=User&m=getUser

需要注意的是baseUrl必須以”/”結尾

正確的寫法:

Base URL: http://example.com/api/
Endpoint: foo/bar/
Result: http://example.com/api/foo/bar/

錯誤寫法:

Base URL: http://example.com/api
Endpoint: foo/bar/
Result: http://example.com/foo/bar/

Endpoint可以爲完整的URL:

  Base URL: http://example.com/
  Endpoint: https://github.com/square/retrofit/
  Result: https://github.com/square/retrofit/

當我們需要請求不同服務器上的API時,這就非常有用了。

另外,Retrofit還支持我們在調用時,傳入URL:

@GET
Call<List<User>> getUser2(@Url String url);
Call<List<User>> call=userClient.getUser2("http://www.jiangxu.online/index.php?c=User&m=getUser");

Get查詢參數

查詢參數是一種很常見的客戶端往服務端傳遞數據的方式,比如我們需要傳一個id給服務端,那麼URL可能是這樣的:

https://api.example.com/tasks?id=123

Retrofit 定義實現查詢參數:

public interface TaskService {  
    @GET("/tasks")
    Call<Task> getTask(@Query("id") long taskId);
}

方法getTask需要傳入taskId,這個參數會被映射到@Query(“id”)中的id中,最後的URL會變成這樣:

/tasks?id=<taskId>

有時我們需要對於一個id參數傳入多個值,比如這樣:

https://api.example.com/tasks?id=123&id=124&id=125  

對於這樣的需求,Retrofit 通過傳入一個List來實現:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}

這樣,拼接後的URL就是我們需要的那樣。

有時,我們在給服務端傳遞數據時,有些參數是可選的,比如下面的URL:

https://your.api.com/tasks?sort=value-of-order-parameter

sort參數是可選的,有些情況下可能不需要傳入。

接口的定義:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTasks(@Query("sort") String order);
}

那麼,在我們不想添加排序控制的時候,我們可以傳入null,Retrofit 會忽略值爲null的參數。

service.getTasks(null);  

需要注意的是,可忽略參數的參數類型不能是int, float, long這些基本類型,應該用Integer, Float, Long來代替。

Post 提交參數

Retrofit Post方式提交表單形式的參數需要添加標記@FormUrlEncoded,通過@Field註釋添加鍵值對。
接口定義:

public interface UserClient {
    @FormUrlEncoded
    @POST("/index.php?c=User&m=login")
    Call<List<User>> login(@Field("phone") String phone, @Field("password") String password);
}

客戶端調用:

 private void login() {
        UserClient userClient = ServiceGenerator.createService(UserClient.class);
        Call<List<User>> call = userClient.login("13695378745","123456");
        call.enqueue(new Callback<List<User>>() {

        });
    }

Post 提交JSON數據

有時提交的數據量比較大時,用鍵值對的方式提交參數不太方便,Retrofit可以通過@Body註釋,直接傳遞一個對象給請求主體,Retrofit通過JSON轉化器,把對象映射成JSON數據。

接口定義:

public interface TaskService {  
    @POST("/tasks")
    Call<Task> createTask(@Body Task task);
}

傳遞實體需要有Model:

public class Task {  
    private long id;
    private String text;

    public Task() {}
    public Task(long id, String text) {
        this.id = id;
        this.text = text;
    }
}

客戶端調用:

Task task = new Task(1, "my task title");  
Call<Task> call = taskService.createTask(task);  
call.enqueue(new Callback<Task>() {}); 

這樣,服務端得到的是JOSN數據:

{
    "id": 1,
    "text": "my task title"
}

文件下載

接口定義:

 @Streaming
    @GET
    Call<ResponseBody> downloadFile(@Url String fileUrl);

在下載的時候,是在子線程中工作運行的,有時需要顯示下載的進度,同時支持多個下載併發進行,這樣需要封裝一下,使用起來方便:

public class DownFileManager {

    private final static String TAG = "DownFileManager";
    private static DownFileManager sDownFileManager;
    private CallBack mCallBack;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private ExecutorService executorService;

    public interface CallBack {

        void onSuccess(String path);

        void inProgress(int progress);

        void onFail();
    }

    public static DownFileManager getInstance() {
        if (sDownFileManager == null) {
            sDownFileManager = new DownFileManager();
        }
        return sDownFileManager;
    }

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("DownFileManager", false));
        }
        return executorService;
    }

    public void download(final String path, final String filePath, CallBack callBack) {
        mCallBack = callBack;
        executorService();
        executorService.submit(new Runnable() {
            boolean isSuccess;

            @Override
            public void run() {
                DownloadService service = ServiceGenerator.createService(DownloadService.class);
                Call<ResponseBody> call = service.downloadFile(path);
                try {
                    isSuccess = writeResponseBodyToDisk(filePath, call.execute().body());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (isSuccess) {
                            mCallBack.onSuccess(filePath);
                        } else {
                            mCallBack.onFail();
                        }
                    }
                });
            }
        });
    }

    private boolean writeResponseBodyToDisk(String path, ResponseBody body) {
        try {
            File futureStudioIconFile = new File(path);
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];

                long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;

                inputStream = body.byteStream();
                outputStream = new FileOutputStream(futureStudioIconFile);
                int oldProgress = 0;
                while (true) {
                    int read = inputStream.read(fileReader);

                    if (read == -1) {
                        break;
                    }
                    outputStream.write(fileReader, 0, read);
                    fileSizeDownloaded += read;
                    final int progress = (int) ((fileSizeDownloaded * 1.0 / fileSize) * 100);
                    if (oldProgress != progress) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mCallBack.inProgress(progress);
                            }
                        });
                    }
                    oldProgress = progress;
                }
                outputStream.flush();
                return true;
            } catch (IOException e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            return false;
        }
    }

}

使用:

 DownFileManager.getInstance().download(downloadUrl, filePath, new DownFileManager.CallBack() {
            @Override
            public void onSuccess(String path) {
                //下載文件的路徑
            }

            @Override
            public void inProgress(int progress) {
                //下載的進度
                Trace.i("DownFileManager","progress  ="+progress);
            }

            @Override
            public void onFail() {
            }
        });

文件上傳

Retrofit 支持 Multipart 請求,所以我們可以用它來實現文件上傳,對於要實現文件上傳的接口,需要添加@Multipart註釋。
接口定義:

public interface FileUploadService {
    @Multipart
    @POST("index.php?c=Upload&m=doUpload")
    Call<Msg> upload(
            @Part("file\"; filename=\"image.png\" ") RequestBody file,
            @Part("description") RequestBody description);
}

對於要上傳的文件,我們需要把它包裝成RequestBody 對象:

 private void uploadFile( File file) {
        FileUploadService service =
                ServiceGenerator.createService(FileUploadService.class);

        String descriptionString = "hello, this is description speaking";
        RequestBody description =
                RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
        RequestBody requestBody =
                RequestBody.create(MediaType.parse("multipart/form-data"), file);

        Call<Msg> call = service.upload(requestBody, description);
        call.enqueue(new Callback<Msg>() {
            @Override
            public void onResponse(Call<Msg> call, Response<Msg> response) {
                Log.v("Upload", "success");
            }

            @Override
            public void onFailure(Call<Msg> call, Throwable t) {
                Log.e("Upload", t.getMessage());
            }
        });
    }

同步請求和異步請求

同步的請求會在同一個線程中執行,在Android中必須另開線程,否則在主線程中執行會拋出異常。
同步和異步請求的接口都是一樣的,只是調用的時候會不一樣:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTasks();
}

同步請求調用:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
List<Task>> tasks = call.execute().body(); 

異步請求調用:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
call.enqueue(new Callback<List<Task>>() {  
    @Override
    public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
        if (response.isSuccess()) {
            // tasks available
        } else {
            // error response, no access to resource?
        }
    }

    @Override
    public void onFailure(Call<List<Task>> call, Throwable t) {
        // something went completely south (like no internet connection)
        Log.d("Error", t.getMessage());
    }
}

異步請求實現了一個CallBack,包含了兩個回調方法:onResponse和 onFailure,在onResponse中我們可以拿到我們需要的實體數據,在onFailure中,可以拿到錯誤的Log。

Demo地址:

http://download.csdn.net/detail/jiangxuqaz/9452369

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章