Retrofit入門[簡單使用方式介紹哦]
阿新 • • 發佈:2018-12-18
一直用OkHttp感覺也沒什麼毛病,空閒時間看看Retrofit怎麼使用
本篇僅介紹最基本的Retrofit用法,如有錯誤請提出,我會虛心請教並改正,謝謝
一、介面
想必有所瞭解的同學都知道Retrofit是基於介面來進行請求,如下
開門見山貼程式碼
interface RetrofitApi { /* 1. 僅請求伺服器資料 */ @GET(".") fun register(): Call<String> /* 2. 上傳引數 */ @GET("hi/register") fun register(@QueryMap
javabean資料類User如下
data class User(val password: String, val username: String)
做個解釋
上面的第三個上傳json(或者有需求是上傳xml的,這裡沒有成功實現,如有需要的同學聯絡我,然後我會盡快待補)
注意事項程式碼裡都加註釋了,再次說明幾點
有@Part的地方基本都要加@Multipart
有@PartMap的地方最好加@JvmSuppressWildcards,否則會出錯(註釋有)
下載檔案時Retrofit會將檔案流整個讀進記憶體,避免OOM需新增@Streaming
二、具體的請求
剛開始,多寫幾遍最基本的請求步驟,加強記憶,不急著封裝啥的
程式碼貼上
object RetrofitRequest { @JvmStatic fun main(args: Array<String>) { // register() // registerWithParams() // postJson() // uploadFile() // uploadFiles() download() } private fun register() { // 先來個模子 -> 給模子加花 -> 通過模子刻個例項出來 val retrofit = Retrofit.Builder() // 建立客戶端建造器 .baseUrl("http://127.0.0.1:8080/hi/register/") // 新增主機地址 .addConverterFactory(ScalarsConverterFactory.create()) // 制定資料解析器[可用gson、jackson等或自定製] .build() // 建立客戶端例項 val api = retrofit.create(RetrofitApi::class.java) // 獲取Api例項 val call = api.register() // 通過Api例項獲取一個Call物件 call.enqueue(object : Callback<String> { // 開始請求[enqueue(非同步) | execute(同步)] override fun onFailure(call: Call<String>?, t: Throwable?) { println("請求失敗: ${t.toString()}") } override fun onResponse(call: Call<String>?, response: Response<String>?) { println("請求成功: ${response?.body()}") } }) } private fun registerWithParams() { val retrofit = Retrofit.Builder() .baseUrl("http://127.0.0.1:8080/") .addConverterFactory(ScalarsConverterFactory.create()) .build() val api = retrofit.create(RetrofitApi::class.java) val call = api.register(mapOf("username" to "catface", "password" to "root")) // 新增請求引數map call.enqueue(object : Callback<String> { override fun onResponse(call: Call<String>?, response: Response<String>?) { println("請求成功: ${response?.body()}") } override fun onFailure(call: Call<String>?, t: Throwable?) { println("請求失敗: ${t.toString()}") } }) } // 僵硬中... private fun postJson() { val retrofit = Retrofit.Builder() .baseUrl("http://127.0.0.1:8080/") .addConverterFactory(GsonConverterFactory.create()) .build() val api = retrofit.create(RetrofitApi::class.java) val call = api.postJson(User("root", "catface")) call.enqueue(object : Callback<String> { override fun onFailure(call: Call<String>?, t: Throwable?) { println("請求失敗: ${t.toString()}") } override fun onResponse(call: Call<String>?, response: Response<String>?) { println("請求成功: ${response?.body()}") } }) } private fun uploadFile() { val retrofit = Retrofit.Builder() .addConverterFactory(ScalarsConverterFactory.create()) .baseUrl("http://127.0.0.1:8080/hi/uploadFileByJava/") .build() val api = retrofit.create(RetrofitApi::class.java) val file = File("D:/下載/圖片/ic_launcher.png") val requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file) val fileBody = MultipartBody.Part.createFormData("file", file.name, requestBody) val description = RequestBody.create(MediaType.parse("multipart/form-data"), "This is a description") val call = api.uploadFile(description, fileBody) call.enqueue(object : Callback<String> { override fun onResponse(call: Call<String>, response: Response<String>) { println("請求成功: ${response.body().toString()}") } override fun onFailure(call: Call<String>, t: Throwable) { println("請求失敗: ${t.toString()}") } }) } private fun uploadFiles() { val retrofit = Retrofit.Builder() .baseUrl("http://127.0.0.1:8080/hi/uploadFileBySpringAndFileName/") .addConverterFactory(ScalarsConverterFactory.create()) .build() val api = retrofit.create(RetrofitApi::class.java) /* 檔案 */ val fileMap = HashMap<String, RequestBody>() val file1 = File("D:/下載/圖片/ic_launcher.png") val file2 = File("D:/下載/圖片/girl01.jpg") fileMap.put("file1\"; filename=\"" + file1.name, RequestBody.create(MediaType.parse("multipart/form-data"), file1)) fileMap.put("file2\"; filename=\"" + file2.name, RequestBody.create(MediaType.parse("multipart/form-data"), file2)) /* 引數 */ val map = HashMap<String, String>() map.put("username", "catface") map.put("password", "root") val call = api.uploadFiles(fileMap, map) call.enqueue(object : Callback<String> { override fun onResponse(call: Call<String>?, response: Response<String>?) { println("請求成功: ${response?.body()}") } override fun onFailure(call: Call<String>?, t: Throwable?) { println("請求失敗: ${t.toString()}") } }) } private fun download() { val retrofit = Retrofit.Builder() .baseUrl("http://127.0.0.1:8080/hi/") .addConverterFactory(ScalarsConverterFactory.create()) .build() val api = retrofit.create(RetrofitApi::class.java) val call = api.download() call.enqueue(object : Callback<ResponseBody> { override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) { println("請求成功") if (response!!.isSuccessful) { writeResponseBodyToDisk(response.body()!!) } } override fun onFailure(call: Call<ResponseBody>?, t: Throwable?) { println("請求失敗: ${t.toString()}") } }) } /* 將流寫到本地的工具 */ fun writeResponseBodyToDisk(body: ResponseBody): Boolean { try { val futureStudioIconFile = File("d:/a_test_dir/Future Studio Icon.jpg") var inputStream: InputStream? = null var outputStream: OutputStream? = null try { val fileReader = ByteArray(4096) val fileSize = body.contentLength() var fileSizeDownloaded: Long = 0 inputStream = body.byteStream() outputStream = FileOutputStream(futureStudioIconFile) while (true) { val read = inputStream!!.read(fileReader) if (read == -1) { break } outputStream.write(fileReader, 0, read) fileSizeDownloaded += read.toLong() println("file download: $fileSizeDownloaded of $fileSize") } outputStream.flush() return true } catch (e: IOException) { return false } finally { if (inputStream != null) { inputStream.close() } if (outputStream != null) { outputStream.close() } } } catch (e: IOException) { return false } } }
做個解釋
在資料解析器方面,我覺得直接返回json串後自己使用gson或者fastjson解析,又清楚也不麻煩,不太喜歡在構造Retrofit例項的時候就固定了解析方式,當然如果為了方便專案需求需要加也沒問題
步驟大致就是:建立Retrofit例項,然後通過介面定義的請求得到Api例項,通過這個例項得到對應的Call物件,最後進行具體的請求
三、服務端
為了方便同學們瞭解服務端是怎麼接收檔案和引數的,在此貼出我寫的完整的服務端小demo,可做大家入門案例,灰常簡單,且功能很全,能接收客戶端傳來的各種引數、檔案,而且能返回給客戶端字串、json串、還能提供檔案給客戶端進行下載,直接看,不用害怕
Controller程式碼貼上
@Controller @RequestMapping("hi") public class HiController { /******************************************** 與客戶端進行入參出參聯絡 ********************************************/ /** * 1. 接收引數 --> 返回字串 */ @RequestMapping(value = "/register", produces = "application/json; charset=utf-8") @ResponseBody public String register(HttpServletRequest request) { System.out.println("介面開始執行-->register"); Map<String, String[]> map = request.getParameterMap(); if (null == map || map.size() < 1) return "本次請求未上傳任何引數"; String result = ""; for (String key : map.keySet()) { System.out.println(key + "-" + map.get(key)[0]); result += "\r\n" + key + "-" + map.get(key)[0]; } return result; } /** * 接收json串 */ @RequestMapping(value = "/postJson", method = RequestMethod.POST, produces = "application/json; charset=utf-8") @ResponseBody public String postJson(@RequestBody Map<String, String> map) throws IOException { System.out.println("介面開始執行-->postJson"); if (null == map || map.size() < 1) return "本次請求未上傳任何引數"; String result = ""; for (String key : map.keySet()) { System.out.println(key + "-" + map.get(key)); result += "\r\n" + key + "-" + map.get(key); } return result; } /** * 2. 接收引數 --> 返回json */ @RequestMapping(value = "/login", produces = "application/json; charset=utf-8") @ResponseBody public String login(@RequestParam("username") String username, @RequestParam("password") String password) throws IOException { System.out.println("介面開始執行-->login"); /* 接收入參 */ System.out.println(username + " || " + password); /* 提供出參[json格式] */ Map<String, String> map = new HashMap<String, String>(); map.put("username", "測試中文 your username is: " + username); map.put("password", "測試中文 your password is: " + password); /* 返回json給客戶端 */ return JSON.toJSONString(map); } @RequestMapping(value = "/receiveArr") public void receiveArr(@RequestParam("ids[]") String[] ids) { System.out.println("介面開始執行-->receiveArr"); for (String id : ids) { System.out.println(id); } } /******************************************** 客戶端上傳檔案 ********************************************/ /** * ↓通過流儲存上傳的檔案(耗時較長) * * @param file 將檔案封裝成CommonsMultipartFile */ @RequestMapping("/uploadFileByStream") @ResponseBody public String uploadFileByStream(@RequestParam("file") CommonsMultipartFile file) throws IOException { System.out.println("介面開始執行-->uploadFileByStream"); OutputStream os = new FileOutputStream("d:/" + file.getOriginalFilename()); InputStream is = file.getInputStream(); int temp; while ((temp = is.read()) != -1) { os.write(temp); } os.flush(); os.close(); is.close(); return "uploadFileByStream suc..."; } /** * ↓通過Java API: file.transferTo()儲存上傳的單個檔案 */ @RequestMapping("/uploadFileByJava") @ResponseBody public String uploadFileByJava(@RequestParam("file") CommonsMultipartFile file) throws IOException { System.out.println("介面開始執行-->uploadFileByJava"); file.transferTo(new File("d:/" + file.getOriginalFilename())); return "uploadFileByJava suc... "; } /** * ↓通過Spring API儲存上傳的多個檔案 */ @RequestMapping("/uploadFilesBySpring") @ResponseBody public String uploadFilesBySpring(HttpServletRequest request) throws IOException { System.out.println("介面開始執行-->uploadFilesBySpring"); String desPath; CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); if (multipartResolver.isMultipart(request)) { MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; Iterator<String> iterator = multiRequest.getFileNames(); while (iterator.hasNext()) { MultipartFile file = multiRequest.getFile(iterator.next()); if (null != file) { desPath = "d:/tt/" + file.getOriginalFilename(); file.transferTo(new File(desPath)); } } } return "uploadFilesBySpring suc..."; } /** * →接收檔案map & 請求引數map← */ @RequestMapping(value = "/uploadFileBySpringAndFileName", method = RequestMethod.POST, produces = "application/json; charset=utf-8") @ResponseBody public String uploadFileBySpringAndFileName(HttpServletRequest request) throws IOException { System.out.println("介面開始執行-->uploadFileBySpringAndFileName"); /* 1. 獲取所有引數 */ Map<String, String[]> parameterMap = request.getParameterMap(); System.out.println("引數數量: " + parameterMap.size()); // 入引數量 for (String key : parameterMap.keySet()) { System.out.println(key + ":" + parameterMap.get(key)[0]); } /* 2. 獲取所有檔案 */ CommonsMultipartResolver multiResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); if (multiResolver.isMultipart(request)) { MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; MultiValueMap<String, MultipartFile> multiFileMap = multiRequest.getMultiFileMap(); System.out.println("檔案數量: " + multiFileMap.size()); // 入引數量 for (String key : multiFileMap.keySet()) { System.out.println(key + " : " + multiFileMap.get(key).get(0).getOriginalFilename()); multiFileMap.get(key).get(0).transferTo(new File("d:/a_test_dir/" + multiFileMap.get(key).get(0).getOriginalFilename())); } /*Iterator<String> iterator = multiRequest.getFileNames(); while (iterator.hasNext()) { Map<String, MultipartFile> fileMap = multiRequest.getFileMap(); MultipartFile file = fileMap.get("file"); System.out.println(file.getOriginalFilename()); }*/ } return "uploadFileBySpringAndFileName suc..."; } /* 新增一個網上的小demo,沒啥用,隨便看看 */ @RequestMapping("/uploadFiles") @ResponseBody public String uploadFiles(HttpServletRequest request) throws IOException { System.out.println("介面開始執行-->uploadFiles"); //建立一個通用的多部分解析器 CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); //判斷 request 是否有檔案上傳,即多部分請求 if (multipartResolver.isMultipart(request)) { //轉換成多部分request MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; //取得request中的所有檔名 Iterator<String> iter = multiRequest.getFileNames(); while (iter.hasNext()) { //記錄上傳過程起始時的時間,用來計算上傳時間 int pre = (int) System.currentTimeMillis(); //取得上傳檔案 MultipartFile file = multiRequest.getFile(iter.next()); if (file != null) { //取得當前上傳檔案的檔名稱 String myFileName = file.getOriginalFilename(); //如果名稱不"",說明該檔案存在,否則說明該檔案不存在 if (myFileName.trim() != "") { System.out.println(myFileName); //重新命名上傳後的檔名 String fileName = "demoUpload" + file.getOriginalFilename(); //定義上傳路徑 String path = "D:/a_test_dir/" + fileName; File localFile = new File(path); file.transferTo(localFile); } } //記錄上傳該檔案後的時間 int finaltime = (int) System.currentTimeMillis(); System.out.println(finaltime - pre); } } return "/success"; } /******************************************* 向頁面輸出驗證碼png ********************************************/ @RequestMapping("/verificationCode") public void verificationCode(HttpServletResponse response, HttpSession session) throws IOException { System.out.println("介面開始執行-->verificationCode"); Object[] objs = ImageUtil.createImage(); session.setAttribute("imgCode", objs[0]); BufferedImage img = (BufferedImage) objs[1]; response.setContentType("image/png"); OutputStream os = response.getOutputStream(); ImageIO.write(img, "png", os); } /******************************************* 向客戶端提供檔案下載 ********************************************/ @RequestMapping("/downloadFile") public ResponseEntity<byte[]> downloadFile() throws IOException { System.out.println("介面開始執行-->downloadFile"); File file = new File("C:\\Users\\Administrator\\Desktop\\temp.jpg"); HttpHeaders headers = new HttpHeaders(); headers.setContentDispositionFormData("attachment", "temp.jpg"); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentLength(file.length()); System.out.println(file.length() + " is length,.."); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED); } }
做個解釋
伺服器就基於SSM(SpringMVC+Spring+Mybatis)
新手學習中,本篇未涉及難點和概念部分,即看完能夠在專案中使用
- 如有關於Retrofit和服務端的問題,請留下回復,一起交流