Kotlin协程请求网络封装,超级简单(协程+Retrofit2.6.0(或以上)+MVP模式)

前言:这文章跟上一个文章同用一个demo,为了区分还是分开比较方便说明

参考:大部分参考这位大佬的https://www.jianshu.com/p/f5f7b9750360

1. 前置准备

1.1 加入依赖build.gradle

// 网络请求类
//1.retrofit2.6.0
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
// implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
//2.协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

1.2 权限AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

1.3 常量类

/**
 * @ObjectName: Constants
 * @Description:定义的常量,URL等
 * @Author: chemoontheshy
 * @Date: 2021/3/11-13:42
 */
object Constants {
    const val WEATHER_URL_TEST =  "https://api.muxiaoguo.cn/"
    const val WEATHER_URL = "https://api.muxiaoguo.cn/"
}

2. http类封装

2.1 HttpUtils

说明createApi的方法可能跟传统的不一样,这里是用泛型代替了指定好的方法,减少了耦合。

GsonConverterFactory.create()也不可以省略,好像省略会报错,不能解析返回的数据。

/**
 * @ClassName: HttpUtils
 * @Description: 封装网络请求的方法,这里是Retrofit2.60以上的方法
 * @Author: chemoontheshy
 * @Date: 2021/3/11-13:46
 */
object HttpUtils {
    /**
     * @name: isTest
     * @Description: 判断是否是test的API接口,方便可以省略
     */
    private fun isTest(isTest:Boolean):String = if(isTest) WEATHER_URL_TEST else WEATHER_URL

    fun<T>createApi(clazz: Class<T>):T = Retrofit.Builder()
        .baseUrl(isTest(true))
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(clazz)
}

2.2 UserApi

  • 具体看注释
  • 具体内容需要看请求API的结构,这里是用了木小果的API的天气查询。
/**
 * @InterfaceName: UserApi
 * @Description:请求API的接口,可以多个接口,根据请求的不同
 * @Author: chemoontheshy
 * @Date: 2021/3/11-13:46
 */
interface UserApi {
    /**
     * 注意问题:
     * 1. 加入@FormUrlEncoded 只支持x-www-form-urlencoded不支持form-data所致,时加入这个
     *    如果不加人,会报错,正常请求应该不需要加
     * 2. BaseModel<MainModel>:BaseModel<T>是Model的基类,参考另外文章的data class
     * 3. MainModel是activity的MVP里的M层,具体内容是请求api返回的格式
     */
    @FormUrlEncoded
        @POST("api/tianqi")
        suspend fun getWeather(
            @Field("city") city: String,
            @Field("type") type: Int
        ): BaseModel<MainModel>
}

3. MainModel

这里的结构也是根据请求API的结构所编写的,这里是用了木小果的API的天气查询。

返回类容

{"code":"200","msg":"success","data":{"cityname":"长沙","nameen":"changsha","temp":"19","WD":"北风","WS":"1级","wse":"12km/h","SD":"50%","weather":"多云","pm25":"39","limitnumber":"","time":"14:00"}}
/**
 * @ClassName: MainModel
 * @Description:这里是根据API返回值,编写的data class
 * @Author: chemoontheshy
 * @Date: 2021/3/11-14:56
 */
data class MainModel(val cityname:String,
                     val nameen:String,
                     val temp:String,
                     val WD:String,
                     val WS:String,
                     val wse:String,
                     val SD:String,
                     val weather:String,
                     val pm25:String,
                     val limitnumber:String,
                     val time:String)

4. MainPresenter

这里直接调用时不行的,需要引入CoroutineScope by MainScope()但是这里为了方便已经提取到基类里。

分析

  • launch 是协程里子线程的挂起,反正相当于创建一个子线程,不会阻塞主线程,另外还有async
  • exceptionHandler 是请求过程中发生错误,处理异常的方法
  • response:BaseModel<MainModel> 是通过基类BaseModel,和实际的M层组成“返回类容”
  • HttpUtils.createApi(UserApi::class.java).getWeather("长沙",1)UserApi相当于之前封装好的里的<T>
/**
 * @ClassName: MainPresenter
 * @Description:P层,提供不涉及数据M层的方法,和涉及M层的方法
 * @Author: chemoontheshy
 * @Date: 2021/3/11-14:56
 */
class MainPresenter: BasePresenter<MainView>() {
    fun setText(){
        getBaseView()?.setData("test")
    }

    /**
     * launch 是协程里子线程的挂起,反正相当于创建一个子线程,不会阻塞主线程,另外还有async
     * exceptionHandler 是请求过程中发生错误,处理异常的方法
     * response:BaseModel<MainModel> 是通过基类BaseModel,和实际的M层组成“返回类容”
     * HttpUtils.createApi(UserApi::class.java).getWeather("长沙",1)
     */
    fun getWeather(){
        launch (exceptionHandler){
            val response:BaseModel<MainModel> = HttpUtils.createApi(UserApi::class.java).getWeather("长沙",1)
            getBaseView()?.setData(response)
        }
    }
}

附1:BasePresenter更新

这里这样写是为了区分前面MVP文章,不一定是使用协程来请求,但是使用协程请求最好还是提取到基基类里面的意思。

/**
 * @ClassName: BasePresenter
 * @Description:Presenter的基类,主要是绑定,解绑,提供方法接口,增加协程
 * @Author: chemoontheshy
 * @Date: 2021/3/11-14:01
 */
open class BasePresenter<V> : CoroutineScope by MainScope(){
    /**
     * 在基类里,提供协程出现错误的调用
     */
    val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        Log.e(ContentValues.TAG, "coroutine: error ${throwable.message}")
    }

    private var mBaseView:V? = null
    /**
     * 绑定View
     */
    fun bindView(mBaseView:V){
        this.mBaseView = mBaseView
    }
    /**
     * 解绑View
     */
    fun unbindView(){
        this.mBaseView = null
    }
    /**
     * 返回方法的接口
     */
    fun getBaseView() = mBaseView
}

附2 :关于response的解析和data class的使用

Log.e(TAG,response.toString())这里是直接打印response。

返回:

BaseModel(code=200, message=null, data=MainModel(cityname=长沙, nameen=changsha, temp=19, WD=北风, WS=1级, wse=12km/h, SD=52%, weather=多云, pm25=39, limitnumber=, time=14:20))

可以分析出,根据BaseModel就是网络请求的基本三个要素,code 、message、data,(事实上这个木小果的API,的基本是code 、mesg、data,所以这里返回了null,根据上面的“返回类容”如果基类变成mgs,应该返回success而不是null,data就是MainModel的数据结构。

至于怎样调用呢,就方便,比如需要提取城市名 cityname和当地温度 temp可以这提出

val cityName = response.data.cityname
val temp = response.data.temp
Log.e(TAG,cityName+temp)

这样就可以获取返回的数据了。另外data class有自带的方法,这里就不演示了。

5 总结

到此,就完全使用了MVP,并使用协程+Retrofit2.6.0封装了网络请求,很方便。

思路主要是封装好HttpUtils,至于UserApi和M层基类和M层基本根据请求的API编写,然后根据MVP的思维调用即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容