文章目录
简介
开始之前让笔者在此对项目中用到的一些基本概念做一些基本的介绍方便大家更好的理解此博客
-
Kotlin是由JetBrains创建的基于JVM的静态编程语言。包含了很多函数式的编程思想的面向对象的的编程语言。kotlin仿佛是为了解决java中存着的一些天生的缺陷而量身定做一般。精简的语法,null-safety,相对应java8更进一步的lambda表达式支持。使用block完美的代替了接口回调等等。使得开发者可以编写尽量少的样板代码。谷歌爸爸早就在2017年就已经宣布kotlin为安卓的官方开发语言了。身为安卓开发从业人员,学习kotlin很有必要。
-
协程也叫微线程,是一种新的多任务并发的操作手段。 协程是创造出来解决异步问题,线程的调度是操作系统层面完成是抢占式的;协程是非抢占式的,是协作运行的,是由应用层完成调度,协程在挂起的时候不会堵塞线程,只会将当前运行的状态存在内存中,当协程执行完成后,会恢复之前保存的状态继续运行。协程的内部是通过状态机实现的。
协程具有以下特性- 可控制:协程能做到可被控制的发起子任务(协程的启动和停止都由代码控制,不像 java)
- 轻量级:协程非常小,占用资源比线程还少
- 语法糖:使多任务或多线程切换不再使用回调语法
本框架中使用协程封装来实现异步的网络请求。
-
Retrofit是Square 公司开发的一款正对Android 网络请求的框架。底层基于OkHttp 实现。Retrofit 负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp 负责请求的过程。异步的实现采用协程实现
-
MVP目前主要的安卓开发框架有mvc,mvp,mvvm,mvc因为代码的冗余度较高,逐渐被淘汰了。mvvm,笔者之前也自己搭建过mvvm的框架,发现在databing的时候有时候出现的资源文件错误无法准确定位问题。所以暂时项目用的还是mvp(个人感觉项目较小没有使用mvvm的必要)。
项目的架构
项目中新建了一个底层的baseUI的library。上层的所有的module均依赖于这个baseUI lib。
Mvp的具体实现
主要包括BaseMvpActivity,BaseMvpPresenter,BaseMvpView
- BaseMvpActivity
-
BaseMvpPresenter
-
BaseMvpView BaseMvpView中主要定义了一些view中常用的方法
协程的使用与封装
- 协程的引入
- 首先我们需要在baseUI中引入相应的库
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1’
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1’
如图
- 首先我们需要在baseUI中引入相应的库
- 我们需要在gradle脚本中添加coroutines支持
kotlin {
experimental {
coroutines ‘enable’
}
}
- 协程的封装
我们新增一个KotlinPresenter的类继承自BaseMvpPresenter用于在底层封装网络请求和对请求结果做出相应的处理。
在KotlinPresenter中我们定义一个CoroutineScope对象。view中所有的协程操作需要presenterScope在实现。
val presenterScope: CoroutineScope by lazy {
CoroutineScope(Dispatchers.Main + Job())
}
网络请求的封装,我们在KotlinPresenter定义了一个launchRequest方法。
launchRequest方法中有四个函数作为参数。
tryBlock:返回的是一个由Result包裹的泛型参数。主要用于网络请求的调用。
successBlock:主要用于请求成功的调用。相当要onSuccess回调。
catchBlock:主要用于请求失败的调用。相当于onError回调。
finallyBlock:主要用于请求完成的回调。相当于onComplete回调
这里调用了一个launchOnUI方法。因为retrofit已经做了线程切换,所以我们不需要切换线程了。
requestTryCatch方法中对协程进行了tryCatch操作。并对结果进行了相应的处理
callResponse,用于对请求的结果进行处理。
这里我们还需要注意一个问题,就是当你的页面被销毁,如果你的协程没有被销毁,就会产生一些不可预知的后果,所以我们要在页面销毁时,将协程关闭。
具体的操作是这样的
重写父类的detachView方法
这样就可以了。
Retrofit的使用
retrofit的使用和封装相信大家都很了解了,这里就不多讲了,直接贴出代码来吧
// An highlighted block
interface ApiServices {
/**
* 用户登录
*/
@GET("login?key=00d91e8e0cca2b76f515926a36db68f5")
fun requestLoginOut( @Query("phone") phone: String,
@Query("passwd") passwd: String): Deferred<Result<LoginBean>>
@GET("createUser?key=00d91e8e0cca2b76f515926a36db68f5")
fun requestRegister(
@Query("phone") phone: String,
@Query("passwd") passwd: String
): Deferred<Result<RegisterBean>>
}
ApiHelper初始化okhttp,和Retrofit。
// An highlighted block
object ApiHelper {
private var api: ApiServices? = null
fun api(): ApiServices? {
if (api == null)
initApi()
return api
}
/**
* 初始化api
*/
fun initApi() {
// Header
val headerInter = Interceptor { chain ->
val builder = chain.request()
.newBuilder()
chain.proceed(builder.build())
}
val mOkHttpClient = OkHttpClient()
.newBuilder()
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(headerInter)
.addInterceptor(LoggingInterceptor())
.build()
//网络接口配置
api = null
api = Retrofit.Builder()
.baseUrl("https://www.apiopen.top/")
.addConverterFactory(ScalarsConverterFactory.create()) //添加字符串的转换器
.addConverterFactory(GsonConverterFactory.create()) //添加gson的转换器
.addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //添加携程的请求适配器 .client(mOkHttpClient)
.client(mOkHttpClient)
.build()
.create(ApiServices::class.java)
}
}
希望大家注意一下这里的Deferred
这里的Deferred用于接收一个Coroutine的返回结果。
协程的请求适配器,需要引入“com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2
”
网络请求的实现
现在我们可以来使用我们封装的协程来实现网络请求了。
在presenter层中定义了两个请求。案例如下
// An highlighted block
override fun requestLogin() {
view?.showWaitDialog()
launchRequest(
{
ApiHelper.api()?.requestLoginOut(phone,password)?.await()
},
{ loginBean: LoginBean? ->
view?.requestLoginSuccess(loginBean)
},
{ errMsg: String? ->
view?.requestError(errMsg)
},
{
view?.hideWaitDialog()
})
}
override fun requestRegister() {
view?.showWaitDialog()
launchRequest(
{ ApiHelper.api()?.requestRegister(phone,password)?.await() },
{ register: RegisterBean? ->
view?.requestRegisterSuccess(register)
},
{ errMsg: String? ->
view?.requestError(errMsg)
},
{
view?.hideWaitDialog()
}
)
}
怎么样,是不是很简洁舒适,没有了rxjava多重的链式api调用。不需要定义回调函数。代码也一目了然。
如果我们需要在view中运用到异步,同样可以使用presenter层中的定义的presenterScope。例如我们需要下载一张照片,然后在显示。
// An highlighted block
presenterScope.launch {
try {
var bitmap = withContext(Dispatchers.IO) {
val bitmap = Glide.with(view.getmActivity())
.load(path)
.asBitmap().skipMemoryCache(false)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get()
bitmap
}
withContext(Dispatchers.Main) {
GlideUtils.load(
view.getmActivity(),
ivAvata,
bitmap,
GlideCircleTransform(view.getmActivity())
)
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
项目地址
此项目已经托管到github并且开源。如果想看源码点击传送门查看。如果觉得还可以,不妨给在下一个star。谢谢大家。
总结
本片文章主要是为了用协程代替RxJava实现网络请求的异步。打造Kotlin+协程+retrofit+mvp的开发架构。
协程作为一种轻量级的用户态的线程。还有很多的语法糖和亮点可以供我们使用和借鉴。后期,我还会跟大家一起探讨协程的实现机制和用途。