本人分成了四個(gè)類,其實(shí)可以更加精簡(jiǎn),兩個(gè)類足以
已封裝可直接引用
DLTool: 基礎(chǔ)項(xiàng)目框架:OkHttp封裝、仿微信位置選擇等 (gitee.com)
implementation 'com.gitee.toune.DLTool:DLHttp:v1.3.1'//網(wǎng)絡(luò)請(qǐng)求框架
首先引入依賴 okhttp github地址
implementation("com.squareup.okhttp3:okhttp:4.9.0")上代碼,首先展示一下請(qǐng)求代碼(Kotlin)
DLHttp.post("url") //請(qǐng)求地址和請(qǐng)求方式,get和post方式切換
.add(map) //參數(shù)可以直接傳入map集合
.add("key","value") //參數(shù)也可以傳入鍵值對(duì)
.add(Object) //參數(shù)也可以類對(duì)象
.build(object : HttpCallBack<T>() { //傳入返回的實(shí)體類
override fun success(t: T?) {
//得到返回的實(shí)體類
}
override fun error(code:Int,err: String?) {
//得到錯(cuò)誤信息提醒
}
})
以上就是請(qǐng)求示例代碼,夠簡(jiǎn)單了吧,當(dāng)然這都是表面的,下面來看一下背后做的工作
- 上關(guān)鍵類 DLHttp :
package com.toune.dltools.http
import android.util.Log
import com.tamsiree.rxkit.RxFileTool
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLConnection
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
/**
* @Author Dong Lei
* @Date 2020/12/16 0016-下午 12:35
* @Info 描述:網(wǎng)絡(luò)請(qǐng)求
*/
class DLHttp {
companion object {
private var dlHttp: DLHttp? = null
/**
* 初始化
*/
fun instance() {
dlHttp = DLHttp()
}
/**
* 獲取client
*/
private fun getHttpClient() {
if (dlHttp != null) {
if (dlHttp!!.dlHttpClient == null) {
dlHttp!!.dlHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.MINUTES)
.callTimeout(30, TimeUnit.MINUTES)
.build()
}
}
}
/**
* post請(qǐng)求
* @param url String? 地址
* @return DLHttp
*/
fun post(url: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.REQUEST_METHOD = dlHttp!!.REQUEST_POST
dlHttp!!.requestUrl = url
return dlHttp!!
}
/**
* put請(qǐng)求
* @param url String? 地址
* @return DLHttp
*/
fun put(url: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.REQUEST_METHOD = dlHttp!!.REQUEST_PUT
dlHttp!!.requestUrl = url
return dlHttp!!
}
/**
* get請(qǐng)求
* @param url String?
* @return DLHttp
*/
fun get(url: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.REQUEST_METHOD = dlHttp!!.REQUEST_GET
dlHttp!!.requestUrl = url
return dlHttp!!
}
/**
* 下載文件
* @param realURL String?
* @param destFileDir String?
* @return DLHttp
*/
fun downFile(realURL: String?, destFileDir: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.downUrl = realURL
dlHttp!!.filrDir = destFileDir
return dlHttp!!
}
}
private val REQUEST_POST = 1001 //post請(qǐng)求
private val REQUEST_GET = 1002 //get請(qǐng)求
private val REQUEST_PUT = 1003 //put請(qǐng)求
private var REQUEST_METHOD = REQUEST_GET //默認(rèn)請(qǐng)求方式GET
private val JSON_TYPE = 301 //JSON格式提交
private val FILE_TYPE = 302 //提交文件
private val FORM_TYPE = 303 //表單提交
private var BUILD_TYPE = FORM_TYPE //默認(rèn)表單提交
private var dlHttpClient: OkHttpClient? = null
private var requestUrl: String? = null
private var downUrl: String? = null
private var filrDir: String? = null
private var downFileName = ""
private val JSONType: MediaType =
"application/json; charset=utf-8".toMediaTypeOrNull()!! //JSON的mediaType表頭
private var requestBody: RequestBody? = null
private var request: Request? = null
/**
* 請(qǐng)求header
*/
private var headers = Headers.Builder()
/**
* json實(shí)體類
*/
private var jsonObj: Any? = null
/**
* 參數(shù)
*/
private var params: MutableMap<String, Any?> = HashMap()
/**
* 文件參數(shù)
*/
private val files: MutableList<File> = ArrayList()
fun addHeader(map: Map<String, Any?>): DLHttp {
for (entry in map) {
headers.add(entry.key, entry.value.toString())
}
return this
}
fun addHeader(key: String, value: Any): DLHttp {
headers.add(key, value.toString())
return this
}
/**
* 添加單一參數(shù)
* @param key String
* @param value Any
* @return DLHttp
*/
fun add(key: String, value: Any): DLHttp {
params[key] = value.toString()
return this
}
/**
* 添加參數(shù)集合
* @param map Map<String, Any?>
* @return DLHttp
*/
fun add(map: Map<String, Any?>): DLHttp {
params.putAll(map)
return this
}
/**
* 添加參數(shù)類
* @param map Map<String, Any?>
* @return DLHttp
*/
fun add(jsonObj: Any): DLHttp {
this.jsonObj = jsonObj
return this
}
/**
* 添加文件參數(shù)
* @param key String
* @param file File
* @return DLHttp
*/
fun add(key: String, file: File): DLHttp {
files.add(file)
return this
}
/**
* 以JSON的方式提交數(shù)據(jù)
* @param dlhttpCallBack IDLHttpCallBack<T>
*/
fun <T> buildByJson(dlhttpCallBack: IDLHttpCallBack<T>) {
BUILD_TYPE = JSON_TYPE
build<T>(dlhttpCallBack)
}
/**
* 基于http的文件上傳(傳入文件數(shù)組和key)混合參數(shù)和文件請(qǐng)求
* 通過addFormDataPart可以添加多個(gè)上傳的文件
*/
fun <T> buildByFile(myDataCallBack: IDLHttpCallBack<T>) {
val multipartBody: MultipartBody.Builder = MultipartBody.Builder()
multipartBody.setType(MultipartBody.FORM)
for (key in params.keys) {
multipartBody.addFormDataPart(key, params[key].toString())
}
if (jsonObj != null) {
multipartBody.addPart(GsonBinder.toJsonStr(jsonObj).toRequestBody(JSONType))
}
var fileBody: RequestBody? = null
for ((index, file) in files.withIndex()) {
val fileName = file.name
fileBody = file.asRequestBody(guessMimeType(fileName).toMediaTypeOrNull())
multipartBody.addFormDataPart("file$index", fileName, fileBody)
}
requestBody = multipartBody.build()
BUILD_TYPE = FILE_TYPE
build<T>(myDataCallBack)
}
/**
* 獲取文件提交的guessMimeType
* @param fileName String
* @return String
*/
private fun guessMimeType(fileName: String): String {
val fileNameMap = URLConnection.getFileNameMap()
var contentTypeFor = fileNameMap.getContentTypeFor(fileName)
if (contentTypeFor == null) {
contentTypeFor = "application/octet-stream"
}
return contentTypeFor
}
fun <T> build(dlHttpCallBack: IDLHttpCallBack<T>) {
//使用默認(rèn)header
for (entry in IBaseCallBack.headerMap) {
headers.add(entry.key, entry.value.toString())
}
//開始請(qǐng)求
dlHttpCallBack.Builder().startH()
//數(shù)據(jù)上傳方式
when (BUILD_TYPE) {
JSON_TYPE -> {
if (jsonObj != null) {
requestBody = GsonBinder.toJsonStr(jsonObj).toRequestBody(JSONType)
} else if (params.isNotEmpty()) {
requestBody = GsonBinder.toJsonStr(params).toRequestBody(JSONType)
}
}
FILE_TYPE -> {
}
FORM_TYPE -> {
//form表單提交
val builder: FormBody.Builder = FormBody.Builder()
for (key in params.keys) {
builder.add(key, params[key].toString())
}
requestBody = builder.build()
}
}
//數(shù)據(jù)上傳方式
when (REQUEST_METHOD) {
REQUEST_GET -> {
val urlBuilder: HttpUrl.Builder = requestUrl!!.toHttpUrlOrNull()!!.newBuilder()
for (key in params.keys) {
urlBuilder.addQueryParameter(key, params[key].toString())
}
request = Request.Builder()
.url(urlBuilder.build())
.headers(headers.build())
.get()
.build()
}
REQUEST_POST -> {
request = Request.Builder()
.url(requestUrl!!)
.headers(headers.build())
.post(requestBody!!)
.build()
}
REQUEST_PUT -> {
request = Request.Builder()
.url(requestUrl!!)
.headers(headers.build())
.put(requestBody!!)
.build()
}
}
val call: okhttp3.Call = dlHttpClient!!.newCall(request!!)
call.enqueue(object : Callback {
//請(qǐng)求錯(cuò)誤
override fun onFailure(call: Call, e: IOException) {
dlHttpCallBack.Builder().errorH(-1, e.message)
}
//請(qǐng)求結(jié)果
override fun onResponse(call: Call, response: Response) {
try {
dlHttpCallBack.Builder().respenseH(response)
var jsonStr: String = response.body!!.string()
Log.e("urlAndJson:${response.request.url}", jsonStr)
if (response.code == 200) {
dlHttpCallBack.Builder()
.successH(GsonBinder.toObj(jsonStr, dlHttpCallBack.mType))
return
}
val baseObj = JSONObject(jsonStr)
if (baseObj != null) {
when (baseObj!!.getInt(IBaseCallBack.codeStr)) {
IBaseCallBack.SUCCESS_CODE -> {
if (DLHttpConfig.useDefaultStr) {
val iBaseCallBack = GsonBinder.toObj(
baseObj.getString(IBaseCallBack.dataStr),
IBaseCallBack::class.java
)
dlHttpCallBack.Builder().successH(iBaseCallBack!!.data as T)
} else {
dlHttpCallBack.Builder()
.successH(GsonBinder.toObj(jsonStr, dlHttpCallBack.mType))
}
}
IBaseCallBack.ERROR_CODE -> {
dlHttpCallBack.Builder()
.errorH(
baseObj.getInt(IBaseCallBack.codeStr),
baseObj.getString(IBaseCallBack.msgStr)
)
}
IBaseCallBack.TOKEN_ERROR_CODE -> {
dlHttpCallBack.Builder()
.tokenErrorH(baseObj.getString(IBaseCallBack.msgStr))
}
else -> {
dlHttpCallBack.Builder()
.errorH(
baseObj.getInt(IBaseCallBack.codeStr),
baseObj.getString(IBaseCallBack.msgStr)
)
}
}
} else {
dlHttpCallBack.Builder().successH(jsonStr as T)
}
dlHttpCallBack.Builder().infoH(baseObj.getString(IBaseCallBack.msgStr))
response.close()
dlHttpCallBack.Builder().endH()
} catch (e: JSONException) {
e.printStackTrace()
Log.e("urlAndJson:", "$response.request.url,${e.message}")
dlHttpCallBack.Builder().errorH(-2, "網(wǎng)絡(luò)開小差,請(qǐng)稍后重試")
dlHttpCallBack.Builder().endH()
}
}
})
}
/**
* 下載文件******************************************************************************************************************************************
*/
/**
* 文件下載
*
* @param url path路徑
* @param destFileDir 本地存儲(chǔ)的文件夾路徑
* @param myDataCallBack 自定義回調(diào)接口
*/
private var downCall: Call? = null
private val totalSize = 0L //APK總大小
private val downloadSize = 0L // 下載的大小
private val count = 0f //下載百分比
fun down(httpFileCallBack: IDLHttpFileCallBack) {
val request: Request = Request.Builder()
.url(downUrl!!)
.build()
downCall = dlHttpClient!!.newCall(request)
downCall!!.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
when (response.code) {
200 -> {
var `is`: InputStream? = null
val buf = ByteArray(2048)
var len = 0
var fos: FileOutputStream? = null
// 儲(chǔ)存下載文件的目錄
val dir: File = File(filrDir)
if (!dir.exists()) {
dir.mkdirs()
}
RxFileTool.createFileByDeleteOldFile(dir.toString() + downFileName)
var updateFile = RxFileTool.getFileByPath("$dir/$downFileName")
try {
`is` = response.body!!.byteStream()
val total: Long = response.body!!.contentLength()
httpFileCallBack.Builder().startH(total)
fos = FileOutputStream(updateFile)
var sum: Long = 0
while (`is`.read(buf).also { len = it } != -1) {
fos!!.write(buf, 0, len)
sum += len.toLong()
httpFileCallBack.Builder().progressH(sum)
}
fos!!.flush()
fos.close()
httpFileCallBack.Builder().successH(updateFile)
} catch (e: Exception) {
httpFileCallBack.Builder().errorH("下載失敗")
} finally {
try {
`is`?.close()
} catch (e: IOException) {
}
try {
fos?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
else -> {
httpFileCallBack.Builder().errorH("下載失敗")
}
}
}
override fun onFailure(call: Call, e: IOException) {
httpFileCallBack.Builder().errorH(e.message)
}
})
}
private fun getFileName(url: String?): String? {
val separatorIndex = url!!.lastIndexOf("/")
return if (separatorIndex < 0) url else url.substring(separatorIndex + 1, url.length)
}
fun cancelDownload() {
if (downCall != null) {
downCall!!.cancel()
}
}
}
這個(gè)類中,我做了符合我們項(xiàng)目的深度配置,但是大部分項(xiàng)目都差不多,所以如果需要適配你們的項(xiàng)目只需要修改
TIM截圖20190803101224.png
這個(gè)部分就可以了
- 上配置類 IBaseCallBack,根據(jù)你項(xiàng)目的結(jié)構(gòu)進(jìn)行成功code,錯(cuò)誤code,已經(jīng)token過期code的值的修改,還有字段名稱的修改
/**
* @Author Dong Lei
* @Date 2020/12/16 0016-下午 14:35
* @Info 描述:
*/
class IBaseCallBack<T> {
companion object {
val headerMap = HashMap<String, Any>()
var SUCCESS_CODE = 1
var ERROR_CODE = 0
var TOKEN_ERROR_CODE = 2001
var codeStr = "code" //code對(duì)應(yīng)的字段對(duì)應(yīng)名
var msgStr = "message" //Message對(duì)應(yīng)的字段對(duì)應(yīng)名
var dataStr = "data" //data對(duì)應(yīng)的字段對(duì)應(yīng)名
var loginVoStr = "" //登錄返回的數(shù)據(jù)
}
var data: T? = null
var msg: String? = null
var code: Int = 0
}
5.返回接口類,可以在這里面修改需要實(shí)現(xiàn)的方法
package com.toune.dltools.http
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import com.google.gson.internal.`$Gson$Types`
import com.tamsiree.rxkit.view.RxToast
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
abstract class IDLHttpCallBack<T> {
var mType: Type? = null
open fun start() {}
abstract fun success(t: T)
open fun error(code: Int, err: String?) {
RxToast.error(err!!)
}
open fun tokenError(err: String?) {
}
open fun info(info: String) {}
open fun end() {}
init {
//Type是 Java 編程語言中所有類型的公共高級(jí)接口。它們包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型。
val superclass = javaClass.genericSuperclass
mType = if (superclass is Class<*>) {
null
} else {
//ParameterizedType參數(shù)化類型,即泛型
val parameterized = superclass as ParameterizedType?
//getActualTypeArguments獲取參數(shù)化類型的數(shù)組,泛型可能有多個(gè)
//將Java 中的Type實(shí)現(xiàn),轉(zhuǎn)化為自己內(nèi)部的數(shù)據(jù)實(shí)現(xiàn),得到gson解析需要的泛型
`$Gson$Types`.canonicalize(parameterized!!.actualTypeArguments[0])
}
}
public inner class Builder {
fun startH() {
handler.sendEmptyMessage(START)
}
fun errorH(code: Int, err: String?) {
val message = Message()
val bundle = Bundle()
bundle.putInt("code", code)
bundle.putString("msg", err)
message.obj = bundle
message.what = ERROR
handler.sendMessage(message)
}
fun successH(data: T?) {
var message = Message()
message.obj = data
message.what = SUCCESS
handler.sendMessage(message)
}
fun endH() {
handler.sendEmptyMessage(END)
}
fun tokenErrorH(string: String) {
val message = Message()
message.obj = string
message.what = TOKEN_ERROR
handler.sendMessage(message)
}
fun infoH(string: String) {
val message = Message()
message.obj = string
message.what = INFO
handler.sendMessage(message)
}
@SuppressLint("HandlerLeak")
var handler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
START -> start()
SUCCESS -> success(msg.obj as T)
ERROR -> {
if (msg.obj != null) {
val bundle = msg.obj as Bundle
error(bundle.getInt("code"), bundle.getString("msg"))
}
end()
}
TOKEN_ERROR -> {
//token出錯(cuò)
if (msg.obj != null) {
tokenError(msg.obj.toString())
}
end()
}
END -> end()
INFO -> info(msg.obj as String)
}
}
}
val START = 10001 //請(qǐng)求開始
val SUCCESS = 10002 //請(qǐng)求成功
val ERROR = 10003 //請(qǐng)求出錯(cuò)
val END = 10004 //請(qǐng)求結(jié)束
val INFO = 10005 //token出錯(cuò)
val TOKEN_ERROR = 10006 //token出錯(cuò)
}
}
- 為了更清晰,我把文件下載做了單獨(dú)的返回
package com.toune.dltools.http
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.os.Message
import java.io.File
/**
* 下載文件返回
*/
abstract class IDLHttpFileCallBack {
abstract fun start(toatleSize: Int)
abstract fun progress(size: Int)
abstract fun success(file: File?)
abstract fun error(err: String?)
open fun end() {}
inner class Builder{
fun errorH(err: String?) {
val message = Message()
message.obj = err
message.what = FILE_ERROR
fileHandler.sendMessage(message)
}
fun startH(total: Long) {
var message = Message()
message.obj = total.toInt()
message.what = FILE_START
fileHandler.sendMessage(message)
}
fun progressH(sum: Long) {
// 下載中更新進(jìn)度條
var message = Message()
message = Message()
message.obj = sum.toInt()
message.what = FILE_PROGRESS
fileHandler.sendMessage(message)
}
fun successH(file: File?) {
// 下載完成
var message = Message()
message = Message()
message.obj = file
message.what = FILE_SUCCESS
fileHandler.sendMessage(message)
}
var fileHandler: Handler = @SuppressLint("HandlerLeak")
object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
FILE_START -> start(msg.obj as Int)
FILE_PROGRESS -> progress(msg.obj as Int)
FILE_SUCCESS -> success(msg.obj as File)
FILE_ERROR -> {
if (msg.obj != null) {
error(msg.obj.toString())
}
end()
}
FILE_END -> end()
}
}
}
private val FILE_START = 100001
private val FILE_SUCCESS = 100002
private val FILE_ERROR = 100003
private val FILE_END = 100004
private val FILE_PROGRESS = 100005
}
}
GsonBinder類
package com.toune.dltools.http
import android.text.TextUtils
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
import kotlin.jvm.Throws
/**
* Created by Administrator on 2018/1/11 0011.
*/
object GsonBinder {
//定義并配置gson
private val gson: Gson = GsonBuilder() //建造者模式設(shè)置不同的配置
.serializeNulls() //序列化為null對(duì)象
.setDateFormat("yyyy-MM-dd HH:mm:ss") //設(shè)置日期的格式
.disableHtmlEscaping() //防止對(duì)網(wǎng)址亂碼 忽略對(duì)特殊字符的轉(zhuǎn)換
.registerTypeAdapter(String::class.java, StringConverter()) //對(duì)為null的字段進(jìn)行轉(zhuǎn)換
.create()
/**
* 對(duì)解析數(shù)據(jù)的形式進(jìn)行轉(zhuǎn)換
*
* @param obj 解析的對(duì)象
* @return 轉(zhuǎn)化結(jié)果為json字符串
*/
fun toJsonStr(obj: Any?): String {
return if (obj == null) {
""
} else try {
gson.toJson(obj)
} catch (e: Exception) {
""
}
}
/**
* 解析為一個(gè)具體的對(duì)象
*
* @param json 要解析的字符串
* @param obj 要解析的對(duì)象
* @param <T> 將json字符串解析成obj類型的對(duì)象
* @return
</T> */
fun <T> toObj(json: String?, obj: Class<T>?): T? {
//如果為null直接返回為null
return if (obj == null || TextUtils.isEmpty(json)) {
null
} else try {
gson.fromJson(json, obj)
} catch (e: Exception) {
null
}
}
/**
* @return 不區(qū)分類型 傳什么解析什么
*/
fun <T> toObj(jsonStr: String?, type: Type?): T? {
var t: T? = null
try {
t = gson.fromJson(jsonStr, type)
} catch (e: Exception) {
e.printStackTrace()
}
return t
}
/**
* 將Json數(shù)組解析成相應(yīng)的映射對(duì)象列表
* 解決類型擦除的問題
*/
fun <T> toList(jsonStr: String?, clz: Class<T>): List<T> {
var list: List<T> = gson.fromJson(jsonStr, type(clz))
if (list == null) list = ArrayList()
return list
}
fun <T> toMap(jsonStr: String?, clz: Class<T>): Map<String?, T> {
var map: Map<String?, T> = gson.fromJson(jsonStr, type(clz))
if (map == null) map = HashMap()
return map
}
fun toMap(jsonStr: String?): Map<String, Any> {
val type: Type = object : TypeToken<Map<String?, Any?>?>() {}.type
return gson.fromJson(jsonStr, type)
}
private class type(private val type: Type) : ParameterizedType {
override fun getActualTypeArguments(): Array<Type> {
return arrayOf(type)
}
override fun getRawType(): Type {
return ArrayList::class.java
}
override fun getOwnerType(): Type? {
return null
}
}
/**
* 實(shí)現(xiàn)了 序列化 接口 對(duì)為null的字段進(jìn)行轉(zhuǎn)換
*/
internal class StringConverter : JsonSerializer<String?>, JsonDeserializer<String?> {
//字符串為null 轉(zhuǎn)換成"",否則為字符串類型
@Throws(JsonParseException::class)
override fun deserialize(
json: JsonElement,
typeOfT: Type?,
context: JsonDeserializationContext?
): String {
return json.getAsJsonPrimitive().getAsString()
}
override fun serialize(
src: String?,
typeOfSrc: Type?,
context: JsonSerializationContext?
): JsonElement {
return if (src == null || src == "null") JsonPrimitive("") else JsonPrimitive(src.toString())
}
}
}
GsonBinder類需要引用 implementation 'com.google.code.gson:gson:2.8.5'
以上就是全部代碼了,第一次寫文章,文字?jǐn)⑹龊捅磉_(dá)有不到位的地方,還請(qǐng)見諒,有問題可以留言