1. 程式人生 > >Android 網路訪問框架retrofit2,okhttp3之簡單封裝,kotlin原始碼

Android 網路訪問框架retrofit2,okhttp3之簡單封裝,kotlin原始碼

本文章需要用到的引用

//okhttp
compile 'com.squareup.okhttp3:okhttp:3.8.0'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'
//rxjava
compile 'io.reactivex.rxjava2:rxjava:2.0.7'
//rxjava介面卡,方便rxjava與retrofit的結合
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
//Log攔截器,方便DEBUG模式輸出log資訊
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
//json轉換器,方便將返回的資料轉換為json格式 compile 'com.squareup.retrofit2:converter-gson:2.3.0' //rxandroid compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.github.bumptech.glide:glide:3.7.0'

定義:

1.基於retrofit2和okhttp3的網路訪問簡單封裝。

基本封裝類

RetrofitClient.kt
import android.content.Context
import android.util.Log
import java.io.File
import okhttp3.Cache
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.Retrofit
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit

/**
 * @author Created by qlang on 2017/7/14.
 */
class RetrofitClient private constructor(context: Context, baseUrl: String) {
    var httpCacheDirectory: File? = null
    val mContext: Context = context
    var cache: Cache? = null
    var okHttpClient: OkHttpClient? = null
    var retrofit: Retrofit? = null
    val DEFAULT_TIMEOUT: Long = 20
    val url = baseUrl

    init {
        //快取地址
        httpCacheDirectory = httpCacheDirectory ?: File(mContext.cacheDir, "app_cache")
        
        try {
            cache = cache ?: Cache(httpCacheDirectory, 10 * 1024 * 1024)
        } catch (e: Exception) {
            Log.e("OKHttp", "Could not create http cache", e)
        }
        //okhttp建立了
        okHttpClient = OkHttpClient.Builder()
                .addNetworkInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                .cache(cache)
                .addInterceptor(CacheInterceptor(context))
                .addNetworkInterceptor(CacheInterceptor(context))
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build()
        //retrofit建立了
        retrofit = Retrofit.Builder()
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(url)
                .build()
    }

    companion object {
        @Volatile
        var instance: RetrofitClient? = null
        var context: Context? = null

        fun init(context: Context) {
            this.context = context
        }

        fun getInstance(baseUrl: String): RetrofitClient {
            return instance ?: synchronized(RetrofitClient::class) {
                    if (context == null) throw NullPointerException("context is null.Use init(content) in your Application")
                    RetrofitClient(context!!, baseUrl).also{ instance = it }
                }
            }
        }
    }

    fun <T> create(service: Class<T>?): T? {
        if (service == null) throw RuntimeException("Api service is null!")
        return retrofit?.create(service)
    }
}

快取輔助類:

CacheInterceptor.kt
import android.content.Context
import android.util.Log
import com.ql.ufun.utils.NetworkUtils
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.CacheControl

/**
 * @author Created by qlang on 2017/7/14.
 */
class CacheInterceptor(context: Context) : Interceptor {
    val context = context
    override fun intercept(chain: Interceptor.Chain?): Response? {
        var request = chain?.request()
        if (NetworkUtils.isNetConneted(context)) {
            val response = chain?.proceed(request)
            // read from cache for 60 s
            val maxAge = 60
            val cacheControl = request?.cacheControl().toString()
            Log.e("CacheInterceptor", "6s load cahe" + cacheControl)
            return response?.newBuilder()?.removeHeader("Pragma")?.removeHeader("Cache-Control")?.header("Cache-Control", "public, max-age=" + maxAge)?.build()
        } else {
            Log.e("CacheInterceptor", " no network load cahe")
            request = request?.newBuilder()?.cacheControl(CacheControl.FORCE_CACHE)?.build()
            val response = chain?.proceed(request)
            //set cahe times is 3 days
            val maxStale = 60 * 60 * 24 * 3
            return response?.newBuilder()?.removeHeader("Pragma")?.removeHeader("Cache-Control")?.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)?.build()
        }
    }

}

使用:

1.定義介面

ApiServer.kt
interface ApiServer {
    companion object {
        val BASE_URL: String get() = "http://route.xxx.com/"
    }

    @GET("xxxxxx") 
    fun getHomeTxtData(@Query("xxx") apiid: String, @Query("xxx") sign: String, @Query("currPage") page: String, @Query("maxResult") num: String): Observable<Xxxxx>

2.定義Base Model

BaseModel.kt
import com.ql.xxx.net.ApiServer
import com.ql.xxx.net.RetrofitClient
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * @author Created by qlang on 2017/7/18.
 */
open class BaseModel {
    fun buildServer(): RetrofitClient {
        return RetrofitClient.getInstance(ApiServer.BASE_URL)
    }

    fun <T> Observable<T>.applySchedulers(): Observable<T> {
        return subscribeOn(Schedulers.io()).
                unsubscribeOn(Schedulers.io()).
                observeOn(AndroidSchedulers.mainThread())
    }
}

3.具體的Model
MainModel.kt
/**
 * @author Created by qlang on 2017/7/18.
 */
class MainModel : BaseModel() {

    fun loadTextData(index: Int): Observable<Xxxxx>? {
        val server = buildServer().create(ApiServer::class.java)
        return server?.getHomeTxtData(ApiServer.API_ID, ApiServer.API_SIGN, "$index", "20")?.applySchedulers()
    }
}

4.發起請求
val model: MainModel by lazy {
        MainModel()
    }
val observable: Observable<Xxxxx>? = model.loadTextData(index)
        observable?.subscribe({ bean: Xxxxx -> listener?.onSuccRespone(bean) },
                { err: Throwable -> listener?.onErrRespone(err) })


附:
基於Glide的圖片載入簡單封裝

ImageLoader.kt
import android.content.Context
import android.graphics.Bitmap
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import com.ql.ufun.R

/**
 * @author Created by qlang on 2017/7/19.
 */
class ImageLoader {

    interface BitmapListener {
        fun onCall(bitmap: Bitmap?)
    }

    companion object {
        fun <T : ImageView> loadImg(context: Context, url: String, imageView: T?) {
            loadImg(context, url, imageView, R.mipmap.ic_default_img, R.mipmap.ic_default_img)
        }

        fun <T : ImageView> loadBigImg(context: Context, url: String, imageView: T?) {
            if (imageView == null) {
                throw IllegalArgumentException("argument error")
            }
            Glide.with(context).load(url)
                    .asBitmap()
                    .format(DecodeFormat.PREFER_ARGB_8888)
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .placeholder(R.mipmap.ic_default_img)
                    .error(R.mipmap.ic_default_img)
                    .into(imageView)
        }

        fun <T : ImageView> loadImg(context: Context, url: String, imageView: T?, defaultImageResId: Int, errImageResId: Int) {
            if (imageView == null) {
                throw IllegalArgumentException("argument error")
            }
            Glide.with(context).load(url)
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .placeholder(defaultImageResId)
                    .error(errImageResId)
                    .crossFade().into(imageView)
        }

        fun loadImg(context: Context, url: String, listener: BitmapListener?) {
            Glide.with(context).load(url)
                    .asBitmap()
                    .format(DecodeFormat.PREFER_ARGB_8888)
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(object : SimpleTarget<Bitmap>() {
                        override fun onResourceReady(resource: Bitmap?, glideAnimation: GlideAnimation<in Bitmap>?) {
                            listener?.onCall(resource)
                        }
                    })
        }
    }
}