0
点赞
收藏
分享

微信扫一扫

安卓系列之 kotlin 项目实战--基础 demo


本章记录一个基础的 demo 项目,使用 kotlin+协程+retrofit+okhttp3+MVVM 实现。

功能需求

调用天气 api,在主页显示天气情况。

大致流程

  1. api 申请及实体分析
  2. 网络请求权限
  3. 添加 kotlin,协程,网络框架等依赖
  4. 网络框架 Retrofit+okhttp3
  5. 主页页面绘制
  6. 基础类构建
  7. 调用接口并显示在当前页面

api 申请及实体分析

这里使用万维易源的数据源,首先注册并登录账号。

  1. 进入天气预报入口。

安卓系列之 kotlin 项目实战--基础 demo_Kotlin

  1. 购买一个月的天气预报 api ,这里使用地址查询当前天气作为例子。

安卓系列之 kotlin 项目实战--基础 demo_android_02

  1. api 请求示例。
    http[s]😕/route.showapi.com/9-2?showapi_appid=替换自己的值&showapi_sign=替换自己的值&area=“杭州”
  2. 进入控制台,可以查看自己的 appId 和 sign。

安卓系列之 kotlin 项目实战--基础 demo_android_03

  1. 若是没有,则添加,并创建 app,可调用接口选择全部。

安卓系列之 kotlin 项目实战--基础 demo_Kotlin_04

  1. 构建 api 接口,访问得到返回数据如下所示。

安卓系列之 kotlin 项目实战--基础 demo_Android_05

  1. 分析返回数据。
    外层是固定格式,可以统一封装,需要获取当前的天气情况,取关键字 now 的内容即可。归纳总结为三个实体,具体内容如下:

通用网络请求实体 CResponse

/**
 * 通用网络请求
 */
data class CResponse<T>(
    @SerializedName("showapi_res_error")
    val msg: String,//错误提示
    @SerializedName("showapi_res_code")
    val code: Int,//错误码
    @SerializedName("showapi_res_body")
    val data: T//数据
)

天气实体

/**
 *  天气
 */
data class Weather(
    val time: Long,//预报发布时间
    val now: WeatherDetail//天气详情
)

天气详情实体

/**
 * 天气详情
 */
data class WeatherDetail(
    val aqi: String,//空气指数
    val rain: String,//下雨时间点
    val sd: String,//空气湿度
    val temperature: String,//气温
    @SerializedName("temperature_time")
    val temperatureTime: String,//获得气温的时间
    val weather: String,//天气
    @SerializedName("weather_pic")
    val weatherPic: String,//天气小图标
    @SerializedName("wind_direction")
    val windDirection: String,//风向
    @SerializedName("windPower")
    val windPower: String//风力
)

网络请求权限

在 AndroidManifest.xml 中添加网络请求权限,代码如下:

<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET" />

添加 kotlin,协程,网络框架等依赖

在 build.gradle 文件中添加依赖,代码如下:

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.10"
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0"

implementation "androidx.activity:activity-ktx:1.2.3"
implementation "androidx.fragment:fragment-ktx:1.3.5"

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"

// okhttp
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'//日志拦截器

// retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"//支持返回结果是string
implementation "com.squareup.retrofit2:converter-gson:2.9.0"//支持返回结果是实体

网络框架 Retrofit+okhttp3

网络请求封装,添加基础访问地址,拦截器等,最后目的是能有个可以得到 service 的方法。

  1. 首先需要一个类,存放静态常量,例如基础访问地址,appId 以及 sign。

/**
 * 通用数据
 */
object HttpConstant {
    /**
     * 访问地址
     */
    const val BASE_HTTP = "https://route.showapi.com"

    /**
     * appID
     */
    const val APP_ID = "替换为万维易源的appId"

    /**
     * app_sign
     */
    const val APP_SIGN = "替换为万维易源的sign"
}

  1. 从请求地址分析,有两个公共参数(showapi_appid 和 showapi_sign)可以封装,构建通用参数拦截器。
    请求地址
    http[s]😕/route.showapi.com/9-2?showapi_appid=替换自己的值&showapi_sign=替换自己的值&area=“杭州”

/**
 * 通用参数拦截器
 */
class CommonInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val oldRequest = chain.request()
        val httUrl = oldRequest.url
        val urlBuilder = httUrl.newBuilder()

        /** 添加公共参数 */
        urlBuilder.addQueryParameter("showapi_appid", HttpConstant.APP_ID)
        urlBuilder.addQueryParameter("showapi_sign", HttpConstant.APP_SIGN)

        val request = oldRequest
            .newBuilder()
            .url(urlBuilder.build())
            .build()
        return chain.proceed(request)
    }
}

  1. 构建 Retrofit 管理类,得到 service 方法。

