Android 網絡請求:Retrofit 使用
示例項目:
總結:結合 RxJava 使用得十分的爽。如果沒有用到 RxJava,個人感覺這樣使用和用 OkHttp3 的封裝差不多
配置
轉換器
轉換器可以被添加到支持其他類型。六兄弟模塊適應流行的序列化庫爲您提供方便。
Converters can be added to support other types. Six sibling modules adapt popular serialization libraries for your convenience.
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
直接返回String類型需引入:ScalarsConverterFactory.create()
<span style="font-size:14px;"> retrofit = new Retrofit.Builder()
.client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//設置不同的底層網絡庫
.baseUrl(strBaseUrl)
.addConverterFactory(ScalarsConverterFactory.create())//添加 String類型[ Scalars (primitives, boxed, and String)] 轉換器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
.build();</span>
返回Gson類型需引入:GsonConverterFactory.create()
retrofit = new Retrofit.Builder()
.client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//設置不同的底層網絡庫
.baseUrl(strBaseUrl)
.addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
.build();
Retrofit 結合 RxJava使用
-
添加依賴
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 'io.reactivex:rxandroid:1.1.0' compile 'io.reactivex:rxjava:1.1.0'
前兩個是 Retrofit 和 Gson 的依賴,第三個是 Retrofit 中 RxJava 轉換器的依賴,最後兩個就是 RxJava 和 Rx Android 的依賴
-
編寫 API
使用 RxJava 的情況下,接口文件稍有修改,接口中的方法的返回類型不再是 Call 而是 Observable 類型:
-
public interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo); //使用 RxJava 的方法,返回一個 Observable @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo); }
結合 RxJava 使用的 接口就定義好了,模型類不需要變動,接下來直接進行網絡請求
-
網絡請求
使用 RxJava 的 Retrofit 可以直接在 主線程中編寫。
-
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Retrofit retrofit=new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器 .build(); GitHub gitHub=retrofit.create(GitHub.class); gitHub.RxContributors("square","retrofit") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<Contributor>>() { @Override public void onCompleted() { Log.i("TAG","onCompleted"); } @Override public void onError(Throwable e) { } @Override public void onNext(List<Contributor> contributors) { for (Contributor c:contributors){ Log.i("TAG","RxJava-->"+c.getLogin()+" "+c.getId()+" "+c.getContributions()); } } }); }
使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過期時間重新請求
使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過期時間重新請求
OuNews 新聞:RetrofitManager類下的 initOkHttpClient 方法
okhttp3.X,retrofit:2.0.0-beta4適用
1 配置okhttp中的Cache
OkHttpClient okHttpClient;
File cacheFile = new File(DemoActivity.this.getCacheDir(), "[緩存目錄]");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 10); //100Mb
okHttpClient = new OkHttpClient.Builder()
.cache(cache)
.build();
<pre name="code" class="java"> // 完成緩存
public class MyOkHttpClient {
//設緩存有效期爲兩天
protected static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2;
//查詢緩存的Cache-Control設置,爲if-only-cache時只查詢緩存而不會請求服務器,max-stale可以配合設置緩存失效時間
protected static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
//查詢網絡的Cache-Control設置,頭部Cache-Control設爲max-age=0時則不會使用緩存而請求服務器
protected static final String CACHE_CONTROL_NETWORK = "max-age=0";
private String TAG = "MyOkHttpClient";
private OkHttpClient okHttpClient;
private static MyOkHttpClient myOkHttpClient;
public static MyOkHttpClient getMyOkHttpClient() {
if (myOkHttpClient == null) {
myOkHttpClient = new MyOkHttpClient();
}
return myOkHttpClient;
}
/**
* 初始化
*
* @param mContext
*/
public void init(final Context mContext) {
if (okHttpClient != null) {
return;
}
Log.e(TAG, "初始化okHttpClient");
// 因爲BaseUrl不同所以這裏Retrofit不爲靜態,但是OkHttpClient配置是一樣的,靜態創建一次即可
File cacheFile = new File(
mContext.getCacheDir(),
"HttpCache"
); // 指定緩存路徑
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); // 指定緩存大小100Mb
// 雲端響應頭攔截器,用來配置緩存策略
Interceptor rewriteCacheControlInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtil.isConnected(mContext)) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE).build();
Log.e(TAG, "no network");
}
Response originalResponse = chain.proceed(request);
if (NetUtil.isConnected(mContext)) {
//有網的時候讀接口上的@Headers裏的配置,你可以在這裏進行統一的設置
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma").build();
} else {
return originalResponse.newBuilder().header("Cache-Control",
"public, only-if-cached," + CACHE_STALE_SEC)
.removeHeader("Pragma").build();
}
}
};
okHttpClient = new OkHttpClient.Builder().cache(cache)
.addNetworkInterceptor(rewriteCacheControlInterceptor)
.addInterceptor(rewriteCacheControlInterceptor)
.connectTimeout(30, TimeUnit.SECONDS).build();
}
public OkHttpClient getOkHttpClient() {
return okHttpClient;
}
/**
* 根據網絡狀況獲取緩存的策略
*
* @return
*/
@NonNull
public static String getCacheControl(Context mContext) {
return NetUtil.isConnected(mContext) ? CACHE_CONTROL_NETWORK : CACHE_CONTROL_CACHE;
}
}
2 配置請求頭中的cache-control
//使用 RxJava 的方法,返回一個 Observable
@Headers("Cache-Control: public, max-age=3600")
@GET("/repos/{owner}/{repo}/contributors")
Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo);
3 配置請求 Retrofit 設置不同的底層網絡庫
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
.build();
4 開啓打印連接日誌
/**
* 開啓打印連接日誌
* @return
*/
private HttpLoggingInterceptor getHttpLoggingInterceptor() {
boolean isDebug= false;//是否開啓
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
if(isDebug)
{
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
}
//開啓打印連接日誌
return interceptor;
}
OkHttpClient.Builder().addInterceptor(getHttpLoggingInterceptor())
eg:
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(getHttpLoggingInterceptor())
.cache(cache)
.addNetworkInterceptor(rewriteCacheControlInterceptor)
.addInterceptor(rewriteCacheControlInterceptor)
.connectTimeout(30, TimeUnit.SECONDS).build();
Retrofit 如何使用Observable.from發出多條連接的?
以自己的開源項目
MeiZiNews 中的 ` MzituImgListModel
爲栗子,大概是這3步:
- 先向網站請求參數鏈表
- 使用 flatMap,它可以返回 Observable,所以在 flatMap 裏使用 Observable.from
- 再使用 flatMap,在它裏面發出單次網絡請求連接的 Observable,這時在它返回的就是單次請求的結果了
/** * Retrofit 使用Observable.from發出多條連接 * @param context * @param listener * @return */ @Override public Subscription loadWeb(final Context context, final OnModelListener<ImgItem> listener) { MyStringRetrofit.getMyStringRetrofit().init(context, MeiZiApi.MZITU_API); final MzituApi mzituApi = MyStringRetrofit.getMyStringRetrofit().getCreate(MzituApi.class); Observable observable = mzituApi.RxImgList( MyOkHttpClient.getCacheControl(context), imgId, page ).flatMap( new Func1<String, Observable<Integer>>() { @Override public Observable<Integer> call(String s) { // 第一次請求先請求頁數,從而獲得要發的鏈接數 Elements mElements = JsoupUtil.getMzituImgPage(s); Elements tempElements = mElements.select("span"); String size = tempElements.get(tempElements.size() - 2).text(); if (TextUtils.isEmpty(size)) { return null; } ArrayList<Integer> numList = new ArrayList<Integer>(); for (int i = 1; i <= Integer.parseInt(size); i++) { numList.add(i); } return Observable.from(numList); } } ).flatMap( new Func1<Integer, Observable<String>>() { @Override public Observable<String> call(Integer integer) { // 發出單次網絡請求連接 return mzituApi.RxImgList( MyOkHttpClient.getCacheControl(context), imgId, integer + "" ); } } ).map( new Func1<String, ImgItem>() { @Override public ImgItem call(String s) { // 處理每次請問的結果 ImgItem img = new ImgItem(); Elements mElements = JsoupUtil.getMzituImgItem(s); Element tempElement = mElements.first(); img.setImgUrl(tempElement.select("img").attr("src")); img.setUrl(tempElement.select("img").attr("src")); img.setStory_title(tempElement.select("img").attr("alt")); return img; } } ); return RxJavaUtil.rxIoAndMain( observable, new Subscriber<ImgItem>() { @Override public void onCompleted() { listener.onCompleted(); } @Override public void onError(Throwable e) { listener.onError(e.toString()); DebugUtil.debugLogErr(e, e.toString()); } @Override public void onNext(ImgItem imgItem) { listener.onSuccess(imgItem); } } ); }
問題集
其他筆記
@Path:路徑
@Query:查詢條件,如:
xx=yy
如果有多個查詢條件可以使用: @QueryMap
;
最後,推薦下自己的開源項目:
有妹子的日報:https://github.com/qq137712630/MeiZiNews
APK下載:http://www.wandoujia.com/apps/com.ms.meizinewsapplication