1、前言

虽然有了解kotlin,但是一直没在项目中使用,最近为了学习Jetpack+组件化,新开了个练习项目,顺便学一下kotlin,记录下学习路线。

2、学习路线

2.1、语言学习

主要学一下基本语法:

官方中文文档https://www.kotlincn.net/docs/reference/

2.2、Android中的学习

kotlin在android中的使用:

google官方文档https://developer.android.google.cn/kotlin/add-kotlin

看完差不多够用了

2.3、协程

协程可以用同步的方式编写异步的代码(减少回调),在Android中如网络请求,文件io,数据库等。协程比线程更轻量,线程是全局的,但是协程可以指定作用域,在Android中利用viewModelScope或者lifecycleScope可以感知activity生命周期,并自动取消协程。

viewModelScope.launch{
    //主线程启动一个协程
    val data = repository.fetchData()  //网络请求
    //更新UI
    textView.text = data.title
}

更多可以参考google开源项目sunflower

2.4、扩展函数

kotlin提供了很多扩展函数,比如apply、run等,也可以自定义,如

/**
 * 图片加载扩展方法
 */
fun ImageView.load(url: String) {
    Glide.with(this)
         .load(url)
         .into(this)
}

imageView.load("http://xxxxx.png")
/**
 * 根据手机的分辨率将dp转成为px。
 */
val Float.dp2px
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this,
        Resources.getSystem().displayMetrics
    )
//使用
20f.dp2px

3、网络请求简单封装

3.1、封装响应类
/**
 * 用密封类sealed封装响应数据类
 */
sealed class ApiResult<out T : Any> {
    //获取数据成功
    data class Success<out T : Any>(val data: T) : ApiResult<T>()
    //失败
    data class Error(val exception: Exception) : ApiResult<Nothing>()
}

//请求数据
viewModelScope.launch{
    //主线程启动一个协程
    try{
    	val data = repository.fetchData()  //网络请求
        //更新UI
    	textView.text = data.data.title
    } catch(e : Exception){
        //请求失败
        ...
    }
}
3.2、统一处理异常

对于每个网络请求,显然不能每个都加异常处理,可以通过高阶函数,统一处理异常。(高阶函数:一个函数可以将另一个函数当作参数)

/**
 * 统一处理网络请求异常
 * [call]:网络请求方法:suspend方法,返回数据类型为泛型T
 * [errorMsg]:错误信息
 * 返回类型:ApiResult<T> 可能为Success也可能为Error
 */
suspend fun <T : Any> request(call: suspend () -> T, errorMsg: String): ApiResult<T> =
    try {
        //请求成功
        ApiResult.Success(call())
    } catch (e: Exception) {
        //请求失败
        ApiResult.Error(Exception(errorMsg, e).also { e.printStackTrace() })
    }

//请求数据
/**
 * 三者关系:ViewModel持有Repository,Repository持有ApiService,ApiService执行网络请求
 */
//retrofit网络请求接口
interface Api {
    /**
     * 获取数据
     */
    @GET
    suspend fun fetchData(@Url url: String = "http://xxx"): Data
}

//repository类
class Repository() {
    /**
     * 获取数据
     */
    suspend fun fetchData() =
        request(call = {
            //网络请求
            apiService.fetchData()
        }, errorMsg = "请求失败")
}

//viewModel类
viewModelScope.launch{
    //主线程启动一个协程
    val data = repository.fetchData()  //网络请求
    when(data){
        is ApiResult.Success ->{ //更新Ui ,或者封装成LiveData发送数据
        	textView.text= data.data.title
        }
        is ApiResult.Error ->{ //错误提示
            data.exception.print()
        }
    }	
}

4、网络请求简单封装(flow篇)

如果用flow,可以以更简单的方式封装

// 添加个顶级函数
fun <T : Any> requestFlow(
    call: suspend () -> T,
    onSuccess: (T) -> Unit,
    onStart: () -> Unit = {},
    onComplete: () -> Unit = {},
    onError: (Throwable) -> Unit = {}
) = flow {
    emit(call())
}.flowOn(Dispatchers.IO)   // 在子线程执行
    .onStart {
        onStart()
    }.catch { ex ->
        onError(ex)
    }.onCompletion {
        onComplete()
    }.onEach {             // 分离消费
        onSuccess(it)
    }

// 使用
override fun fetchData() {
    requestFlow({
    	// 网络请求
        repository.fetchData()
    }, {                // 请求成功
        _discoveryData.value = it
    }, onStart = {      // 请求开始
       _onRefreshing.value = true
       _onError.value = false
    }, onComplete = {   // 请求完毕
       _onRefreshing.value = false
    }, onError = {      // 请求失败
       _onError.value = true
    }).launchIn(viewModelScope)
}