/**
 * Retrofit管理类
 */
object RetrofitManager {

    /**
     * okhttpClient
     */
    private val okhttpClient: OkHttpClient
        get() = OkHttpClient.Builder()
            .addInterceptor(CommonInterceptor())//通用参数拦截器
            .addInterceptor(HttpLoggingInterceptor())//日志拦截器
            .followRedirects(true)
            .build()

    /**
     * 构建service
     */
    fun <T> getService(serviceClass: Class<T>): T {
        val retrofit = Retrofit.Builder().apply {
            baseUrl(HttpConstant.BASE_HTTP)//基础访问地址
            client(okhttpClient)
            addConverterFactory(GsonConverterFactory.create())//Gson转换工厂
            addConverterFactory(ScalarsConverterFactory.create())//String转换工厂
        }.build()
        return retrofit.create(serviceClass)
    }
}

主页页面绘制

主界面显示当前的天气情况即可,使用 databinding 方式,代码如下。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.elaine.little_project.MainViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.weatherData.weather}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

基础类构建

在项目构架过程中,都会习惯性地将一些公共方法或者属性进行封装操作,方便后续使用。

  1. 抽象类 BaseViewModel,继承 ViewModel(),定义一个初始化数据的方法。

/**
 * 基础ViewModel
 */
abstract class BaseViewModel : ViewModel() {
    /** 初始化数据 */
    abstract fun start()
}

  1. 抽象类 BaseActivity,自定绑定 ViewModel 和 databinding,避免过多重复代码,其他的 Activity 继承 BaseActivity 即可。

/**
 * 基础类Activity
 */
abstract class BaseActivity<VM : BaseViewModel, VB : ViewDataBinding>(private val contentViewResId: Int) :
    AppCompatActivity() {
    lateinit var mViewModel: VM
    lateinit var mBinding: VB

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initViewModel()
        initDataBinding()
        initView()
        initData()
    }

    /**
     * 初始化ViewModel
     */
    private fun initViewModel() {
        //注意:actualTypeArguments[0] 0-->指上面BaseActivity<VM : BaseViewModel, VB : ViewDataBinding>的VM放在第一个
        val type: Class<VM> =
            (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<VM>
        mViewModel = ViewModelProvider(this).get(type)
        mViewModel.start()
    }

    /**
     * 初始化dataBinding
     */
    private fun initDataBinding() {
        mBinding = DataBindingUtil.setContentView(this, contentViewResId)
        mBinding.apply {
            //绑定生命周期
            lifecycleOwner = this@BaseActivity
            //mBinding绑定viewModel
            setVariable(BR.viewModel, mViewModel)
        }
    }

    /**
     * 初始化UI
     */
    abstract fun initView()

    /**
     * 初始化数据
     */
    abstract fun initData()
}

调用接口并显示在当前页面

  1. 接口文件 Api,这里是挂起函数。

/**
 * api接口
 */
interface Api {

    /**
     * 请求天气
     * @param area 地址 eg:杭州
     */
    @FormUrlEncoded
    @POST("/9-2")
    suspend fun getWeather(
        @Field("area") area: String,
    ): CResponse<Weather>
}

  1. api 接口实现类 WeatherRepository,通过 Retrofit 管理器获取 service,然后调用天气接口。

/**
 * api接口实现类
 */
object WeatherRepository : Api {

    /** 获取service */
    private val service by lazy { RetrofitManager.getService(Api::class.java) }

    /**
     * 请求天气
     * @param area 地址 eg:杭州
     */
    override suspend fun getWeather(area: String): CResponse<Weather> {
        return service.getWeather(area)
    }
}

  1. MainActivity 对应的 MainViewModel,利用 viewModelScope 进行网络请求,其是一个协程。

/**
 * viewModel
 */
class MainViewModel : BaseViewModel() {
    /** 天气数据 */
    var weatherData: MutableLiveData<WeatherDetail> = MutableLiveData()

    override fun start() {

    }

    /**
     * 获取天气数据
     */
    fun getWeather() {
        viewModelScope.launch {
            val result = WeatherRepository.getWeather("杭州")
            weatherData.value = result.data.now
        }
    }

}

  1. 主页 MainActivity,主页就是请求接口即可。

/**
 * 主页
 */
class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(R.layout.activity_main) {

    override fun initView() {

    }

    override fun initData() {
        getData()
    }

    /**
     * 获取数据
     */
    private fun getData() {
        mViewModel.getWeather()
    }

}

项目效果

安卓系列之 kotlin 项目实战--基础 demo_kotlin_06

项目 github 地址

github.com/ElaineTaylo…

具体内容在 little_project 项目中

举报

相关推荐

0 条评论