本章记录一个基础的 demo 项目,使用 kotlin+协程+retrofit+okhttp3+MVVM 实现。
功能需求
调用天气 api,在主页显示天气情况。
大致流程
- api 申请及实体分析
- 网络请求权限
- 添加 kotlin,协程,网络框架等依赖
- 网络框架 Retrofit+okhttp3
- 主页页面绘制
- 基础类构建
- 调用接口并显示在当前页面
api 申请及实体分析
这里使用万维易源的数据源,首先注册并登录账号。
- 进入天气预报入口。
- 购买一个月的天气预报 api ,这里使用地址查询当前天气作为例子。
- api 请求示例。
http[s]😕/route.showapi.com/9-2?showapi_appid=替换自己的值&showapi_sign=替换自己的值&area=“杭州” - 进入控制台,可以查看自己的 appId 和 sign。
- 若是没有,则添加,并创建 app,可调用接口选择全部。
- 构建 api 接口,访问得到返回数据如下所示。
- 分析返回数据。
外层是固定格式,可以统一封装,需要获取当前的天气情况,取关键字 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 的方法。
- 首先需要一个类,存放静态常量,例如基础访问地址,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"
}
- 从请求地址分析,有两个公共参数(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)
}
}
- 构建 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>
基础类构建
在项目构架过程中,都会习惯性地将一些公共方法或者属性进行封装操作,方便后续使用。
- 抽象类 BaseViewModel,继承 ViewModel(),定义一个初始化数据的方法。
/**
* 基础ViewModel
*/
abstract class BaseViewModel : ViewModel() {
/** 初始化数据 */
abstract fun start()
}
- 抽象类 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()
}
调用接口并显示在当前页面
- 接口文件 Api,这里是挂起函数。
/**
* api接口
*/
interface Api {
/**
* 请求天气
* @param area 地址 eg:杭州
*/
@FormUrlEncoded
@POST("/9-2")
suspend fun getWeather(
@Field("area") area: String,
): CResponse<Weather>
}
- 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)
}
}
- 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
}
}
}
- 主页 MainActivity,主页就是请求接口即可。
/**
* 主页
*/
class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(R.layout.activity_main) {
override fun initView() {
}
override fun initData() {
getData()
}
/**
* 获取数据
*/
private fun getData() {
mViewModel.getWeather()
}
}
项目效果
项目 github 地址
具体内容在 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官方认证微信卡片免费领取↓↓↓