前言
當我們要從零去搭建一個自己的應用框架時 。做為2017年Android程序員的我,就會把Kotlin+Retrofit+MVP+RX系列拿的去實戰(zhàn)。整體框架模式構(gòu)思好后,那就得想想大概實現(xiàn)的步驟。說到這里,就得整理下應用大概有哪些東西了。

目前個人能想到的也就這些,這樣就有個引導的步驟和思路了。所以寫了下面幾篇文章
Android搭建應用框架系列之Retrofit封裝
Android搭建應用框架系列之MVP封裝
Android搭建應用框架系列之RxBus
Android搭建應用框架系列之ORM數(shù)據(jù)庫
Android搭建應用框架系列之Glide
Android搭建應用框架系列之BaseActivity
Android搭建應用框架系列之StatusView
也算自己給自己的的一些總結(jié),具體代碼參考GoachFrame-Github
接下來,就先從網(wǎng)絡層Retrofit+OkHttp說起,記得以前自己寫過一篇Retrofit的博客,學會Retrofit+OkHttp+RxAndroid三劍客的使用,讓自己緊跟Android潮流的步伐,但返回的數(shù)據(jù)沒有結(jié)合RxJava來使用,所以這里重新來寫下。
思路
我們都知道,實現(xiàn)一個Retrofit大概需要下面的幾個步驟
- 配置一個
OkHttp對象 - 配置
BaseUrl - 需要返回
Obserable對象就配置RxJava2CallAdapterFactory - 需要
Gson解析就配置GsonConverterFactory
同時一個Retrofit對象最好對應一個BaseUrl。本著封裝變化的原則,仔細相想,這里也就OkHttp是變化的,BaseUrl可以通過參數(shù)傳入,然后RxJavaCallAdapterFactory和GsonConverterFactory直接配置,另外GsonConverterFactory可以傳入一個Gson對象,來統(tǒng)一處理返回JSON數(shù)據(jù)。其中Retrofit創(chuàng)建通過一個單例來實現(xiàn),自定義的OkHttp對象可在Application里面注入,也可以直接傳默認實現(xiàn)的OkHttp對象
OkHttp
- 先在
build.gradle添加依賴庫
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.3-2'
implementation 'com.android.support:appcompat-v7:26.+'
implementation 'com.squareup.okhttp3:logging-interceptor:3.4.1'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2::2.3.0'
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.jakewharton.timber:timber:4.5.1'
其中okhttp3:logging庫是下面做http請求攔截器使用,Timber是Log封裝庫
- 創(chuàng)建
OkHttp對象的接口IClient
interface IClient {
fun getClient():OkHttpClient
}
抽象一個getClient方法供外部自己配置。
- 接下來設置一個默認配置的
OkHttp的DefaultOkHttpClient
class DefaultOkHttpClient:IClient {
private var mConnectionTimeOut = Consts.CONNECTION_TIMEOUT
private var mWriteTimeOut = Consts.CONNECTION_TIMEOUT
private var mReadTimeOut = Consts.CONNECTION_TIMEOUT
private var isRetryOnConnectionFailure = Consts.IS_RETRY_ON_CONNECTION_FAILURE
private var mCookieJar = getCookieJar()
private var mInterceptors : Array<Interceptor> = emptyArray()
override fun getClient(): OkHttpClient {
return OkHttpClient()
.newBuilder()
.connectTimeout(mConnectionTimeOut,TimeUnit.SECONDS)
.writeTimeout(mWriteTimeOut,TimeUnit.SECONDS)
.readTimeout(mReadTimeOut,TimeUnit.SECONDS)
.retryOnConnectionFailure(isRetryOnConnectionFailure)
.cookieJar(mCookieJar)
.addInterceptors(mInterceptors)
.build()
}
fun getCookieJar():CookieJar{
return object:CookieJar{
var mCookieStore:MutableMap<String,MutableList<Cookie>> = mutableMapOf()
override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
mCookieStore.put(url.host(),cookies)
}
override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
val cookies = mCookieStore[url.host()]
return cookies?: mutableListOf()
}
}
}
fun OkHttpClient.Builder.addInterceptors(mInterceptors : Array<Interceptor>):OkHttpClient.Builder{
if(mInterceptors.isNotEmpty()){
mInterceptors.forEach {
this.addInterceptor(it)
}
}
return this
}
fun setConnectionTimeOut(time:Long):DefaultOkHttpClient{
this.mConnectionTimeOut = time
return this
}
fun setWriteTimeOut(time:Long):DefaultOkHttpClient{
this.mWriteTimeOut = time
return this
}
fun setReaderTimeOut(time:Long):DefaultOkHttpClient{
this.mReadTimeOut = time
return this
}
fun isRetryOnConnectionFailure(isRetry:Boolean):DefaultOkHttpClient{
this.isRetryOnConnectionFailure = isRetry
return this
}
fun setCookieJar(cookieJar:CookieJar):DefaultOkHttpClient{
this.mCookieJar = cookieJar
return this
}
fun setInterceptors(interceptors:Array<Interceptor>):DefaultOkHttpClient{
this.mInterceptors = interceptors
return this
}
}
上面的getCookieJar方法可以實現(xiàn)在發(fā)送請求時候,CookieJar方法會回調(diào)loadForRequest把cookie加入request header里面,在請求響應的時候,Cookjar會回調(diào)saveFromResponse方法,從而讀取response header里面的cookie。這是只是簡單的配置下,這里暫時沒用到cookie的使用,在注入的時候還可以進一步cookie持久化和保存在本地,比如實現(xiàn)用戶的自動登錄功能。
上面還提供了請求時間的配置和攔截器的配置,這樣就可以在Application注入的時候進一步配置
GsonConverterFactory
創(chuàng)建Retrofit的時候,我們還需要傳入GsonConverterFactory,做為請求返回json使用Gson來使用,其中GsonConverterFactory可以傳入一個Gson對象,統(tǒng)一做一些數(shù)據(jù)的序列化和反序列化數(shù)據(jù)處理。其中TypeAdapter是同時對數(shù)據(jù)進行序列化處理和反序列化處理,或者單獨的通過JsonSerializer進行序列化,以及JsonDeserializer反序列化,如下
class GsonConverter {
fun <T> createGson():Gson{
return GsonBuilder()
.registerTypeAdapter(String::class.java, NullStringAdapter())
.registerTypeAdapter(Long::class.java, LongDeserializer())
.registerTypeAdapter(Double::class.java, DoubleDeserializer())
.registerTypeAdapter(Date::class.java, DateSerializer())
.registerTypeAdapter(Date::class.java, DateDeserializer())
.registerTypeAdapter(ResponseWrapper::class.java,ResponseWrapperDeserializer<T>())
.create()
}
}
創(chuàng)建GsonBuilder然后通過registerTypeAdapter注入需要處理的一些 TypeAdapter或者JsonSerializer和JsonDeserializer等等
private class NullStringAdapter : TypeAdapter<String>() {
override fun read(reader: JsonReader): String {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
return ""
}
return reader.nextString()
}
override fun write(writer: JsonWriter, value: String?) {
if (value == null) {
writer.nullValue()
return
}
writer.value(value)
}
}
NullStringAdapter對String類型的NULL轉(zhuǎn)換為"",
private class DateSerializer : JsonSerializer<Date> {
override fun serialize(src: Date?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement? {
return if (src == null) null else JsonPrimitive(src.time / 1000)
}
}
對Date數(shù)據(jù)類型序列化的時間戳轉(zhuǎn)換到精確到秒
private class DateDeserializer : JsonDeserializer<Date> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Date? {
return if (json == null || json.asLong == 0L) null else Date(json.asLong * 1000)
}
}
對Date數(shù)據(jù)類型反序列化的時間戳轉(zhuǎn)換到精確到毫秒
private class DoubleDeserializer : JsonDeserializer<Double> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Double {
return if (json == null || TextUtils.isEmpty(json.asString)) 0.0 else json.asDouble
}
}
對Double數(shù)據(jù)類型反序列化JSON返回NULL或者為空的時候返回默認值
private class LongDeserializer : JsonDeserializer<Long> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Long {
return if (json == null || TextUtils.isEmpty(json.asString)) 0 else json.asLong
}
}
對Long數(shù)據(jù)類型反序列化JSON返回NULL或者為空的時候返回默認值
private class ResponseWrapperDeserializer<T>:JsonDeserializer<ResponseWrapper<T>>{
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ResponseWrapper<T> {
val jsonObj = json.asJsonObject
val code = jsonObj.get("code").asInt
val msg = jsonObj.get("msg").asString
val version = jsonObj.get("version").asString
val timestamp = jsonObj.get("timestamp").asLong
val data = context.deserialize<T>(jsonObj.get("data"), (typeOfT as ParameterizedType).actualTypeArguments[0])
return ResponseWrapper(code,msg,version,timestamp,data)
}
}
這個主要是處理Java在編譯的時候會擦除泛型,如果不處理,在Obserable的時候就無法傳入泛型了。其中ResponseWrapper處理JSON的第一層統(tǒng)一樣式,比如這里的
{
"code":0,
"msg":"",
"version":"",
"timestamp",12324334,
"data":T
}
其中上面的data可以是對象,也可以是數(shù)組,所以這里我們返回的時候就可以用泛型傳入,ResponseWrapper如下
data class ResponseWrapper<out T>(val code:Int = -1,
val msg:String = "",
val version:String = "",
val timestamp:Long = 0,
@Transient val data: T): Serializable
Retrofit
OkHttp和Gson準備好后,接下來就可以創(chuàng)建Retrofit對象了。通過單例實現(xiàn)
object ApiService {
private var mIClient:IClient = DefaultOkHttpClient()
private val mRetrofitMap:MutableMap<String,Retrofit> = mutableMapOf()
fun <T> get(baseUrl: String, service: Class<T>): T {
return this.getRetrofit<T>(baseUrl).create(service)
}
private fun <T> getRetrofit(baseUrl: String):Retrofit{
if(baseUrl.isEmpty()){
throw IllegalArgumentException("baseUrl can not be empty")
}
if(mRetrofitMap[baseUrl] != null){
return mRetrofitMap[baseUrl]!!
}
val mRetrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(GsonConverter().createGson<T>()))
.client(mIClient.getClient()).build()
mRetrofitMap.put(baseUrl,mRetrofit)
return mRetrofit
}
fun registerClient(client:IClient){
this.mIClient = client
}
}
IClient是傳入的OkHttp對象,默認實例化DefaultOkHttpClient;
mRetrofitMap保存?zhèn)魅氲?code>BaseUrl對應的Retrofit對象,實現(xiàn)一一對應的關系;
get方法供外界調(diào)用傳入BaseUrl和接口Service,以及Response轉(zhuǎn)換的data對應的bean;
getRetrofit方法,當通過在mRetrofitMap找不到Retrofit對象的時候,就創(chuàng)建Retrofit對象。同時保存;
registerClient供在Application里面注入自定義的OkHttp對象
注入
準備好后接下來就是在Application的onCreate方法里面注入OkHttp實現(xiàn)一些攔截器
ApiService
.registerClient(DefaultOkHttpClient()
.setInterceptors(arrayOf(
OkHttpLogInterceptor().getInterceptor(),
BasicParamsInterceptor().getInterceptor(),
ResponseInterceptor().getInterceptor())))
其中OkHttpLogInterceptor是通過上面說的 okhttp3.logging庫創(chuàng)建的,主要是攔截http請求日志
class OkHttpLogInterceptor:IInterceptor {
override fun getInterceptor(): Interceptor {
val mHttpLogInter = HttpLoggingInterceptor{
message ->
Timber.d("HttpLogging=====$message")
}
mHttpLogInter.level = HttpLoggingInterceptor.Level.BODY
return mHttpLogInter
}
}
BasicParamsInterceptor是傳入一些公共的參數(shù),可以自己在注入的時候傳入,也可使用默認的一套公共參數(shù)
class BasicParamsInterceptor(val mQueryParameters : MutableMap<String,String>? = null):IInterceptor {
override fun getInterceptor(): Interceptor {
return Interceptor { chain ->
val originalRequest = chain.request()
val originalHttpUrl = originalRequest.url()
val newUrl = originalHttpUrl
.newBuilder()
.addQueryParameters(mQueryParameters?:defaultBaseParameters(originalHttpUrl))
.build()
val newRequest = originalRequest
.newBuilder()
.url(newUrl)
.method(originalRequest.method(),originalRequest.body())
.build()
chain.proceed(newRequest)
}
}
fun HttpUrl.Builder.addQueryParameters(mQueryParameters : MutableMap<String,String>):HttpUrl.Builder{
if(mQueryParameters.isNotEmpty()){
mQueryParameters.forEach {
this.addQueryParameter(it.key,it.value)
}
}
return this
}
fun defaultBaseParameters(originalHttpUrl:HttpUrl):MutableMap<String,String>{
return mutableMapOf("version" to Consts.API_VERSION,
"platform" to Consts.API_PLATFORM,
"methodName" to originalHttpUrl.encodedPath().split("/").last(),
"token" to "")
}
}
ResponseInterceptor是一些異常code處理,
class ResponseInterceptor(val handlerResponseException:((response:Response)->Unit)?=null):IInterceptor {
override fun getInterceptor(): Interceptor {
return Interceptor {
chain ->
val response = chain.proceed(chain.request())
handlerResponseException?.invoke(response)
when(response.code()){
200 -> response
10001 -> throw TokenExpiredException(response.code(), response.message())
else -> throw RequestException(response.code(), response.message())
}
}
}
}
其他的code處理都可以在這里處理,或者結(jié)合RxBus進行進一步的操作。
接口
定義一個接口
interface CommService {
@FormUrlEncoded
@POST("ArticleList")
fun articleList(@Field("page") page:Int = 0,@Field("size") size:Int = 15,@Field("id") id:Long): Observable<ResponseWrapper<ArticleListResponse>>
}
注意,返回的bean里面不能用泛型,否則會報錯,這里ArticleListResponse是json返回的數(shù)據(jù),這里只是隨便定義一些數(shù)據(jù)。
open class BaseResponse:Serializable
open class PageResponse : BaseResponse() {
var page = 0
var size = 0
var total = 0
}
class ArticleListResponse : PageResponse() {
val data: List<Item> = emptyList()
class Item(
val id: Long,
val title: String,
val image: String,
val desp: String,
) : Serializable
}
接下來提供一個AppModel提交請求
object AppModel {
fun articleList(pageInfo: PageInfo,id:Long = 0):Observable<ArticleListResponse>{
return ApiClient(CommService::class.java).articleList(pageInfo.page,pageInfo.size,id).responseWrapperLogic()
}
fun <T> ApiClient(service: Class<T>):T{
return ApiService.get(BuildConfig.BASE_URL,service)
}
private fun <T> Observable<ResponseWrapper<T>>.responseWrapperLogic() =
map { it.data}.compose{it.subscribeOn(Schedulers.io())}.observeOn(AndroidSchedulers.mainThread())
}
這里只是隨便定義接口方法而已,可以根據(jù)自己相應的接口添加。
使用
最簡單調(diào)用
AppModel.articleList(page,1).subscribeWith ({},{}).bindTo(mvpView.sub)
接下來可以進一步結(jié)合compose來寫請求的加載框或者其他加載動畫。其中subscribeWith是定義的一個DisposableObserver<T>,bindTo是結(jié)合 CompositeDisposable一起綁定多個Disposable,后面可以更好的管理綁定和解綁
fun <T> Observable<T>.subscribeWith(onNext:((res: T) ->Unit)?=null,
onError:((e: Throwable) ->Unit)?=null,
onComplete:(() ->Unit)?=null):DisposableObserver<T>{
return this.subscribeWith(object : DisposableObserver<T>() {
override fun onError(e: Throwable) {
if(onError!=null) onError(e)
}
override fun onComplete() {
if(onComplete!=null) onComplete()
}
override fun onNext(res: T) {
if(onNext!=null) onNext(res)
}
})
}
fun <T> DisposableObserver<T>.bindTo(sub: CompositeDisposable) {
sub.add(this)
}
mvpView.sub是在BasePresenter里面定義的CompositeDisposable對象,在下一篇Android搭建應用框架系列之MVP封裝進一步講解。