最開(kāi)始使用AndroidStudio的時(shí)候,各種不適應(yīng),各種懷戀Eclipse,寫(xiě)了幾千行代碼委曲熟習(xí)了AndroidStudio后,感覺(jué)AndroidStudio不要太棒了。
學(xué)習(xí)Retrofit也是這樣,遇到麻煩就想去用之前用過(guò)的框架,熟習(xí)了過(guò)后我現(xiàn)在連吃個(gè)湯圓都喜歡串著吃,囧….
固然實(shí)際的項(xiàng)目開(kāi)發(fā)中,不可能給我們時(shí)間漸漸去適應(yīng)、去學(xué)習(xí),要斟酌技術(shù)風(fēng)險(xiǎn)和學(xué)習(xí)本錢(qián)、團(tuán)隊(duì)共用。所以我才覺(jué)得Retrofit這1點(diǎn)好貼心,基于OkHttp實(shí)現(xiàn),也就是說(shuō)我當(dāng)我遇到我不知道該怎樣實(shí)現(xiàn)的功能的時(shí)候,可以無(wú)縫切換到OkHttp上面實(shí)現(xiàn)需求。
看看下面的代碼,一樣申明1個(gè)要求call
,一樣的異步call.enqueue
,一樣的回調(diào)Callback
,乃至連mDatas = new Gson().fromJson(res, new TypeToken<List<SearchImage>>
Gson解析都是從前的味道。但是我們已開(kāi)始在使用Retrofit了,不知不覺(jué),潛移默化中已前進(jìn)了1小步。
private void initBtnBasic() {
mBtnBasic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Retrofit build = new Retrofit.Builder().baseUrl("http://zhuangbi.info/").build();
Call<ResponseBody> call = build.create(SearchApi.class).basicSearch("牛逼");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String res = response.body().string();
mDatas = new Gson().fromJson(res, new TypeToken<List<SearchImage>>() {
}.getType());
for (SearchImage searchImage : mDatas) {
Log.d(TAG, "onResponse: " + searchImage.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "onFailure: ", t);
}
});
}
});
}
public interface SearchApi {
@GET("search")
Call<ResponseBody> basicSearch(@Query("q") String qTitle);
}
固然,100%的雷同也不好嘛,來(lái)了1趟總得學(xué)點(diǎn)東西,1下接觸陌生知識(shí)太多,跨步前進(jìn)容易扯到蛋不可取,原地踏步也不爽,小步慢行。
這里我們構(gòu)建要求URL的事后使用了1個(gè)接口SearchApi
,
Retrofit
Retrofit.Builder().baseUrl("http://zhuangbi.info/").build().create(SearchApi.class).basicSearch("牛逼");
OkHttp
new Request.Builder()
.url("http://zhuangbi.info/search?q=" + getString("牛逼"))
.build();
聰明的我們1看就知道
@GET("search")
Call<ResponseBody> basicSearch(@Query("q") String qTitle);
這里的@GET("search")
表示使用get要求,同時(shí)search是get命令路徑的1部份
固然,你也能夠把他寫(xiě)到參數(shù)里面,Like This
@GET("{path}")
Call<ResponseBody> basicSearch(@Path("path") String path, @Query("q") String qTitle);
這樣用就好了:
Retrofit.Builder().baseUrl("http://zhuangbi.info/").build().create(SearchApi.class).basicSearch("search","牛逼");
@Query("q") String qTitle
Query是查詢(xún)參數(shù),就相當(dāng)于http://zhuangbi.info/search?q=牛逼
的
?q=牛逼
部份
或許你會(huì)想如果要查詢(xún)很多參數(shù),豈不是也要配置很多函數(shù)參數(shù)里面,然后讓函數(shù)變得巨長(zhǎng)
固然不用@QueryMap
為你排難解紛
@GET("search")
Call<ResponseBody> basicSearch(@QueryMap Map<String, String> map);
就這么簡(jiǎn)單,輕松進(jìn)入到Retrofit的地盤(pán)
private void initTrans() {
final Retrofit build = new Retrofit.Builder()
.baseUrl("http://zhuangbi.info/")
.addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器
.build();
mBtnTrans.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Call<List<SearchImage>> call = build.create(SearchApi.class).transSearch("牛逼");
call.enqueue(new Callback<List<SearchImage>>() {
@Override
public void onResponse(Call<List<SearchImage>> call, Response<List<SearchImage>> response) {
mDatas.clear();
//使用轉(zhuǎn)換器,內(nèi)部已幫我們實(shí)現(xiàn)轉(zhuǎn)換,省去了new Gson().form(jsonStr,new TypeToken<>())的進(jìn)程
mDatas = response.body();
for (SearchImage searchImage : mDatas) {
Log.d(TAG, "onResponse: " + searchImage.toString());
}
}
@Override
public void onFailure(Call<List<SearchImage>> call, Throwable t) {
Log.e(TAG, "onFailure: ", t);
}
});
}
});
}
所謂轉(zhuǎn)換器,其實(shí)再剛才的代碼基礎(chǔ)上也沒(méi)多大改動(dòng)嘛,不過(guò)是再構(gòu)造的時(shí)候加了1句.addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器
Callback里面的json轉(zhuǎn)換不用再使用
Gson gson = new Gson();
mDatas = gson.fromJson(resStr, new TypeToken<List<SearchImage>>() {
}.getType());
直接使用mDatas = response.body();
就能夠獲得,固然要求的函數(shù)泛型也要改寫(xiě)
@GET("search")
Call<List<SearchImage>> transSearch(@Query("q") String qTitle);
省去了在回調(diào)函數(shù)new Gson的干擾,代碼變得跟簡(jiǎn)潔。
固然,這里使用的是Gson,如果你需要JackSon,F(xiàn)astJson選擇適合的解析器傳遞進(jìn)去就好了。
如果,你需要自定義解析規(guī)則,也是沒(méi)問(wèn)題的,這需要自己去實(shí)現(xiàn),但是這不是我們今天要說(shuō)的重點(diǎn),放到后面再寫(xiě)1個(gè)這類(lèi)例子。
總之—使用了解析器,讓我們的代碼可讀性變得更好,代碼也更簡(jiǎn)潔。
private void initAdapt() {
final Retrofit build = new Retrofit.Builder()
.baseUrl("http://zhuangbi.info/")
.addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接RxJava
.build();
//使用RxBinding處理點(diǎn)擊事件 RxBinding基于RxJava實(shí)現(xiàn)
RxView.clicks(mBtnAdapter)
.debounce(500, TimeUnit.MILLISECONDS) //排除500ms內(nèi)重復(fù)點(diǎn)擊事件
.observeOn(AndroidSchedulers.mainThread())//切換到主線(xiàn)程履行響應(yīng)內(nèi)容,相當(dāng)于在回調(diào)方法中履行runOnUiThread方法
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
//不需要Call了,直接使用Observable,順滑的切換到RxJava領(lǐng)域
Observable<List<SearchImage>> observable = build.create(SearchApi.class)
.search("牛逼")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
//這里主要是為了演示,代碼簡(jiǎn)潔,實(shí)際中不要這樣,
// 還要斟酌取消1個(gè)要求,返回毛病值處理,業(yè)務(wù)需求等等
observable.subscribe(new Action1<List<SearchImage>>() {
@Override
public void call(List<SearchImage> searchImages) {
for (SearchImage searchImage : searchImages) {
Log.d(TAG, "call: " + searchImage.toString());
}
}
});
}
});
}
和解析器1樣,適配器也就是加了1句.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接
代碼雖然簡(jiǎn)潔,作用巨大,注意現(xiàn)在我們已不用Call來(lái)做要求了,我們使用Observable
,這直接就能夠利用RxJava操作。
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
2.之前的回調(diào)Callback
變成了監(jiān)聽(tīng)者處理事件observable.subscribe
,有更多
Action1
Subscription
方式讓我們選擇
3.鏈?zhǔn)秸{(diào)用,全部邏輯清晰明了
但是注意1點(diǎn):
對(duì)API調(diào)用了 observeOn(MainThread) 以后,線(xiàn)程會(huì)跑在主線(xiàn)程上,包括 onComplete 也是, unsubscribe 也在主線(xiàn)程,然后如果這時(shí)候候調(diào)用 call.cancel
會(huì)致使 NetworkOnMainThreadException ,所以1定要在 retrofit 的API調(diào)用.subscribeOn(io).observeOn(MainThread)
以后加1句 unsubscribeOn(io)
完全的就是
Api
.subscribeOn(io)
.observeOn(MainThread)
.unsubscribeOn(io)
哦,這里取消1個(gè)命令和call的方式不同,用的是unsubscribeOn(io)
,Call對(duì)應(yīng)的方法是cancel()
這里還加入了1點(diǎn)小元素,注意我們使用的是
RxView.clicks(mBtnAdapter)
.debounce(500, TimeUnit.MILLISECONDS) //排除500ms內(nèi)重復(fù)點(diǎn)擊事件
.observeOn(AndroidSchedulers.mainThread())//切換到主線(xiàn)程履行響應(yīng)內(nèi)容,相當(dāng)于在回調(diào)方法中履行runOnUiThread方法
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
取代
mBtnTrans.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {}
這是使用了RxBinding這個(gè)庫(kù),基于RxJava實(shí)現(xiàn),看看用著這類(lèi)方式實(shí)現(xiàn)方式對(duì)照傳統(tǒng)方式在上述代碼表達(dá)出來(lái)的,線(xiàn)程切換、避免重復(fù)點(diǎn)擊的實(shí)現(xiàn)優(yōu)勢(shì)
我寫(xiě)了,快1周也沒(méi)有找到他的,要求方式,要求頭在哪兒打印,1直好慌。知道再OkHttp的Github上看到HttpLoggingInterceptor
這個(gè)庫(kù)。
使用方式:導(dǎo)入
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
private void initLog() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS);
//自定義攔截方式
HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.v("Retrofit", message);
}
}).setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(logger).build();
final Retrofit build = new Retrofit.Builder()
.baseUrl("http://zhuangbi.info/")
.addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接RxJava
.client(httpClient)
.build();
RxView.clicks(mBtnLog)
.debounce(300, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
//不需要Call了,直接使用Observable,順滑的切換到RxJava領(lǐng)域
Observable<List<SearchImage>> observable = build.create(SearchApi.class)
.search("牛逼")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
//這里主要是為了演示,代碼簡(jiǎn)潔,實(shí)際中不要這樣,
// 還要斟酌取消1個(gè)要求,返回毛病值處理,業(yè)務(wù)需求等等
observable.subscribe(new Action1<List<SearchImage>>() {
@Override
public void call(List<SearchImage> searchImages) {
for (SearchImage searchImage : searchImages) {
Log.d(TAG, "call: " + searchImage.toString());
}
}
});
}
}
);
}
有4個(gè)要求層次,1般BASIC和HEADER用得比較多
public enum Level {
/** No logs. */
NONE,
/**
* Logs request and response lines.
*
* <p>Example:
* <pre>{@code
* --> POST /greeting http/1.1 (3-byte body)
*
* <-- 200 OK (22ms, 6-byte body)
* }</pre>
*/
BASIC,
/**
* Logs request and response lines and their respective headers.
*
* <p>Example:
* <pre>{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
* --> END POST
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
* <-- END HTTP
* }</pre>
*/
HEADERS,
/**
* Logs request and response lines and their respective headers and bodies (if present).
*
* <p>Example:
* <pre>{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
*
* Hi?
* --> END GET
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
*
* Hello!
* <-- END HTTP
* }</pre>
*/
BODY
}
還可以通過(guò)自定義HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger()
個(gè)性化打造自己的網(wǎng)絡(luò)數(shù)據(jù)要求打印。
把攔截器,進(jìn)1步升華,比以下載數(shù)據(jù)的時(shí)候,攔截下Response,讀取里面的信息,然后再傳輸給下1層,不就能夠?qū)崿F(xiàn)監(jiān)聽(tīng)下行數(shù)據(jù)么。
先實(shí)現(xiàn)1個(gè)監(jiān)聽(tīng)器
public interface ProcessListener {
/**
* @param current 已完成字節(jié)數(shù)
* @param total 總字節(jié)數(shù)
* @param isCompleted 是不是已完成
*/
void onProcess(long current, long total, boolean isCompleted);
}
實(shí)現(xiàn)1個(gè)具有攔截能力的Response類(lèi)
public class ProcessResponseBody extends ResponseBody {
private ProcessListener mProcessListener;
private ResponseBody mResponseBody;
private BufferedSource mBufferedSource;
public ProcessResponseBody(ResponseBody responseBody, ProcessListener processListener) {
mProcessListener = processListener;
mResponseBody = responseBody;
}
@Override
public MediaType contentType() {
return mResponseBody.contentType();
}
@Override
public long contentLength() {
return mResponseBody.contentLength();
}
@Override
public BufferedSource source() {
if (mBufferedSource == null) {
mBufferedSource = Okio.buffer(source(mResponseBody.source()));
}
return mBufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long nowSize = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long readSize = super.read(sink, byteCount);
nowSize += (readSize != -1 ? readSize : 0);
mProcessListener.onProcess(nowSize, mResponseBody.contentLength(), readSize == -1);
return readSize;
}
};
}
}
調(diào)用
final OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response oldResponse = chain.proceed(chain.request());
return oldResponse.newBuilder()
.body(new ProcessResponseBody(oldResponse.body(), new ProcessListener() {
@Override
public void onProcess(long current, long total, boolean isCompleted) {
Log.d(TAG, "onProcess: " + current + "/" + total + "---" + isCompleted);
}
}))
.build();
}
}).build();
查看Log
上傳和下行,同理,實(shí)現(xiàn)1個(gè)具有攔截能力的Request便可
太快了,摹擬器里面沒(méi)有大1點(diǎn)的圖片,本來(lái)是有進(jìn)度條顯示,看下日志吧
實(shí)現(xiàn)代碼:
public class ProcessRequestBody extends RequestBody {
private ProcessListener mProcessListener;
private RequestBody mRequestBody;
private BufferedSink mBufferedSink;
public ProcessRequestBody(RequestBody requestBody, ProcessListener processListener) {
mProcessListener = processListener;
mRequestBody = requestBody;
}
@Override
public long contentLength() throws IOException {
return mRequestBody.contentLength();
}
@Override
public MediaType contentType() {
return mRequestBody.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
if (mBufferedSink == null) {
mBufferedSink = Okio.buffer(sink(sink));
}
mRequestBody.writeTo(mBufferedSink);
mBufferedSink.flush();
}
private Sink sink(Sink sink) {
return new ForwardingSink(sink) {
long writenBytes = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
writenBytes += byteCount;
mProcessListener.onProcess(writenBytes, mRequestBody.contentLength(), writenBytes == mRequestBody.contentLength());
}
};
}
}
調(diào)用
final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
RequestBody requestBody = new ProcessRequestBody(chain.request().body(), new ProcessListener() {
@Override
public void onProcess(final long current, final long total, final boolean isCompleted) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mDialog.show();
mDialog.setProgress((int) (current * 100 / total));
if (isCompleted) {
mDialog.dismiss();
}
}
});
Log.d(TAG, "onProcess: " + current + "/" + total + isCompleted);
}
});
Request newRequest = chain.request().newBuilder().post(requestBody).build();
return chain.proceed(newRequest);
}
})
.build();
沒(méi)有進(jìn)度條終究是不爽的,用真機(jī)選個(gè)大圖再來(lái)1發(fā)
源碼下載地址:https://github.com/zhouruikevin/RxJavaSamples-master