廢話部分
只是在工作上的一些無聊的事情,想閑扯的可以看下,不想的可以用直接看下一段的正題。
我是兩個(gè)月前換的一個(gè)工作,去的一個(gè)公司只有我一個(gè)人開發(fā),并且是從零開始,什么都沒有的情況下我還是去了,因?yàn)槲覀€(gè)人覺得這個(gè)機(jī)會(huì)不錯(cuò),以前工作了兩年都是copy別人的代碼或者維護(hù)別人的代碼。項(xiàng)目也是不倫不類的MVC,網(wǎng)絡(luò)請(qǐng)求、緩存、項(xiàng)目架構(gòu)現(xiàn)在看來都是糟的一塌糊涂(現(xiàn)在這么認(rèn)為,因?yàn)檫@兩個(gè)月自己感覺自己有了很多提高),不過當(dāng)時(shí)的我竟然那么安逸的選擇在那里一直待下去,很慶幸自己已經(jīng)出來了,不然面對(duì)現(xiàn)在這么激烈的競爭,我想我可能就被市場淘汰了。可能現(xiàn)在還有很多人在一個(gè)比較安逸的公司拿著不錯(cuò)的薪資一直沒有跳槽的想法,我只是給大家一個(gè)建議,如果你喜歡技術(shù),喜歡挑戰(zhàn),建議早些脫離這些公司,把每個(gè)公司當(dāng)成自己技術(shù)增長的跳板,不過不建議不到一年就換工作的,太頻繁了也不好,最起碼要把這個(gè)公司用到的技術(shù)全部學(xué)到了才能走,不然到最后還是很難學(xué)到東西。好了不廢話了。開始今天的正題了。
Retrofit??
現(xiàn)在Retrofit可能很多人都有了解,畢竟現(xiàn)在很多開發(fā)者社區(qū)到處可見的標(biāo)題都是Retrofit+RxJava+MVP這些文章,剛開始自己看這些的時(shí)候也有些懵,不過多研究一下,在項(xiàng)目中用一下就發(fā)現(xiàn)其實(shí)這些東西都不是很難,給開發(fā)者一些建議,別上來就去看一些開源項(xiàng)目的源碼,那樣你會(huì)一頭懵逼的,大家可以嘗試去了解應(yīng)用場景,嘗試在項(xiàng)目中或者自己寫個(gè)東西去實(shí)踐一下,然后再去看源碼。
Retrofit通俗的說就是一個(gè)HTTP請(qǐng)求的一個(gè)框架,通過一個(gè)Interface來定義api的實(shí)現(xiàn),通過注釋的方式進(jìn)行一些請(qǐng)求的設(shè)置。
public interface GitHubService
{
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
就像上面的代碼一樣,所有的網(wǎng)絡(luò)請(qǐng)求接口都是定義在一個(gè)Interface中,然后在使用的時(shí)候通過Retrofit中的create()方法即可實(shí)現(xiàn),詳細(xì)的可以看
[Retrofit官網(wǎng)](http://square.github.io/retrofit/
[Retrofit api文檔](http://square.github.io/retrofit/2.x/retrofit/
有關(guān)Retrofit的在這里不多說,因?yàn)楸酒饕莵碚f在使用過程中有關(guān)OkHttp的事情,所以想了解Retrofit的可以去查詢其他的資料
Retrofit中OkHttp使用
大家都知道Retrofit中默認(rèn)使用的網(wǎng)絡(luò)請(qǐng)求方式是使用OkHttp的,先了解下OkHttp
OkHttp的官網(wǎng)介紹和Retrofit很像,也是一個(gè)基于Http的一個(gè)請(qǐng)求客戶端,畢竟Retrofit和OkHttp都是square的開源項(xiàng)目,下面來說下我在項(xiàng)目里面使用的時(shí)候有關(guān)okhttp的配置問題。
首先okhttp的確非常棒,在接入到項(xiàng)目里面使用的時(shí)候日志輸入非常詳細(xì),類似Content-type,header、參數(shù)返回值這些全部都很詳細(xì)的顯示,Okhttp最棒的一個(gè)設(shè)計(jì)就是整個(gè)網(wǎng)絡(luò)請(qǐng)求過程中我們可以用在任意一個(gè)地方去做一個(gè)攔截處理,我們可以通過實(shí)現(xiàn)提供的接口[Interceptor](http://square.github.io/okhttp/3.x/okhttp/okhttp3/Interceptor.html
下面我來舉個(gè)例子:
我在和我們后臺(tái)人員溝通的時(shí)候發(fā)現(xiàn)我們每個(gè)接口都有兩個(gè)共同的參數(shù) version和device,我們不可能在定義的時(shí)候每個(gè)接口里面都去添加上這兩個(gè)參數(shù),為了方便我們會(huì)在一個(gè)地方統(tǒng)一的去添加,那么我們就可以通過自定義一個(gè)Interceptor來實(shí)現(xiàn),具體代碼如下:
public class CommonParamsInterceptor implements Interceptor
{
@Override
public Response intercept(Chain chain) throws IOException
{
Request request = chain.request();
//get請(qǐng)求后面追加共同的參數(shù)
HttpUrl httpUrl = request.url().newBuilder()
.addQueryParameter("version", ConstantConfig.APP_VERSION_CODE)
.addQueryParameter("token", ConstantConfig.APP_TOKEN)
.addQueryParameter("device", "Android").build();
request = request.newBuilder().url(httpUrl).build();
return chain.proceed(request);
}
}
這樣就可以實(shí)現(xiàn)每個(gè)請(qǐng)求上面添加上參數(shù),不過有些事情總是比你想象的蛋疼一些,因?yàn)楹笈_(tái)接口是兩個(gè)人開發(fā)的,一個(gè)人告訴我所有的請(qǐng)求方法是get和post都支持,當(dāng)時(shí)這樣寫也沒發(fā)現(xiàn)什么問題,后臺(tái)和另一個(gè)人調(diào)試的時(shí)候發(fā)現(xiàn)不行,找了半天原因才發(fā)現(xiàn)原來上面的寫法是吧這些參數(shù)都放到了url的后面,也就是get請(qǐng)求的傳參方式,所以如果限定了必須使用post請(qǐng)求那么這個(gè)方法就不行了。唉。。。沒辦法,只能去修改,然后就是一番查找,終于解決了,代碼如下:
public class CommonParamsInterceptor implements Interceptor
{
@Override
public Response intercept(Chain chain) throws IOException
{
Request request = chain.request();
//post請(qǐng)求追加參數(shù)
FormBody.Builder newBody = new FormBody.Builder();
newBody.add("version", ConstantConfig.APP_VERSION_CODE)
.add("token", ConstantConfig.APP_TOKEN).add("device", "Android").build();
//攔截請(qǐng)求中用戶傳入的數(shù)據(jù),把參數(shù)遍歷放入新的body里面,然后進(jìn)行一塊提交
FormBody oldBody = (FormBody) request.body();
for (int i = 0; i < oldBody.size(); i++)
{
newBody.add(oldBody.encodedName(i), oldBody.encodedValue(i));
}
request = request.newBuilder().post(newBody.build()).build();
chain.proceed(request);
}
}
這樣就實(shí)現(xiàn)了表單數(shù)據(jù)的提交,get和post的設(shè)置共同參數(shù)的方式。從這一個(gè)例子就可以看出使用okhttp的時(shí)候我們可以在每一個(gè)步驟進(jìn)行攔截處理,比如設(shè)置緩存、頭部信息等等。
自定義的Interceptor可以在初始化okhttp的時(shí)候設(shè)置,如:
/**
* OkHttp的初始化,設(shè)置緩存目錄,設(shè)置請(qǐng)求最大時(shí)間,請(qǐng)求失敗重連
*/
public void initOkHttp()
{
OkHttpClient.Builder builder = new OkHttpClient.Builder();
CustomParamsInterceptor paramsInterceptor = new CustomParamsInterceptor();
builder.addInterceptor(paramsInterceptor);
if (BuildConfig.DEBUG)
{
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(loggingInterceptor);
}
// 緩存 http://www.itdecent.cn/p/93153b34310e
File cacheFile = new File(ConstantConfig.CACHE_DIR, "/NetCache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
Interceptor cacheInterceptor = new Interceptor()
{
@Override
public Response intercept(Chain chain) throws IOException
{
Request request = chain.request();
if (!Util.isNetworkConnected(App.getmAppContext()))
{
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
}
Response response = chain.proceed(request);
if (Util.isNetworkConnected(App.getmAppContext()))
{
int maxAge = 0;
// 有網(wǎng)絡(luò)時(shí) 設(shè)置緩存超時(shí)時(shí)間0個(gè)小時(shí)
response.newBuilder().header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else
{
// 無網(wǎng)絡(luò)時(shí),設(shè)置超時(shí)為4周
int maxStale = 60 * 60 * 24 * 28;
response.newBuilder().header("Cache-Control",
"public, only-if-cached, max-stale=" + maxStale)
.build();
}
return response;
}
};
builder.cache(cache).addInterceptor(cacheInterceptor);
//設(shè)置超時(shí)
builder.connectTimeout(15, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
//錯(cuò)誤重連
builder.retryOnConnectionFailure(true);
okHttpClient = builder.build();
}
okhttp很方便,很靈活,我們可以隨意的折騰。不過,話說,最氣人的是因?yàn)閕os那邊的問題,接口竟然要求我參數(shù)的傳遞用json格式。
這里為大家說下數(shù)據(jù)傳輸格式和數(shù)據(jù)格式是兩個(gè)不同的東西,我們一般認(rèn)為數(shù)據(jù)格式使我們手機(jī)在接收接口api返回的數(shù)據(jù)是一個(gè)json的String串,但是和后臺(tái)接口傳遞數(shù)據(jù)的格式有以下幾種:
- application/xhtml+xml :XHTML格式
- application/xml : XML數(shù)據(jù)格式
- application/atom+xml :Atom XML聚合格式
- application/json : JSON數(shù)據(jù)格式
- application/pdf :pdf格式
- application/msword : Word文檔格式
- application/octet-stream : 二進(jìn)制流數(shù)據(jù)(如常見的文件下載)
- application/x-www-form-urlencoded : <form encType=””>中默認(rèn)的encType,form表單數(shù)據(jù)被編碼為key/value格式發(fā)送到服務(wù)器(表單默認(rèn)的提交數(shù)據(jù)的格式)
我們?cè)诓辉O(shè)置默認(rèn)的情況下http請(qǐng)求的數(shù)據(jù)傳輸格式是最后一個(gè)application/x-www-form-urlencoded ,這個(gè)設(shè)置是一key value的形式傳遞的參數(shù),但是我們的后臺(tái)要求我使用application/json的格式去傳遞數(shù)據(jù),唉。。。。那以上兩種方法就不可行了,沒辦法,接著一番,最終也是搞出來了,代碼如下:
public class CustomParamsInterceptor implements Interceptor
{
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
@Override
public Response intercept(Chain chain) throws IOException
{
Request request = chain.request();
ArrayMap<String, String> paramsMap = new ArrayMap<String, String>();
paramsMap.put("version", "1.0");
paramsMap.put("token", "");
paramsMap.put("device", "Android");
if (request.body() instanceof FormBody)
{
FormBody oldBody = (FormBody) request.body();
for (int i = 0; i < oldBody.size(); i++)
{
paramsMap.put(oldBody.encodedName(i), oldBody.encodedValue(i));
}
}
Gson gson = new Gson();
AppLog.i("Gson參數(shù)格式---" + gson.toJson(paramsMap));
RequestBody body = RequestBody.create(JSON, gson.toJson(paramsMap));
request = request.newBuilder().post(body).build();
return chain.proceed(request);
}
}
這就是我今天所有的經(jīng)歷了,我每天都會(huì)寫一篇關(guān)于在開發(fā)中遇到的問題和學(xué)到的技術(shù)點(diǎn),如果有興趣的可以關(guān)注,雖說現(xiàn)在還是個(gè)小白,不過努力總不會(huì)白費(fèi)的,加油吧騷年們。
我的項(xiàng)目使用了MVP+RxJava+Retrofit 很多東西都還在持續(xù)的完善中,如果有興趣的大家可以一起討論。感謝大家的閱讀。