安卓MVVM開發(fā):基于AAC架構(gòu)玩安卓客戶端(Databinding+LiveData+ViewModel+Coroutines+Repository)誉察,(上)

一高诺、開篇

全篇內(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ù)覽圖

image
image

三锐锣、圖片墻部分預(yù)覽圖:

本部分采用Room實(shí)現(xiàn)基本登錄注冊(cè),點(diǎn)贊绳瘟,下單(假功能雕憔,僅效果顯示)的操作

image

四、從網(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矢炼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子阿纤,更是在濱河造成了極大的恐慌句灌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異胰锌,居然都是意外死亡骗绕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門资昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酬土,“玉大人,你說我怎么就攤上這事格带〕方桑” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵叽唱,是天一觀的道長(zhǎng)屈呕。 經(jīng)常有香客問我,道長(zhǎng)尔觉,這世上最難降的妖魔是什么凉袱? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮侦铜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钟鸵。我一直安慰自己钉稍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布棺耍。 她就那樣靜靜地躺著贡未,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒙袍。 梳的紋絲不亂的頭發(fā)上俊卤,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音害幅,去河邊找鬼消恍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛以现,可吹牛的內(nèi)容都是我干的狠怨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼邑遏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼佣赖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起记盒,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤憎蛤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后纪吮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俩檬,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萎胰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豆胸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奥洼。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晚胡,靈堂內(nèi)的尸體忽然破棺而出灵奖,到底是詐尸還是另有隱情,我是刑警寧澤估盘,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布瓷患,位于F島的核電站,受9級(jí)特大地震影響遣妥,放射性物質(zhì)發(fā)生泄漏擅编。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一箫踩、第九天 我趴在偏房一處隱蔽的房頂上張望爱态。 院中可真熱鬧,春花似錦境钟、人聲如沸锦担。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)洞渔。三九已至,卻和暖如春缚态,著一層夾襖步出監(jiān)牢的瞬間磁椒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工玫芦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浆熔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓姨俩,卻偏偏與公主長(zhǎng)得像蘸拔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子环葵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348