Android UI:機智的遠程動態更新策略


騰訊Bugly特約做者: 王金波json

問題描述

作過Android開發的人都遇到過這樣的問題:隨着需求的變化,某些入口界面一般會出現 UI的增長、減小、內容變化、以及跳轉界面發生變化等問題。每次發生變化都要手動修改代碼,而入口界面一般具備未讀信息提醒這樣的「小紅點」邏輯;一旦UI變化,「小紅點」邏輯也要從新計算。若是不一樣的RD來維護這些代碼,耦合性很是高,出錯機率也很大。本文以自選股的我的頁卡爲例(界面以下圖所示),並給出了一套方案來解決動態更新UI的問題以及更好的解決未讀提醒的邏輯。markdown


舊的方案(Phase out)

(1)對於UI動態變化的問題,一般結合遠程控制來解決。以上圖的「資產管理」爲例,舊的解決方案會在XML寫死所有的item,如:「港股交易」、「基金交易」和「精品理財」這三個item。而後根據後臺傳遞過來的json解析出須要隱藏哪些item。點擊不一樣的item會跳轉到不一樣的activity(以下圖所示),這部分跳轉操做也是寫死在代碼中的。數據結構

這解決了一部分問題,可是若是需求新增了item,好比新增了「滬深交易」、「美股交易」,那就須要改動現有代碼了。app

(2)對於未讀指示(小紅點)功能,它的做用是,有未讀信息來了,須要在UI上面顯示一個小紅點提醒用戶。好比下圖的,股友動態的頭像提醒,資產管理的「NEW」提醒,系統設置的新版本提醒等。異步

舊的方案是定義了一個int型(32位),用它的每一位表明一個UI上的Item。好比好友動態是第1位,未讀提醒是第2位… 小紅點思想是哪一個item有未讀信息,則該int型對應的那一位就置1,不然爲0。一旦某個item有未讀提醒的改變,則將這個int型對應的位改變,異步寫入SharedPreference中,同時利用觀察者模式通知UI作更新,以下圖所示:函數

上述作法整體來講最大的缺陷就是沒有作到「開放-封閉」原則。面對擴展的時候,即添加一個item則不得不修改現有代碼,須要在該int型中添加一位標誌位,觀察者模式也要註冊新item。因此下面我會介紹另外一種方案能夠更好的解決該問題。ui


新的解決方法

(1) 數據抽象spa

首先進行數據的抽象,並將UI進行分組,以下圖所示:對象

按照組合模式,將數據以樹形結構組織起來,表現「總體/部分」層次結構,以下圖所示。這樣作的好處是,能夠以一致的方式來處理個別對象以及對象組合。藍色的表示節點,而綠色的表示葉節點。遞歸

組合模式的類圖,以下所示:

對UI進行的數據抽象。不管是ListItem列表項,仍是GridView Item的項,都採用了PersonalItem對象來表示,以下所示:

對於PersonalItem來講,有些沒有意義的方法(如add()、remove()、getChild())就不實現,調用它們會拋出UnsupportedOperationException()異常。

(2) 完美解決未讀提醒(小紅點)的問題

關於計算小紅點,PersonalGroup類利用組合+迭代器的模式,代碼以下:

這裏使用了迭代器,用它遍歷全部PersonalComponent組件。遍歷過程當中可能遇到PersonalItem也可能遇到PersonalGroup。因爲它們都是PersonalComponent,且實現了getUnreadIndicator()方法,那咱們只需調用getUnreadIndicator()便可。

如上圖所示,PersonalGroup中加載的是PersonalComponent,多是PersonalItem也多是PersonalGroup。組合模式的優勢就是無視具體類型 — 獲取出來的都是PersonalComponent,而後利用多態,調用具體類的getUnredIndicatorCount()方法。若是是PersonalGroup,則繼續調用它的這個方法(與此方法同樣,會開始另外一個遍歷);若是爲PersonalItem,則說明遍歷到了樹形結構的末端(即葉節點),則進行以下處理:

