在此记录项目开发过程种学到的。
MVVM是一种高级的项目架构模式。分别为 Model-View-ViewModel ,类似的架构有MVP、MVC。
Model是数据模型部分、View是界面展示部分、View Model是实现业务逻辑和界面展示分离的程序结构。
一个优秀的项目架构处理包含以上三种,还应该包含仓库、数据源等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1CwSnjuF-1689129418641)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230712093801391.png)]
一般情况,会按照以上结构实现以下项目结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJzJlYZZ-1689129418643)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230712093942545.png)]
logic用于存放业务逻辑相关的代码,ui用于存放界面展示的代码。
logic又存放了dao、model、network,分别用于存放数据访问的对象、对象模型、以及网络相关的代码。
ui包含place和weather,分别对应两个主要界面。
在com.sunnyweather.android包下简历一个SunnyWeatherApplication类,代码如下:
class SunnyWeatherApplication:Application() {
companion object{
const val TOKEN = "uYjcEJAykI0e"
@SuppressLint("StaticFieldLeak")
lateinit var context: Context
}
override fun onCreate() {
super.onCreate()
context = applicationContext
}
}
TOKEN是申请的令牌值,也顺便放在这里。
在logic/model下创建数据类,data class即数据类。
代码如下:
data class PlaceResponse(val status:String, val places:List<Place>)
data class Place(val name:String , val location:Location, @SerializedName("formatted_address") val address:String)
data class Location(val lng:String, val lat:String)
由于JSON种的一些字段可能与Kotlin命名的规范不一样,因此这里使用了@SerializedName注解,建立映射关系。
新建PlaceService接口,代码如下:
interface PlaceService {
@GET("v2/place?token=${SunnyWeatherApplication.TOKEN}&lang=zh_CN")
fun searchPlaces(@Query("query")query: String):Call<PlaceResponse>
}
@GET是请求范围注解内的地址,@Query是实现动态传参。
建立ServiceCreator,代码如下:
object ServiceCreator {
private const val BASE_URL = "https://api.caiyunapp.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun <T> create(serviceClass: Class<T>):T = retrofit.create(serviceClass)
inline fun <reified T> create():T = create(T::class.java)
}
对所有网络请求的API进行封装。
新建SunnyWeatherNetwork
object SunnyWeatherNetWork {
private val placeService = ServiceCreator.create<PlaceService>()
suspend fun searchPlaces(query:String) = placeService.searchPlaces(query).await()
private suspend fun <T> Call<T>.await():T{
return suspendCoroutine { continuation ->
enqueue(object :Callback<T>{
override fun onResponse(call: Call<T>, response: Response<T>) {
val body = response.body()
if (body!=null)continuation.resume(body)
else continuation.resumeWithException(
RuntimeException("response body is null")
)
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
}
在logic下新建单例类Respository,代码实例如下:
object Repository {
fun searchPlaces(query:String) = liveData(Dispatchers.IO){
val result = try {
val placeResponse = SunnyWeatherNetWork.searchPlaces(query)
if (placeResponse.status == "OK"){
val places = placeResponse.places
Result.success(places)
}else {
Result.failure(RuntimeException("response status is ${placeResponse.status}"))
}
}catch (e:Exception){
Result.failure<List<Place>>(e)
}
emit(result)
}
}
class PlaceViewModel:ViewModel() {
private val searchLiveData = MutableLiveData<String>()
val placeList = ArrayList<Place>()
val placeLiveData = Transformations.switchMap(searchLiveData){
query->Repository.searchPlaces(query)
}
fun searPlaces(query:String){
searchLiveData.value = query
}
}
witchMap(searchLiveData){
query->Repository.searchPlaces(query)
}
fun searPlaces(query:String){
searchLiveData.value = query
}
}