Android应用程序的框架结构演变
(1)MVC应用程序框架结构分为视图层(V)控制层(C)模型层(M)
控制层负责操作数据M,视图层则可以通过调用C层来设置以及获取数据,各司其职,看似没有问题,但由于业务逻辑变得复杂,V层的逻辑越来越多,维护起来成本比较高
(2)MVP应用程序框架结构类似MVC,只不过C—》P,这种变化是为了让P减轻V的责任,与MVC对比,P负责了C+部分V 中的工作,但是依然摆脱不了数据变化产生的层层调用,由于很多逻辑都是set及get类型的操作,因此出现了MVVM
(3)MVVM应用程序框架结构分为视图层(V)数据层(M)viewModel层(VM),使用这种结构,关键点是VM层,在Android应用程序中JetPack组件库中的LiveData,它是一种响应式数据对象,在它当中包装了T类型的数据以及支持对T类型数据的观察者,当T的值发生变化的时候观察者中的代码就可以被执行,这个关键的功能应用到ViewModel中,就能很好的服务于我们的应用程序开发中
MVVM框架结构实践—来自第一行代码中的demo天气查询
我们本着从下到上的实现逻辑来实现
(1)M层,实体对象抽象
class PlaceResponse(val status: String, val places: List<Place>)
class Place(val name: String, val location: Location, @SerializedName("formatted_address") val address: String)
class Location(val lng: String, val lat: String)
(2)逻辑实现–网络请求
interface PlaceService {
@GET("v2/place?token=${SunnyWeatherApplication.TOKEN}&lang=zh_CN")
fun searchPlaces(@Query("query") query: String): Call<PlaceResponse>
}
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)
}
(3)将一系列有关联的业务逻辑对应的网络请求集合到一个单例类当中
object Repository {
fun searchPlaces(query: String) = fire(Dispatchers.IO) {
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}"))
}
}
fun refreshWeather(lng: String, lat: String, placeName: String) = fire(Dispatchers.IO) {
coroutineScope {
val deferredRealtime = async {
SunnyWeatherNetwork.getRealtimeWeather(lng, lat)
}
val deferredDaily = async {
SunnyWeatherNetwork.getDailyWeather(lng, lat)
}
val realtimeResponse = deferredRealtime.await()
val dailyResponse = deferredDaily.await()
if (realtimeResponse.status == "ok" && dailyResponse.status == "ok") {
val weather = Weather(realtimeResponse.result.realtime, dailyResponse.result.daily)
Result.success(weather)
} else {
Result.failure(
RuntimeException(
"realtime response status is ${realtimeResponse.status}" +
"daily response status is ${dailyResponse.status}"
)
)
}
}
}
fun savePlace(place: Place) = PlaceDao.savePlace(place)
fun getSavedPlace() = PlaceDao.getSavedPlace()
fun isPlaceSaved() = PlaceDao.isPlaceSaved()
private fun <T> fire(context: CoroutineContext, block: suspend () -> Result<T>) =
liveData<Result<T>>(context) {
val result = try {
block()
} catch (e: Exception) {
Result.failure<T>(e)
}
emit(result)
}
}
(4)ViewModel层一般一个View对应一个View层,所以放到UI层对应的包中
class PlaceViewModel : ViewModel() {
private val searchLiveData = MutableLiveData<String>()
val placeList = ArrayList<Place>()
val placeLiveData = Transformations.switchMap(searchLiveData) { query ->
Repository.searchPlaces(query)
}
fun searchPlaces(query: String) {
searchLiveData.value = query
}
fun savePlace(place: Place) = Repository.savePlace(place)
fun getSavedPlace() = Repository.getSavedPlace()
fun isPlaceSaved() = Repository.isPlaceSaved()
}
(5)View层
class PlaceFragment : Fragment() {
val viewModel by lazy { ViewModelProviders.of(this).get(PlaceViewModel::class.java) }
private lateinit var adapter: PlaceAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_place, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (activity is MainActivity && viewModel.isPlaceSaved()) {
val place = viewModel.getSavedPlace()
val intent = Intent(context, WeatherActivity::class.java).apply {
putExtra("location_lng", place.location.lng)
putExtra("location_lat", place.location.lat)
putExtra("place_name", place.name)
}
startActivity(intent)
activity?.finish()
return
}
val layoutManager = LinearLayoutManager(activity)
recyclerView.layoutManager = layoutManager
adapter = PlaceAdapter(this, viewModel.placeList)
recyclerView.adapter = adapter
searchPlaceEdit.addTextChangedListener { editable ->
val content = editable.toString()
if (content.isNotEmpty()) {
viewModel.searchPlaces(content)
} else {
recyclerView.visibility = View.GONE
bgImageView.visibility = View.VISIBLE
viewModel.placeList.clear()
adapter.notifyDataSetChanged()
}
}
viewModel.placeLiveData.observe(this, Observer{ result ->
val places = result.getOrNull()
if (places != null) {
recyclerView.visibility = View.VISIBLE
bgImageView.visibility = View.GONE
viewModel.placeList.clear()
viewModel.placeList.addAll(places)
adapter.notifyDataSetChanged()
} else {
Toast.makeText(activity, "未能查询到任何地点", Toast.LENGTH_SHORT).show()
result.exceptionOrNull()?.printStackTrace()
}
})
}
}
最后再说明一下Databinding
DataBinding简单理解是一种实现双向绑定的工具,ViewModel实现了单向绑定,当ViewModel中的数据发生变化,则可以将变化传给View层,但是View层的数据产生变化想要传给ViewModel只能调用ViewModel中的方法,有没有什么办法更简单呢,答案是DataBinding,它提供了一种双向绑定的功能,简单理解可以让我们直接在XML文件中写代码,达到View与逻辑层的双向绑定。