RxJava配合Retrofit實現網絡封裝
那么呢,首先呢,我們呢,來記錄一下Android中比較火的兩種技術,火了大半壁江山的RxJava和壟斷了大部分的網絡請求Retrofit。這兩者的結合其實不需要太多的封裝,只要簡簡單單的搞兩下子基本就實現了常用的網絡框架了。
廢話不多說,代碼說明一切:
1、創建一個Android項目;
2、導入下面的依賴;
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.google.code.gson:gson:2.6.2'
3、新建一個接口 NetService
public interface NetService { }
4、新建一個類 NetUtils
構造函數
private static final long DEFAULT_TIMEOUT = 8; //超時時間設置為8秒
private final String BASE_URL ="http://op.juhe.cn/onebox/"; //固定的網址 必須以‘/’結尾
private static NetUtils INSTANCE;
private final Retrofit retrofit;
public static NetService netService = null;
private NetUtils() {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();//創建一個OkHttp,如果不指定默認就是OkHttp
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create()) //GSON數據解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
netService = retrofit.create(NetService.class);
}
我們可以看到這個構造函數是私有的,這里主要是想讓這個工具類是一個單例模式:接下來我們實現單例模式:
public static NetService getInstance()
{ if (null == INSTANCE) {
INSTANCE = new NetUtils();
}
return netService;
}
工具已經封裝好了,
接下來:看看NetService中的請求方法怎么寫:
一般的現在后臺返回的數據都是下面這個種格式的:
{
"error_code":"200",
"reason":"請求成功",
"result":"{ }"
}
JSON在線視圖查看器.png
前面是狀態和提示,通常我們只關心result里面的真實數據,所以這里寫個通用數據類BaseData
public class BaseData<T> {
public T result;
public int status;
public String reason;
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
5、NetServer中的請求方法
@GET("news/query") //get請求 括號內為請求地址后綴
//@Query("key") @Query("q") key 和q 查詢字段
Observable<BaseData<NewsData>> getNewsData(@Query("key") String key, @Query("q")String name);
為什么要用BaseData 主要是每次返回的error_code 和reason和result字段名永遠都是不變的,我們不需要再每個接受數據的實體中都寫重復的字段,這里以聚合數據中的新聞接口為例,簡單寫幾個接收字段;
public class NewsData {
private String title;
private String content;
private String url;
}
當然set get方法你需要實現,我就不貼代碼了;
6、如何請求網絡
public void startRequest(View v){
String key=""; //key我就不給你了,少年自己請求賬號吧
String q="雙十一"; //雙十一關鍵字
getNetService().getNewsData(key,q).
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseData<List<NewsData>>>() {
@Override
public void onCompleted()
{
}
@Override
public void onError(Throwable e) {
Toast.makeText(NetUtilsActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(BaseData<List<NewsData>> listBaseData) {
result = listBaseData.getResult();
newsAdapter.notifyDataSetChanged();
Toast.makeText(NetUtilsActivity.this, result.get(0).getTitle(), Toast.LENGTH_SHORT).show();
}
});
}
手指輕輕那么一點,網絡請求立馬實現
那么是不是就結束了呢,不要著急,連進度框都沒有算什么網絡請求?所以呢,還需要進一步的優化!
7、建一個BaseSubscriber
public abstract class BaseSubscriber<T> extends Subscriber<T> {
private Context mContext;
private ProgressDialog progressDialog;
public BaseSubscriber(Context context) {
mContext=context;
progressDialog = new ProgressDialog(mContext);
progressDialog.setMessage("正在加載數據,請稍后...");
}
@Override
public void onStart() {
super.onStart();
Log.d("BaseSubscriber", "onStart");
progressDialog.show();
}
@Override public void onCompleted(){
progressDialog.dismiss();
}
@Override
public void onError(Throwable e){
progressDialog.dismiss();
final AlertDialog.Builder builder=new AlertDialog.Builder(mContext);
builder.setMessage(e.getMessage()).setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
builder.create().dismiss();
}
});
builder.show(); }
@Override public abstract void onNext(T t);
}
onNext為抽象方法,主要是因為大部分時間我們并不需要關心onError()和onCompleted(),這樣保證了調用的Fragent和Acitivity的簡潔,如果真要處理錯誤只需要將onError重寫。
請求變成這樣了
getNetService().getNewsData(key, q).
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<BaseData<List<NewsData>>>(NetUtilsActivity.this) {
@Override
public void onNext(BaseData<List<NewsData>> newsDatas) {
result=newsDatas.getResult();
newsAdapter.notifyDataSetChanged();
}
});
getNetService()是從BaseActivity來的:
public class BaseActivity extends AppCompatActivity {
public NetService getNetService(){
return NetUtils.getInstance();
}
}
但還是不完美,比如BaseSubscriber<BaseData<List<NewsData>>> 泛型太累贅,比如每次都要寫
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()這一串進行線程切換,這種固定的我們能不能寫成方法直接調用?
那么:
8、新建HttpResultFunc
public class HttpResultFunc<T> implements Func1<BaseData<T>, T> {
@Override
public T call(BaseData<T> baseData) {
if (baseData.geError_Code!=200) {
try {
throw new Exception(baseData.getReason());
} catch (Exception e) {
e.printStackTrace(); }
}
return baseData.getResult(); }
該類的主要功能就是將不關心的數據過濾掉,如果error_code!=200,說明請求數據出錯了,此時通常result這樣的數據為null,只需要在activity或者Fragment中判斷數據是否為null;
修改后 map變形
getNetService().getNewsData(key, q)
.map(new HttpResultFunc<List<NewsData>>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<List<NewsData>>(NetUtilsActivity.this) {
@Override
public void onNext(List<NewsData> newsDatas) {
if(null!=newsDatas) {
result = newsDatas;
newsAdapter.notifyDataSetChanged();
}
}
});
9、在NetUtils中加入:
public static void toSubscribe(Observable o, Subscriber s) {
o.subscribeOn(Schedulers.io()
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);}
這一樣我們就將固定的東西封裝起來了:
請求變成這樣了
NetUtils.toSubscribe(getNetService().getNewsData(key, q)
.map(new HttpResultFunc<List<NewsData>>()),
new BaseSubscriber<List<NewsData>>(NetUtilsActivity.this) {
@Override public void onNext(List<NewsData> o) {
if(null!=0){
result=o;
newsAdapter.notifyDataSetChanged();
}
}
});
但筆者沒有找到有個很好的方法把.map(new HttpResultFunc<List<NewsData>>()),用泛型封裝起來,主要是對泛型的知識還很欠缺。
來自:http://www.jianshu.com/p/d96be4123308