AndroidSDK开发6我用kotlin协程开发简单sdk
1.前言:
由于前面几篇文章讲解了sdk初始化、sdk设计原则、将moule或者项目打成aar、不用第三方库开发一个sdk等,目前市面上很少有使用sdk开发采用kotlin语言,本文讲解使用如何使用kolin开发一个简单sdk,当然这里有2个版本,由于最开始没有使用协程导致使用和代码还是很多,经过之前的经验和对新语言的使用情况,于是产生了加入协程的概念,当然其中也遇到不是问题,代码如下:
2.之前的文章参考:
3.kotlin依赖如下:
(如果不使用协程可以去掉协程的导包减少sdk包大小)
//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${kotlinx_coroutines_version}"
}
4.Application代码如下:
/**
* @auth: njb
* @date: 2022/4/27 15:56
* @desc: 描述
*/
class App : Application() {
override fun onCreate() {
super.onCreate()
context = applicationContext
}
companion object{
var context: Context by Delegates.notNull()
private set
}
}
5.常量工具类:
/**
* @auth: njb
* @date: 2022/4/25 11:43
* @desc: 常量工具类
*/
object Constants {
const val BASE_URL = "https://www.wanandroid.com"
const val DEFAULT_TIMEOUT = 20000 //10秒
const val TAG = "WanHttp"
//搜索
const val QUERY_ARTICLE = "/article/query/0/json"
//获取首页banner列表
const val BANNER_LIST = "/banner/json"
//搜索热词
const val HOT_KEY = "/hotkey/json"
}
6.日志工具类:
/**
* @auth: njb
* @date: 2022/4/26 14:26
* @desc: 日志工具类
*/
object LogUtils {
const val showLog:Boolean = true
fun i(tag: String, msg: String) {
if (showLog) Log.i(tag, msg)
}
fun d(tag: String, msg: String) {
if (showLog) Log.d(tag, msg)
}
fun w(tag: String, msg: String) {
if (showLog) Log.w(tag, msg)
}
fun e(tag: String, msg: String) {
if (showLog) Log.e(tag, msg)
}
}
7.网络状态工具类:
/**
* @auth: njb
* @date: 2022/4/27 11:45
* @desc: 网络状态工具类
*/
class NetWorkUtils {
companion object{
@get:RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
val isConnected: Boolean
get() {
val info = activeNetworkInfo
return info.isConnected
}
@get:RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
private val activeNetworkInfo: NetworkInfo
get() {
val cm = App.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return cm.activeNetworkInfo!!
}
}
}
8.初始化及管理工具类:
/**
* @auth: njb
* @date: 2022/4/23 22:39
* @desc: 描述
*/
class WanAndroidManager() {
var mContext:Context by Delegates.notNull()
fun init(app: Application){
mContext = app.applicationContext
}
}
9.网络请求工具类:
class WanHttpUtils {
companion object {
lateinit var connection: HttpURLConnection
fun doRequestPost(params: Map<String, String>): WanAndroidHttpResult<String> {
val result by lazy { WanAndroidHttpResult<String>() }
try {
runBlocking {
withContext(Dispatchers.Default) {
val url by lazy { URL(params["url"]) }
LogUtils.d(Constants.TAG, "$url --url--")
setConnection(url, params)
LogUtils.d(Constants.TAG, "$params --params--")
val requestParamJsonObject by lazy { params["body"] }
if (requestParamJsonObject != null && "" != requestParamJsonObject?.trim { it <= ' ' }) {
val bytes by lazy { requestParamJsonObject?.toByteArray() }
connection.setRequestProperty("Content-Length", bytes?.size.toString())
val outputStream by lazy { connection.outputStream }
outputStream.run {
write(bytes)
flush()
close()
}
}
setResult(responseCode = connection.responseCode, result)
}
}
} catch (e: Exception) {
showErrorMsg(result, e)
} finally {
connection.disconnect()
}
return result
}
fun doRequestGet(params: Map<String, String>): WanAndroidHttpResult<String>? {
val result by lazy { WanAndroidHttpResult<String>() }
runBlocking {
withContext(Dispatchers.Default) {
try {
val url by lazy { URL(params["url"]) }
setConnection(url, params)
setResult(responseCode = connection.responseCode, result)
} catch (e: Exception) {
showErrorMsg(result, e)
}
}
}
return result
}
private fun setResult(responseCode: Int, result: WanAndroidHttpResult<String>) {
result.run {
responseCode.run {
if (this == 200) {
isBizSucceed(true)
setData(getResponseDataString(connection.inputStream))
} else {
isBizSucceed(false)
setData("")
result.errorMsg =
"errorCode:" + responseCode + connection.responseMessage
}
}
}
}
private fun setConnection(
url: URL,
params: Map<String, String>,
) {
connection = (url.openConnection() as HttpURLConnection).apply {
readTimeout = DEFAULT_TIMEOUT
connectTimeout = DEFAULT_TIMEOUT
doOutput = true
doInput = true
requestMethod = params["Method"]
setRequestProperty("Connection", "Keep-Alive")
setRequestProperty("Charset", "UTF-8")
setRequestProperty("Content-Type", "application/json; charset=UTF-8")
setRequestProperty("accept", "application/json")
}
}
private fun showErrorMsg(result: WanAndroidHttpResult<String>, e: Exception) {
val msg: Any
msg = when {
!isConnected -> {
"网络连接失败,请检查网络"
}
e is SocketTimeoutException -> {
"网络请求超时,请稍后重试"
}
e is MalformedJsonException -> {
"数据解析失败"
}
else -> {
"请求失败,请稍后重试:" + e.message
}
}
result.errorMsg = msg
}
private fun getResponseDataString(inputStream: InputStream): String {
val inputStreamReader by lazy { InputStreamReader(inputStream) }
val bufferedReader by lazy { BufferedReader(inputStreamReader) }
val stringBuilder by lazy { StringBuilder() }
var temp: String?
try {
while (bufferedReader.readLine().also { temp = it } != null) {
stringBuilder.append(temp)
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
bufferedReader.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
inputStreamReader.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return stringBuilder.toString()
}
}
}
10.网络请求接口监听:
/**
* @auth: njb
* @date: 2022/6/10 15:59
* @desc: 描述
*/
interface OnResponseListener {
suspend fun onResponse(wanAndroidHttpResult: WanAndroidHttpResult<*>?)
}
11.网络请求结果解析工具类:
/**
* @auth: njb
* @date: 2022/4/22 15:53
* @desc: 描述
*/
class WanAndroidHttpResult<T> {
/**
* 记录请求回来的错误状态描述
*/
private var errorCode:Int ?=0
/**
* 记录请求回来的错误状态描述
*/
var errorMsg = ""
/**
* 记录返回的数据
*/
var data: T? = null
private set
/**
* 业务请求是否成功
*/
fun isBizSucceed(defaultValue: Boolean): Boolean {
return if (null == errorCode) defaultValue else errorCode == 0
}
fun setData(data: T) {
this.data = data
}
}
12.网络请求接口回调方法:
/**
* @auth: njb
* @date: 2022/4/22 15:33
* @desc: 描述
*/
interface WanHttpRequestCallBack {
fun onResponse(response: String?): Boolean?
fun onFailed(message: String?): Boolean?
}
13.网络请求线程工具类:
/**
* @auth: njb
* @date: 2022/6/6 10:06
* @desc: 描述
*/
open class WanRequestThread(
private val params: Map<String, String>,
private val listener: OnResponseListener
) {
private val lock by lazy { ByteArray(0) }
private var isCancel = false
suspend fun run() {
val result: WanAndroidHttpResult<String>? =
if (params["Method"] == "POST") WanHttpUtils.doRequestPost(params) else WanHttpUtils.doRequestGet(params)
if (!isCancel) {
listener.onResponse(result)
}
}
suspend fun cancel() {
synchronized(lock) { isCancel = true }
}
}
14.网络请求回调:
/**
* @auth: njb
* @date: 2022/6/6 10:27
* @desc: 描述
*/
class WanRequestListener {
companion object{
suspend fun getQueryArticle(responseListener: OnResponseListener, keyWord:String): WanRequestThread {
val params by lazy { HashMap<String, String>() }
val url by lazy { Constants.BASE_URL + Constants.QUERY_ARTICLE + "?k="+keyWord }
params["url"] = url
params["Method"] = "POST"
val requestThread by lazy { WanRequestThread(params, responseListener) }
requestThread.run()
return requestThread
}
}
suspend fun getHomeBanner(responseListener: OnResponseListener):WanRequestThread{
val params by lazy { HashMap<String, String>() }
val url by lazy { Constants.BASE_URL + Constants.BANNER_LIST }
params["url"] = url
params["Method"] = "GET"
val requestThread by lazy { WanRequestThread(params, responseListener) }
requestThread.run()
return requestThread
}
}
15.网络请求封装的具体方法:
/**
* @auth: njb
* @date: 2022/5/2 22:37
* @desc: 描述
*/
object WanHttpRequest {
suspend fun queryArticle(callBack: WanHttpRequestCallBack,keyWord:String){
WanRequestListener.getQueryArticle(object : OnResponseListener {
override suspend fun onResponse(wanAndroidHttpResult: WanAndroidHttpResult<*>?) {
if (wanAndroidHttpResult == null && wanAndroidHttpResult?.data == null) {
return
}
try {
val content by lazy { wanAndroidHttpResult.data.toString() }
wanAndroidHttpResult.run {
callBack.run {
if (isBizSucceed(true)) {
onResponse(content)
} else {
val message by lazy { wanAndroidHttpResult.errorMsg }
onFailed(message)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
},keyWord)
}
}
16.具体测试代码如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
getQueryData()
}
private fun getQueryData() {
GlobalScope.launch(Dispatchers.Main) {
WanHttpRequest.queryArticle(object : WanHttpRequestCallBack {
override fun onResponse(response: String?): Boolean? {
try {
val jsonObject by lazy { JSONObject(response) }
val jsonObject2 = jsonObject.getJSONObject("data")
val jsonArray by lazy { jsonObject2.getJSONArray("datas") }
val list: MutableList<ArticleListDataBean> = ArrayList()
if (jsonArray.length() > 0) {
for (i in 0 until jsonArray.length()) {
val datasBean = ArticleListDataBean()
val bannerObject: JSONObject = jsonArray.getJSONObject(i)
datasBean.run {
bannerObject.run {
id = bannerObject.getInt("id")
link = bannerObject.getString("link")
desc = bannerObject.getString("desc")
apkLink = bannerObject.getString("apkLink")
title = bannerObject.getString("title")
chapterName = bannerObject.getString("chapterName")
}
list.add(this)
}
}
CoroutineScope(Dispatchers.IO).launch {
list.forEach { it ->
Log.d("---MainActivity---", it.chapterName + " 数据获取成功 ")
tvTest.text = it.chapterName
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
override fun onFailed(message: String?): Boolean? {
return null
}
}, "Android")
}
}
}
17.运行效果截图和日志:
这里使用的是鸿洋大佬的WanAndroidApi,只是写了一个简单的接口测试,没有写具体的逻辑,如果想测试的同学随便新建一个项目,导入打好的aar包即可测试,至于怎么打包和导入新项目参考上面我的博客链接。
17.1 具体实现效果截图如下:
17.2 运行日志如下:
18.总结:
以上是今天的内容,其实就是使用kotlin协程简单的封装了一下网络请求,还有优化的空间,目前sdk开发中基本上很少有使用kotlin,当然有些可能已经使用了,这里我也只是简单写个demo,实际项目并没有使用,所以需要谨慎更换,如果本文对你有帮助,麻烦您给个点赞,原创不易,且看且珍惜,毕竟大环境不太好,写博客的就更少了,目前很多都是恰饭和水文,后面准备把aar自动和手动合并,sdk文档编写讲解一下,so加密还没研究透,所以暂时就不列入计划了!!
19.项目源码地址如下:
(分支选择dev_kotlin)