本人分成了四个类,其实可以更加精简,两个类足以
-
首先引入依赖 okhttp github地址
implementation("com.squareup.okhttp3:okhttp:4.9.0") -
上代码,首先展示一下请求代码(Kotlin)
DLHttp.post("url") //请求地址和请求方式,get和post方式切换
.add(map) //参数可以直接传入map集合
.add("key","value") //参数也可以传入键值对
.add(Object) //参数也可以类对象
.build(object : HttpCallBack<T>() { //传入返回的实体类
override fun success(t: T?) {
//得到返回的实体类
}
override fun error(code:Int,err: String?) {
//得到错误信息提醒
}
})
以上就是请求示例代码,够简单了吧,当然这都是表面的,下面来看一下背后做的工作
- 上关键类 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 描述:网络请求
*/
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请求
* @param url String? 地址
* @return DLHttp
*/
fun post(url: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.REQUEST_METHOD = dlHttp!!.REQUEST_POST
dlHttp!!.requestUrl = url
return dlHttp!!
}
/**
* put请求
* @param url String? 地址
* @return DLHttp
*/
fun put(url: String?): DLHttp {
instance()
getHttpClient()
dlHttp!!.REQUEST_METHOD = dlHttp!!.REQUEST_PUT
dlHttp!!.requestUrl = url
return dlHttp!!
}
/**
* get请求
* @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请求
private val REQUEST_GET = 1002 //get请求
private val REQUEST_PUT = 1003 //put请求
private var REQUEST_METHOD = REQUEST_GET //默认请求方式GET
private val JSON_TYPE = 301 //JSON格式提交
private val FILE_TYPE = 302 //提交文件
private val FORM_TYPE = 303 //表单提交
private var BUILD_TYPE = FORM_TYPE //默认表单提交
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
/**
* 请求header
*/
private var headers = Headers.Builder()
/**
* json实体类
*/
private var jsonObj: Any? = null
/**
* 参数
*/
private var params: MutableMap<String, Any?> = HashMap()
/**
* 文件参数
*/
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
}
/**
* 添加单一参数
* @param key String
* @param value Any
* @return DLHttp
*/
fun add(key: String, value: Any): DLHttp {
params[key] = value.toString()
return this
}
/**
* 添加参数集合
* @param map Map<String, Any?>
* @return DLHttp
*/
fun add(map: Map<String, Any?>): DLHttp {
params.putAll(map)
return this
}
/**
* 添加参数类
* @param map Map<String, Any?>
* @return DLHttp
*/
fun add(jsonObj: Any): DLHttp {
this.jsonObj = jsonObj
return this
}
/**
* 添加文件参数
* @param key String
* @param file File
* @return DLHttp
*/
fun add(key: String, file: File): DLHttp {
files.add(file)
return this
}
/**
* 以JSON的方式提交数据
* @param dlhttpCallBack IDLHttpCallBack<T>
*/
fun <T> buildByJson(dlhttpCallBack: IDLHttpCallBack<T>) {
BUILD_TYPE = JSON_TYPE
build<T>(dlhttpCallBack)
}
/**
* 基于http的文件上传(传入文件数组和key)混合参数和文件请求
* 通过addFormDataPart可以添加多个上传的文件
*/
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>) {
//使用默认header
for (entry in IBaseCallBack.headerMap) {
headers.add(entry.key, entry.value.toString())
}
//开始请求
dlHttpCallBack.Builder().startH()
//数据上传方式
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()
}
}
//数据上传方式
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 {
//请求错误
override fun onFailure(call: Call, e: IOException) {
dlHttpCallBack.Builder().errorH(-1, e.message)
}
//请求结果
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, "网络开小差,请稍后重试")
dlHttpCallBack.Builder().endH()
}
}
})
}
/**
* 下载文件******************************************************************************************************************************************
*/
/**
* 文件下载
*
* @param url path路径
* @param destFileDir 本地存储的文件夹路径
* @param myDataCallBack 自定义回调接口
*/
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
// 储存下载文件的目录
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()
}
}
}
这个类中,我做了符合我们项目的深度配置,但是大部分项目都差不多,所以如果需要适配你们的项目只需要修改
TIM截图20190803101224.png
这个部分就可以了
- 上配置类 IBaseCallBack,根据你项目的结构进行成功code,错误code,已经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对应的字段对应名
var msgStr = "message" //Message对应的字段对应名
var dataStr = "data" //data对应的字段对应名
var loginVoStr = "" //登录返回的数据
}
var data: T? = null
var msg: String? = null
var code: Int = 0
}
5.返回接口类,可以在这里面修改需要实现的方法
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 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
val superclass = javaClass.genericSuperclass
mType = if (superclass is Class<*>) {
null
} else {
//ParameterizedType参数化类型,即泛型
val parameterized = superclass as ParameterizedType?
//getActualTypeArguments获取参数化类型的数组,泛型可能有多个
//将Java 中的Type实现,转化为自己内部的数据实现,得到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出错
if (msg.obj != null) {
tokenError(msg.obj.toString())
}
end()
}
END -> end()
INFO -> info(msg.obj as String)
}
}
}
val START = 10001 //请求开始
val SUCCESS = 10002 //请求成功
val ERROR = 10003 //请求出错
val END = 10004 //请求结束
val INFO = 10005 //token出错
val TOKEN_ERROR = 10006 //token出错
}
}
- 为了更清晰,我把文件下载做了单独的返回
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) {
// 下载中更新进度条
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() //建造者模式设置不同的配置
.serializeNulls() //序列化为null对象
.setDateFormat("yyyy-MM-dd HH:mm:ss") //设置日期的格式
.disableHtmlEscaping() //防止对网址乱码 忽略对特殊字符的转换
.registerTypeAdapter(String::class.java, StringConverter()) //对为null的字段进行转换
.create()
/**
* 对解析数据的形式进行转换
*
* @param obj 解析的对象
* @return 转化结果为json字符串
*/
fun toJsonStr(obj: Any?): String {
return if (obj == null) {
""
} else try {
gson.toJson(obj)
} catch (e: Exception) {
""
}
}
/**
* 解析为一个具体的对象
*
* @param json 要解析的字符串
* @param obj 要解析的对象
* @param <T> 将json字符串解析成obj类型的对象
* @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 不区分类型 传什么解析什么
*/
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数组解析成相应的映射对象列表
* 解决类型擦除的问题
*/
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
}
}
/**
* 实现了 序列化 接口 对为null的字段进行转换
*/
internal class StringConverter : JsonSerializer<String?>, JsonDeserializer<String?> {
//字符串为null 转换成"",否则为字符串类型
@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'
以上就是全部代码了,第一次写文章,文字叙述和表达有不到位的地方,还请见谅,有问题可以留言
网友评论