編輯:關於Android編程
在學習 Retrofit2 的過程中受到了一些阻力,現 Retrofit2 學會使用了,特此寫此文驗證所學知識。同時也希望幫助和我一樣在學習Retrofit2遇到困難的猿們。
當我在剛開始學習 Retrofit2 的時候並不知道Retrofit2是什麼東西,後來逐漸了解 “它可能是一個方便我們網絡請求的庫 ,可以幫我們讓請求網絡變得更靈活、易於維護”。然後還可以和時下比較火熱的RxJava進行完美融合。
先看看如何使用,如何進行一個簡單的Get/Post請求
首先在build.gradle中添加如下代碼,添加Retrofit2庫
compile 'com.squareup.retrofit2:retrofit:2.1.0'
有的教程裡寫要手動添加okhttp的庫,其實是不需要的,因為retrofit2封裝了okhttp,不信自己編譯下看看:
添加完庫,我們開始正文。
我們在項目中進行網絡請求時,肯定不是一個地址吧,那麼這些請求地址存放在哪呢?是在哪個類裡請求就在哪個類裡存放,還是統一放在一個專門存地址的類中呢?
我在學習Android期間就是哪裡有請求就放哪裡,後來有人告訴我要集中存放。於是後來就建立一個AppURL.java所有地址都存放這裡。
然而Retrofit2這裡也可以這麼理解:專門有一個‘地方’來存儲鏈接地址(也可以創建多個‘地方’存儲)。這個‘地方’不是類而是接口,在這個接口中可以設定請求地址的一些信息。就像這樣:
public interface AppURL { @GET("index") CallgetIndex(); ... ... // 可以存儲多個連接地址 }
說明:
接口名“AppURL ”可以隨意定義,根據自己喜好。
第一行:代表get請求,請求地址為“設定的BaseURL/index” (BaseURL設定在下面介紹如何設定)
第二行:getIndex是方法名;Call是默認返回類型,暫且不要管能干什麼。
下面我們看下如何使用這些地址進行網絡請求:
1. 創建Retrofit對象,並設定BaseURL
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://www.這裡是BaseURL.com/") .build();
獲取“AppURL”對象(創建請求服務)需要注意的是BaseURL必須以‘/’結尾
AppURL url= retrofit.create(AppURL.class);用AppURL對象得到具體請求對象(獲取請求服務方法 )
Callcall = getIndex.getIndex();
開始(異步)請求後期也會在這一步中進行設置鏈接參數、請求頭等
call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { Log.e("tag", response.body().toString());//獲得數據 } @Override public void onFailure(Call call, Throwable t) { Log.e("tag", t.getMessage());//請求失敗 } });
好了,現在一個簡單的網絡請求就寫完了。不是很難吧(當時我可是覺得挺難 ^_^)
單單會這些是遠遠不夠的,那麼我們如何來滿足項目中各種各樣的需求呢?請繼續看
其實在Retrofit2中,我們不用自己來解析數據,Retrofit2可以幫我們自動解析,怎麼做呢?請看:
0. 添加
在Retrofit2中是用Gson解析的,所以我們要在build.gradle中添加。
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
有寫教程說還有添加gosn庫,經過測試是不需要的,converter-gson中已經封裝了gson庫。
需要注意的是converter-gson和retrofit版本號應為一致,在這裡我都用2.1.0
1. 創建Bean
創建一個JavaBean,用於解析服務器返回數據。就和我們平常自己解析創建的Bean一樣,推薦用As的插件JsonFormat,挺方便的。
我們創建一個Bean起名為MBean.java(隨便起的)
2. 為retrofit添加addConverterFactory
添加後的代碼如下:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://www.這裡是BaseURL.com/") .addConverterFactory(GsonConverterFactory.create()) .build();修改泛型
//在AppURL 接口中修改: @GET("index") CallgetIndex(); Call call = url.getIndex(); //調用服務請求時的修改 call.enqueue(new Callback () { @Override public void onResponse(Call call, Response response) { Log.e("tag", response.body().toString());//解析好的數據 } @Override public void onFailure(Call call, Throwable t) { Log.e("tag", t.getMessage()); } });
這樣輸出的就直接是用Gson解析好的MBean數據了。
retrofit不僅僅只支持gson,還支持其他許多json解析庫。 如:
Jackson、Moshi、Protobuf、Wire、Simple XML、Scalars (primitives, boxed, and String) 具體請看官網
說到固定地址了,那麼固定地址長什麼樣呢?
http://www.BaseURL.com/index http://www.BaseURL.com/user http://www.BaseURL.com/login http://www.BaseURL.com/register/qq http://www.BaseURL.com/register/wechat
那麼應該如何請求呢?除了上面例子中的寫法還可以這樣寫:
@請求類型("{name}") Call<返回類型> 方法名(@path("name") String name); 如: @GET("{name}") Callget(@Path("name") String urlName); //如果想訪問登錄的鏈接,在使用時就直接url.get("login");就可以。這樣請求的地址就是http://www.BaseURL.com/login 是不是很方便 //注意@Path和{}中的參數名要一致
@Path的應該作用暫且理解為 為上面的GET請求傳值吧
帶參地址長這樣子:
www.BaseURL.com/login?page=1 www.BaseURL.com/movieTop?start=1&count=5 www.BaseURL.com/login?username=testuser&password=123456
上個用的是@Path,這回用的是@Query其實和@Path一樣
直接看例子:
@GET("movieTop") Callget(@Query("start") int start, @Query("count") int count); //假設想查詢電影排行榜的第1-5名,則使用時候是這樣: Call call = url.get(1, 5); //請求的地址是這樣:www.BaseURL.com/movieTop?start=1&count=5
使用@Body來聲明即可,如下:
@POST("/aaa") Callsend( @Body UserInfo body); //使用 Call call=url.send(); 這裡的UserInfo就是要發送的實體,Retrofit2 會自動轉成Gson
學到這裡,一般的網絡請求都可以了進行,可以應付一陣子了。
還有一些要求較高的請求,請看下節。
如果看到這裡,相信對Retrofit2的基本請求會用了,那麼這節就說一說其他的網絡請求。
我們可以使用@FormUrlEncoded注解來發送表單數據。使用 @Field注解和參數來指定每個表單項的Key,value為參數的值。
@FormUrlEncoded @POST("user/login") CallupdateUser(@Field("username") String name, @Field("password") String pass);
@Multipart @POST("register") CallregisterUser( @Part MultipartBody.Part headPhoto, @Part("username") RequestBody userName, @Part("password") RequestBody passWord ); //使用 MediaType textType = MediaType.parse("text/plain"); RequestBody name = RequestBody.create(textType, "二傻子"); RequestBody pass = RequestBody.create(textType, "123456"); RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), 文件對象); MultipartBody.Part photo = MultipartBody.Part.createFormData("上傳的key", "文件名.png", photoRequestBody); Call call = url.registerUser(photo,name, pass);
@Part 後面支持三種類型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意類型;
動手測試:username的RequestBody 換成String是否可以
@Multipart @POST("register") CallregisterUser(@PartMap Map params, @Part("password") RequestBody password); //使用 RequestBody photo = RequestBody.create(MediaType.parse("image/png"), 文件對象2); RequestBody photo = RequestBody.create(MediaType.parse("image/png"), 文件對象2); Map photos = new HashMap<>(); photos.put("對應的key1"; filename=\"文件名1.png", photo1); photos.put("對應的key2"; filename=\"文件名2.png", photo2); photos.put("username", RequestBody.create(null, "二傻子")); Call call = userBiz.registerUser(photos, RequestBody.create(null, "123456"));
也可以都塞Map裡上傳,也可以只在Map中上傳文件,隨你喽~
文章結尾有參考鏈接。不一樣的上傳方式。
@GET("地址") @Headers("Accept-Encoding: application/json") Call<返回類型> 方法名(); // 請求結果: // GET 地址 HTTP/1.1 // Accept-Encoding: application/json動態請求頭
@GET("地址") Call<返回類型> 方法名(@Header("Location") String location); //使用 url.方法名("參數"); // 請求結果: // GET 地址 HTTP/1.1 // Location: 參數固定+動態
@GET("地址") @Headers("Accept-Encoding: application/json") Call<返回類型> 方法名(@Header("Location") String location); //使用 url.方法名("參數"); // 請求結果: // GET 地址 HTTP/1.1 // Accept-Encoding: application/json // Location: 參數
注意XXXMap的使用, 比如@PathMap,@FieldMap等,具體怎麼使用,可以自己研究,研究不出來的可以參考結尾處的文章。
下載文件得說說,在Retrofit2中下載文件是默認存儲到緩存中,也就是說不能進行大的文件下載,如果要下載大文件要用 @streaming 。但話說回來了,下載文件我們可以不用Retrofit2啊,直接用okhttp不就得啦
我們是可以添加 okhttpclient 到retrofit中去,這樣可以來統一的log管理,給每個請求添加統一的header等,那麼我們沒有添加為什麼沒有報錯呢? 因為在build()方法中會判斷是否為空,如果我們沒有添加okhttpclient 則就是空了,那麼retrofit會自動給我們添加了一個new OkHttpClient();
execute是同步執行 需要在子線程中執行、enqueue是異步執行。 看下我這幾個圖,整理一下思路吧以上表格中的除HTTP以外都對應了HTTP標准中的請求方法,而HTTP注解則可以代替以上方法中的任意一個注解,有3個屬性:method、path、hasBody,public interface BlogService { /** * method 表示請的方法,不區分大小寫 * path表示路徑 * hasBody表示是否有請求體 */ @HTTP(method = "get", path = "blog/{id}", hasBody = false) CallgetFirstBlog(@Path("id") int id); }
注1:{占位符}和PATH盡量只用在URL的path部分,url中的參數使用Query和QueryMap 代替,保證接口定義的簡潔
注2:Query、Field和Part這三者都支持數組和實現了Iterable接口的類型,如List,Set等,方便向後台傳遞數組。
Callfoo(@Query("ids[]") List ids); //結果:ids[]=0&ids[]=1&ids[]=2
首先說下如何和當前火熱的RxJava進行配合使用。
引入RxJava支持 (版本號要一致)compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'寫接口
@GET(" ^_^ ") Observable通過RxJavaCallAdapterFactory為Retrofit添加RxJava支持get();
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://www.BaseURL.com/") .addConverterFactory(GsonConverterFactory.create())//自動通過Gson轉josn,上面有提到 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加RxJava支持 .build();使用
url.get() .subscribeOn(Schedulers.io()) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(MBean mBean) { } });
根據需要添加RxAndroid,這個版本號沒有要求。
剩下的內容講的主要是進行自定義 Converter 和 自定義CallAdapter。還有就是源碼的解析。
這裡可以參考結尾處的鏈接,不獻丑了。
在做項目的時候,因為要用到我們自動獲取聯系人的姓名和電話,就想到了ContentProvider分享數據的功能,這樣做既節省了時間,也減少了我們輸入錯誤號碼的幾率,所以,
效果圖思路:就是先設置Gridlayout的行列數,然後往裡面放置一定數目的自定義日歷按鈕控件,最後實現日歷邏輯就可以了。步驟:第一步:自定義日歷控件(初步)第二步:實現
通過前面幾篇博客,我們能夠自定義出一些比較簡單的自定義控件,但是這在實際應用中是遠遠不夠的,為了實現一些比較牛X的效果,比如側滑菜單、滑動卡片等等,我們還需要了解自定義V
不少玩家會想到將圖片拷貝到電腦中,然後用PS等工具去標注編輯加工,然在再發送到朋友圈或社交平台。那麼,轉來轉去,是不是也不太方便呢?其實Android手機也