Android Retrofit 2.0(二)使用教程OkHttp3 + Gson + RxJava
系列文章推薦:
Android 必須知道的網路請求框架庫,你不可錯過的框架介紹篇
Android Retrofit 2.0(一)初次見面請多多關照
Android Retrofit 2.0(三)從原始碼分析原理
相關資料
新增依賴
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
別忘在Manifest裡新增許可權
<uses-permission android:name="android.permission.INTERNET"/>
定義介面
- http://gank.io/api/data/Android/10/1
- 這是Gank的介面,下面是我們的Json資料。
我們來定義一下我們的介面類 GnakApi:
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
public interface GnakApi {
@GET("api/data/Android/10/1")
Call<ResponseBody> getAndroidInfo();
}
其實,這個介面我們可用這樣去拆分:
我現在不需要返回值,所以我直接傳了一個ResponseBody。上面我用GET請求,直接請求了api/data/Android/10/1,這樣就能和我們的baseUrl拼接了,而後面的引數如果我們要改變的話,那介面方法就要傳參了。
先看下文件上的介面定義:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
可用發現同樣的是GET請求,只不過他的返回值是一個List,型別是repo,repo就是他的實體類,傳了一個path是一個引數,user的引數。
簡單請求
介面定義好,開始請求了。
寫一個button 響應事件,textview顯示返回結果:
<Button
android:id="@+id/btn_requet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/button_bg"
android:text="請求"
android:textAllCaps="false"
android:textColor="@android:color/white"/>
<TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
例項化Retrofit物件
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.build();
GnakApi api = retrofit.create(GnakApi.class);
上面是返回一個GnakApi。通過原始碼我們知道,這裡的create實際上是通過代理的方式拿到的,可以看下:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
接下來,直接去呼叫介面方法,返回一個Call:
Call<ResponseBody> call = api.getAndroidInfo();
到這裡,會奇怪:怎麼和okHttp這麼像啊,如果單純從簡單請求來看,確實有一點像,但是別急,Retrofit可沒這麼簡單,我們有了call之後就直接請求了,一般我們都是非同步請求:
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String result = response.body().string();
Log.i(TAG, result);
tv_result.setText(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
點選執行,檢視結果:
可以看到,已經成功的請求到了結果了,這就是Retrofit的無參簡單請求了。
Retrofit+Gson
聽說 Retrofit + OkHttp + Gson 更配哦。接下來我們新增Gson的Jar之後我們寫一個實體類,也就上面案例請求介面的javaBean,這裡我使用的解析外掛是Gsonformat。
GankBean:
import java.util.List;
public class GankBean {
private boolean error;
private List<ResultsBean> results;
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
public List<ResultsBean> getResults() {
return results;
}
public void setResults(List<ResultsBean> results) {
this.results = results;
}
public static class ResultsBean {
/**
* _id : 5827f41b421aa911d3bb7ecb
* createdAt : 2016-11-13T13:03:23.38Z
* desc : 獨立全端開發的開源小作:簡詩 2.0
* images : ["http://img.gank.io/b6be7a85-4035-437f-9521-65593fdbc48e"]
* publishedAt : 2016-11-14T11:36:49.680Z
* source : web
* type : Android
* url : https://www.v2ex.com/t/320154
* used : true
* who : wingjay
*/
private String _id;
private String createdAt;
private String desc;
private String publishedAt;
private String source;
private String type;
private String url;
private boolean used;
private String who;
private List<String> images;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(String publishedAt) {
this.publishedAt = publishedAt;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isUsed() {
return used;
}
public void setUsed(boolean used) {
this.used = used;
}
public String getWho() {
return who;
}
public void setWho(String who) {
this.who = who;
}
public List<String> getImages() {
return images;
}
public void setImages(List<String> images) {
this.images = images;
}
}
}
用Gson是一鍵生成實體類,還是很方便的。
正常請求
現在實體類GankBean已經有了,那我們重新修改一下介面,讓返回這個實體類:
import retrofit2.Call;
import retrofit2.http.GET;
public interface GnakApi {
@GET("api/data/Android/10/1")
Call<GankBean> getAndroidInfo();
}
呼叫泛型上新增返回的實體類:
Call<GankBean> call = api.getAndroidInfo();
非同步請求:
//非同步
call.enqueue(new Callback<GankBean>() {
@Override
public void onResponse(Call<GankBean> call, Response<GankBean> response) {
GankBean.ResultsBean bean = response.body().getResults().get(0);
tv_result.setText(
"_id:" + bean.get_id() + "\n"
+ "createdAt:" + bean.getCreatedAt() + "\n"
+ "desc:" + bean.getDesc() + "\n"
+ "images:" + bean.getImages() + "\n"
+ "publishedAt:" + bean.getPublishedAt() + "\n"
+ "source" + bean.getSource() + "\n"
+ "type:" + bean.getType() + "\n"
+ "url: " + bean.getUrl() + "\n"
+ "who:" + bean.getWho());
}
@Override
public void onFailure(Call<GankBean> call, Throwable t) {
}
});
這裡就可以直接get出我們想要的值了嗎?答案是否定的。
我們執行之後會報出錯誤日誌:
IllegalArgumentException: Unable to create converter for class com.liuguilin.retrofitsample.GankBean
Could not locate ResponseBody converter for class com.liuguilin.retrofitsample.GankBean
錯誤日誌說明不能建立一個轉換器。我們需要去配置一個Gson,並不是我們的google.gson,我們新增源:
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
配置完後,我們開始使用:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
執行結果:
到這裡,基本請求就已經講完了。只要配置好Retrofit,根據api定義好介面,使用起來比較簡單。然後配合Gson,就可以直接拿值了。如果再能加上比較火的Dagger2註解,程式碼會變得更加的簡潔、高效。
Get請求引數
官網文件:https://www.juhe.cn/docs/api/id/73
官網案例:http://op.juhe.cn/onebox/weather/query?cityname=深圳&key=您申請的KEY
根據以上案例,我們實現一個簡單練習。
預設cityname是深圳,動態設定引數key,先寫一個WeatherApi介面:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface WeatherApi {
@GET("onebox/weather/query?cityname=深圳")
Call<WeatherDataBean> getWeather(@Query("key") String key);
}
上面介面API 動態設定一個引數 key,所以加上 Query 。(如果是兩個動態引數,如 cityname ,就要多加一個引數。)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://op.juhe.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(WeatherApi.class);
例項化Retrofit 配置,然後拿到介面API進行呼叫:
Call<WeatherDataBean> call = api.getWeather("4ea58de8a7573377cec0046f5e2469d5");
//非同步
call.enqueue(new Callback<WeatherDataBean>() {
@Override
public void onResponse(Call<WeatherDataBean> call, Response<WeatherDataBean> response) {
String info = response.body().getResult().getData().getRealtime().getWeather().getInfo();
tv_result.setText("深圳天氣:" + info);
}
@Override
public void onFailure(Call<WeatherDataBean> call, Throwable t) {
}
});
返回的WeatherDataBean 資料太多了,就沒有全顯示出來,這裡就顯示一個資料了,來看執行之後的結果:
上面是Get請求是一般少量引數的介面,但對於特殊引數請求,是有問題的。
比如我們最上面的這個介面:http://gank.io/api/data/Android/10/1 。在最後面有三個引數:
- Android :可接受引數( Android、 iOS 、 休息視訊 、 福利 、 前端 、 App )
- count :最大 50
- page :是頁數
它並不像 cityname 或者 key 的拼接引數,而是直接傳引數:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface GnakApi {
@GET("api/data/Android/10/{page}")
Call<GankBean> getAndroidInfo(@Path("page") int page);
}
如上所示,引數用大括號做佔位符,再用 path 做關鍵字 。下面繼續請求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(GankApi.class);
這樣拿到我們的介面物件後直接請求:
api.getAndroidInfo(1).enqueue(new Callback<GankBean>() {
@Override
public void onResponse(Call<GankBean> call, Response<GankBean> response) {
tv_result.setText(response.body().getResults().get(0).getDesc());
}
@Override
public void onFailure(Call<GankBean> call, Throwable t) {
}
});
執行結果:
我們知道少量引數可以使用@Query 。另外,如果需要多個引數,我們通常是傳入字典集合 @QueryMap。
仍以天氣介面為案例,cityname、key 作為兩個引數:
import java.util.Map;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;
public interface WeatherApi {
@GET("onebox/weather/query?")
Call<WeatherDataBean> getWeather(@QueryMap Map<String, String> params);
}
例項化Retrofit 物件,拿到請求介面。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://op.juhe.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(WeatherApi.class);
傳入引數,發起請求:
Map<String, String> params = new HashMap<>();
params.put("cityname", "深圳");
params.put("key", "4ea58de8a7573377cec0046f5e2469d5");
api.getWeather(params).enqueue(new Callback<WeatherDataBean>() {
@Override
public void onResponse(Call<WeatherDataBean> call, Response<WeatherDataBean> response) {
}
@Override
public void onFailure(Call<WeatherDataBean> call, Throwable t) {
}
});
Post請求引數
以簡單的Post提交使用者id、name 介面為例:
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface PostApi {
@POST("user/new")
Call<Result> postUser(@Body User user);
}
請求實體類User。
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
響應結果類Result。
public class Result {
private int yes;
private int no;
public int getYes() {
return yes;
}
public void setYes(int yes) {
this.yes = yes;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
}
介面呼叫:
User user = new User();
user.setId(1);
user.setName("lgl");
api.postUser(user).enqueue(new Callback<Result>() {
@Override
public void onResponse(Call<Result> call, Response<Result> response) {
if (response.body().getYes() == 0) {
//成功
}
}
@Override
public void onFailure(Call<Result> call, Throwable t) {
//錯誤
}
});
Post提交表單
官網例子,修改使用者介面提交表單:
@POST("user/edit")
Call<Result> editUser(@Field("id") int id, @Field("name") String name);
可見關鍵字是@Field,介面這樣去呼叫:
api.editUser(1, "liuguilin").enqueue(new Callback<Result>() {
@Override
public void onResponse(Call<Result> call, Response<Result> response) {
if (response.body().getYes() == 0) {
Toast.makeText(MainActivity.this, "成功", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Result> call, Throwable t) {
}
});
這樣就把表單提交了。最後,有一個PUT,他是多種檔案型別上傳,比如檔案,圖片,另外還有修改我們的Headers,這個很簡單,看例子
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
這是官網的例子,但是你只需要新增 Headers, 引數就可以傳了,而且引數是一個陣列,可以傳多個。
Retrofit2.0+RxJava
GitHub地址
新增依賴
//介面卡
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
//RxJava
compile 'io.reactivex:rxjava:1.1.6'
//RxAndroid
compile 'io.reactivex:rxandroid:1.2.1'
練習案例。
登入後獲取 user_id,請求使用者資訊 ,共兩個請求:一個是登入,傳參使用者名稱和密碼,另一個,用id去查詢使用者資訊。
@POST("user/login")
Call<User> login(@Field("username") String user, @Field("password") String password);
@GET("user/info")
Call<User> getUser(@Query("id") String id);
例項化構建Retrofit物件,拿到請求介面:增加了addCallAdapterFactory
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("baseUrl")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
api = retrofit.create(PostApi.class);
然後進行兩次呼叫:
api.login("liuguilin", "748778890").enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
String id = response.body().getUser_id();
api.getUser(id).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
Toast.makeText(MainActivity.this, "id:" +
response.body().getId()
+ "name:" + response.body().getName(),
Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
上面是Retrofit的寫法,這樣看著有點臃腫。
下面是RxJava的寫法,需要重新定義兩個介面:
@POST("user/login")
rx.Observable<User> loginForRX(@Body User user);
@GET("user/info")
rx.Observable<User> getUserForRX(@Query("id") String id);
使用呼叫:
api.loginForRX(new User("liuguilin", "748778890")).flatMap(new Func1<User, Observable<User>>() {
@Override
public Observable<User> call(User user) {
return api.getUser(user.getUser_id());
}
}).subscribe(new Action1<User>() {
@Override
public void call(User user) {
Toast.makeText(MainActivity.this, "name:" + user.getName(), Toast.LENGTH_SHORT).show();
}
});
小結 :這就是比較簡潔的寫法了,RxJava作為鏈式的表示式,響應式的操作還是很不錯的。
結束。
相關文章
- Retrofit 2.0 使用教程
- Retrofit + RxJavaRxJava
- Android:Retrofit 2.0 使用攻略(含例項講解)Android
- Android RxJava系列三: 與Retrofit2結合使用和封AndroidRxJava
- 網路元件 基於Retrofit2+RxJava2+GSON/Fastjson的網路框架元件RxJavaASTJSON框架
- Retrofit2+RxJava 簡單使用RxJava
- Android專案框架搭建:mvp+retrofit+rxjava+rxbusAndroid框架MVPRxJava
- Android實現Rxjava2+Retrofit完美封裝AndroidRxJava封裝
- Android Gson使用詳解Android
- Android使用Kotlin+Retrofit+Rxjava實現簡單的網路請求AndroidKotlinRxJava
- OkHttp、rxJava、Retrofit聯合網路請求(二)HTTPRxJava
- Rxjava2與Retrofit2的使用RxJava
- android使用Gson來解析jsonAndroidJSON
- RxJava + Retrofit原始碼解析RxJava原始碼
- 我們真的需要使用RxJava+Retrofit嗎?RxJava
- 使用Retrofit+RxJava實現網路請求RxJava
- Retrofit2<三> rxJava 分析RxJava
- 【Android架構】基於MVP模式的Retrofit2+RXjava封裝之檔案下載(二)Android架構MVP模式RxJava封裝
- 使用Retrofit+RxJava實現帶進度下載RxJava
- 讓我的專案也使用RxJava+OkHttp+RetrofitRxJavaHTTP
- 給初學者的RxJava2.0教程(三)RxJava
- 給初學者的RxJava2.0教程(七)RxJava
- 給初學者的 RxJava2.0 教程 (四)RxJava
- 分分鐘使用Retrofit+Rxjava實現網路請求RxJava
- 【Android架構】基於MVP模式的Retrofit2+RXjava封裝(一)Android架構MVP模式RxJava封裝
- Android中Retrofit的封裝使用Android封裝
- Retrofit+Rxjava的資料請求RxJava
- retrofit如何配合Rxjava封裝程式碼RxJava封裝
- Android進階:七、Retrofit2.0原理解析之最簡流程Android
- Android Retrofit 2.5.0使用基礎詳解Android
- 【Android架構】基於MVP模式的Retrofit2+RXjava封裝之多Url(七)Android架構MVP模式RxJava封裝
- Android Retrofit原始碼解析:都能看懂的Retrofit使用詳解Android原始碼
- Retrofit2.0使用——帶進度下載檔案
- Android進階:七、Retrofit2.0原理解析之最簡流程【上】Android
- Retrofit2.0- 原始碼分析原始碼
- Android RxJava:基礎介紹與使用AndroidRxJava
- 基於MVP+RxJava2+Retrofit+Dagger2+MD的仿B站Android客戶端MVPRxJavaAndroid客戶端
- OkHttp、rxJava、Retrofit聯合網路請求(一)HTTPRxJava
- Android RxJava:這是一份RxJava使用入門學習指南AndroidRxJava