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

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

功能需求

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

大致流程

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

api 申请及实体分析

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

  1. 进入天气预报入口。

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

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

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

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

  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()
    }

}

项目效果

项目 github 地址

github.com/ElaineTaylo…

具体内容在 little_project 项目中

作者:木子闲集
链接:https://juejin.cn/post/7142775573385838606

最后

附赠一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值