若是getUnreadIndicator爲true,則表示該PersonalComponent須要顯示小紅點。所以,利用上述組合+迭代方式,運用遞歸在根節點處進行一次調用便可。以下圖所示,當計算出葉節點「A股大賽」有未讀提醒,則它上級的groups也有未讀提醒,一直統計到根節點。

getUnredIndicatorCount()是每個item本身來決定本身是否須要展現小紅點的方法。這就是將局部與總體解耦了。總體上面,須要計算小紅點,至於如何計算則委託給具體類來實現。即面向對象中的將 「作什麼」 與 「怎麼作」分開。RD能夠從中解放出來,沒必要關注總體實現,只需關注本身的實現便可。好比,須要在「資產管理」中添加「美股交易」,RD只需添加「美股交易」的內容便可。下一節會說明,這部份內容也由遠程控制來代勞了,遠程控制傳遞過來的Date與本地存儲的Date比較,若是是新的Date值,則證實這個Item爲「NEW」,則對應的小紅點須要顯示。

(3)遠程控制動態更新UI

當遠程控制發生變化時(5分鐘主動發一次請求),經過解析遠程控制接口返回的json串,生成PersonalItem對象的列表。其中每一項對應UI上面的一個Item。須要注意的是,這裏還包含了一個URL,它是點擊UI控件跳轉的URL。以「資產管理」爲例,它包含「滬深交易」、「基金交易」等子項。當點擊任意一個子項的時候啓動的是同一個Activity - WebviewActivity,它包含一個WebView控件。由於每一個子項的跳轉URL不同,因此這個WebView load了不一樣的URL,即完成了跳轉不一樣界面的問題。 而後按照上面描述的樹形結構,把PersonalItem放到Groups中。以下圖所示:

Model存儲了待顯示的數據結構。這份數據經過Parser的解析生成UI的內容。過程以下圖所示:

Parser模塊是一個遞歸函數,遞歸的對Model進行解析。並將解析出來的List Item、Grid Group、GridView Item加載各自的XML文件,在程序中動態的添加UI組件。其中onClick事件是在定義PersonalItem的時候已經寫好了回調。例如,「資產管理」屬於Grid Group,其子項「滬深交易」、「基金交易」等屬於GridView Item。在上述「Build PersonalItem Objects」步驟中,已經定義了onClick方法,調用onClick方法跳轉至WebViewActivity,這個Activity會加載不一樣GridView Item的URL,從而實現點擊不一樣item跳轉不一樣頁面的目的。

Note:

對於ListItem元素,即上圖的列表項(不是GridView元素),並無實現遠程更新的策略。由於它們跳轉的邏輯是跳轉到各自的Activity,是固定不變的;而且它們的文字描述、圖標、是否隱藏均不須要後臺來控制更新。故實際項目中,只對GridView內容做了遠程控制動態更新UI機制的處理。

另外,在經過遠程控制動態更新UI的過程當中也遇到了一些坑,好比遠程控制更新的時刻,剛好用戶退出app,此時系統恰好銷燬activity。那麼在執行到上述Parser模塊的inflateUI的時候就須要判斷當前上下文是否爲空,若是爲空則直接退出。


結論與數據

本文經過將UI數據進行抽象,利用組合模式進行數據的構建。利用遞歸的方式將數據映射爲UI。同時處理了點擊事件。數據源則能夠經過遠程控制動態的更新,RD從中解放。另外,組合+迭代器的方式完美的解決了小紅點的問題,遵循了「開放-封閉」原則,將「作什麼」與「怎麼作」分開。下圖從數據的角度描述了改版先後 代碼量、Bug量 以及 RD工做量的差別。


騰訊Bugly簡介

Bugly是騰訊內部產品質量監控平臺的外發版本,其主要功能是App發佈之後,對用戶側發生的Crash以及卡頓現象進行監控並上報,讓開發同窗能夠第一時間瞭解到App的質量狀況,及時機型修改。目前騰訊內部全部的產品,均在使用其進行線上產品的崩潰監控。

相關文章
相關標籤/搜索