一高诺、開篇
全篇內(nèi)容將簡(jiǎn)單介紹AAC的在我個(gè)人開發(fā)上的應(yīng)用, AAC即Android Architecture Components,一個(gè)處理UI的生命周期與數(shù)據(jù)的持久化的架構(gòu)摘悴。
核心使用:LiveData峭梳、ViewModel、Databinding 蹂喻、Lifecycles
輔助:Room葱椭、WorkManager 、Glide等等常用框架
全篇大部分內(nèi)容都圍繞Kotlin口四,這門Google強(qiáng)推的語(yǔ)言上孵运,加上協(xié)程這個(gè)工具的出現(xiàn),使得Kotlin對(duì)于網(wǎng)絡(luò)請(qǐng)求蔓彩,異步操作變得更加方便治笨。所以網(wǎng)絡(luò)部分我已經(jīng)不再使用Retrofit2 + Rxjava2了。
首先赤嚼、本客戶端借助玩安卓的API實(shí)現(xiàn)網(wǎng)絡(luò)功能旷赖,非常感謝鴻洋大神玩安卓,給我們提供了分享知識(shí)和技術(shù)的地方
玩安卓:https://www.wanandroid.com/
接著本客戶端使用了來自github的第三方庫(kù)為:
1探膊、live-event-bus github地址:https://github.com/JeremyLiao/LiveEventBus 一款很優(yōu)秀的消息總線杠愧。
2、Activity返回側(cè)滑動(dòng)畫SlideBack github地址:https://github.com/ParfoisMeng/SlideBack 項(xiàng)目中下載了源碼并進(jìn)行了部分修改逞壁。
非常感謝所有作者
二流济、玩安卓部分預(yù)覽圖
三锐锣、圖片墻部分預(yù)覽圖:
本部分采用Room實(shí)現(xiàn)基本登錄注冊(cè),點(diǎn)贊绳瘟,下單(假功能雕憔,僅效果顯示)的操作
四、從網(wǎng)絡(luò)開始講起
在使用的API方法上加上suspend
@FormUrlEncoded
@POST("user/login")
suspend fun login(@Field("username") username:String,@Field("password") password:String) : Response<JsonResult<Auth>>
處理請(qǐng)求的邏輯封裝了另一個(gè)類CallResult糖声,只顯示關(guān)鍵部分
fun hold(result: suspend () -> Response<JsonResult<T>>): CallResult<T> {
var response: Response<JsonResult<T>>?
var netJob: Job? = null
owner?.apply {
netJob = lifecycleScope.launchWhenStarted {
__________ 處理loading狀態(tài) ————————————————
response = withContext(Dispatchers.IO) {
withTimeoutOrNull(10000){//超時(shí)處理
result.invoke() //網(wǎng)絡(luò)請(qǐng)求
}
}
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
withContext(Dispatchers.Main) {
__________ 處理超時(shí)和返回結(jié)果的邏輯————————————————
}
} else {
netJob?.cancel()
}
}
}
return this
}
使用時(shí)候如下:
fun login(username:String,password:String,call:MutableLiveData<Result<Auth>>){
CallResult<Auth>(owner)
.loading {
__________ 處理讀取————————————————
}.success { result, message ->
__________ 處理成功————————————————
call.value = result
}.error { result, code, message ->
__________ 處理錯(cuò)誤————————————————
call.value = result
}.outTime {
__________ 處理超時(shí)————————————————
call.value = it
}.hold {
api.login(username, password)//登錄
}
}
五斤彼、架構(gòu)細(xì)講
由于著重在AAC上,所以采用了MVVM的設(shè)計(jì)模式蘸泻,利用DataBinding的優(yōu)勢(shì)琉苇,把數(shù)據(jù)和交互的簡(jiǎn)單操作,都能在xml上完成
以下拉加載和讀取更多為例子
xml如下
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
onRefresh="@{Home::loadRefresh}"
refreshing="@{Home.refreshing}"
>
<showmethe.github.core.widget.common.AutoRecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="vertical"
android:fadingEdge="vertical"
android:fadingEdgeLength="@dimen/px20dp"
loadMore="@{Home::loadMore}"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
請(qǐng)勿直接復(fù)制悦施,省略databinding的set操作和初始化
class HomeFragment : LazyFragment<FragmentHomeBinding, MainViewModel>() {
val refreshing = MutableLiveData<Boolean>() //不能private
private val pagerNumber = MutableLiveData<Int>()
override fun observerUI() {
pagerNumber.observe(this, Observer {
it?.apply {
router.toTarget("getHomeArticle",this) //這個(gè)待會(huì)講并扇,利用注解調(diào)用viewModel中的方法
}
})
}
//___________省略無關(guān)代碼_________
fun loadMore(){//不能private
pagerNumber.value = pagerNumber.value!! + 1
}
fun loadRefresh(){
pagerNumber.value = 0
}
}
}
對(duì)應(yīng)的BindingAdapter拓展方法,利用了Kotlint的特性
@BindingAdapter("loadMore")
fun AutoRecyclerView.loadMore(loadingMore: (()->Unit)?){
setOnLoadMoreListener {
loadingMore?.invoke()
}
}
@BindingAdapter("onRefresh")
fun SwipeRefreshLayout.onRefresh(onRefreshListener: SwipeRefreshLayout.OnRefreshListener?){
setOnRefreshListener(onRefreshListener)
}
@BindingAdapter("refreshing")
fun SwipeRefreshLayout.refreshing(newValue: Boolean) {
if(isRefreshing != newValue)
isRefreshing = newValue
}
5.1 VMRouter的使用
在上面代碼中有一段router.toTarget("getHomeArticle",this)的代碼,是用來調(diào)用ViewModel中的方法,ViewModel中使用注解VMPath 和一個(gè)在該ViewModel中唯一的路徑抡诞,如下:
/**
* 登錄
*/
@VMPath("login")
fun login(username:String,password:String){
repository.login(username, password, auth)
}
通過反射拿到對(duì)應(yīng)方法穷蛹,并緩存起來。其實(shí)這里是因?yàn)榇嬖诜瓷渲绾梗鋵?shí)是會(huì)有性能的問題肴熏,但是這個(gè)消耗很低。
5.2 RecyclerViewAdapter的封裝
既然使用了Databinding顷窒,那不可不提ObservableArrayList,這個(gè)類可以讓你對(duì)數(shù)組的變化進(jìn)行刷新處理蛙吏,不在需要每次新數(shù)據(jù)都自己調(diào)用Adapter的刷新,減少漏調(diào)時(shí)候出現(xiàn)的奇怪問題蹋肮。實(shí)現(xiàn)OnListChangedCallback即可出刷,
/**
* The callback that is called by ObservableList when the list has changed.
*/
abstract class OnListChangedCallback<T extends ObservableList> {
/**
* Called whenever a change of unknown type has occurred, such as the entire list being
* set to new values.
*
* @param sender The changing list.
*/
public abstract void onChanged(T sender);
/**
* Called whenever one or more items in the list have changed.
* @param sender The changing list.
* @param positionStart The starting index that has changed.
* @param itemCount The number of items that have changed.
*/
public abstract void onItemRangeChanged(T sender, int positionStart, int itemCount);
/**
* Called whenever items have been inserted into the list.
* @param sender The changing list.
* @param positionStart The insertion index
* @param itemCount The number of items that have been inserted
*/
public abstract void onItemRangeInserted(T sender, int positionStart, int itemCount);
/**
* Called whenever items in the list have been moved.
* @param sender The changing list.
* @param fromPosition The position from which the items were moved
* @param toPosition The destination position of the items
* @param itemCount The number of items moved
*/
public abstract void onItemRangeMoved(T sender, int fromPosition, int toPosition,
int itemCount);
/**
* Called whenever items in the list have been deleted.
* @param sender The changing list.
* @param positionStart The starting index of the deleted items.
* @param itemCount The number of items removed.
*/
public abstract void onItemRangeRemoved(T sender, int positionStart, int itemCount);
}
再結(jié)合Databinding的特性,不再需要ButterKnife和findviewbyId找到view中對(duì)應(yīng)的id,而且利用數(shù)據(jù)綁定坯辩,而且數(shù)據(jù)綁定使得有時(shí)候連Id也不再需要書寫了馁龟,當(dāng)然有些場(chǎng)景還是需要的,代碼更加的簡(jiǎn)潔和方便了漆魔,但是也有人覺得這樣不好維護(hù)坷檩,但我覺得數(shù)據(jù)綁定減少了不少代碼的書寫,再加上kotlin更加簡(jiǎn)潔了改抡。
項(xiàng)目github地址:https://github.com/ShowMeThe/WanAndroid