02.28 還在用 MVP?快來試試 MVVM框架吧


還在用 MVP?快來試試 MVVM框架吧


優勢

穩定

  • 減少內存洩漏:新手很容易在線程切換的地方寫出導致內存洩漏的代碼,但如果把線程切換交給框架來做,出錯的概率就大大降低。
  • 減少 crash:根據我的開發經歷,大部分 crash 都是空指針導致的。一般線程回調裡最容易出現問題,當UI銷燬後,子線程依舊去操作UI,容易導致 crash。 本框架有完善的生命週期,UI銷燬後,框架對子線程做了強制的停止操作,大大減少 crash 的概率。


還在用 MVP?快來試試 MVVM框架吧


輕量

  • 最少依賴:僅依賴 lifecycle 和 support lib.
  • 實現精簡:只有幾十個類

提示:這兩個依賴庫在 Android Studio 新建的項目裡幾乎都包含,也就是幾乎 0 依賴。

接入成本低

  • 侵入性低:不需要修改任何現有代碼
  • 無縫嵌入:可間接當做 View 使用,無論之前使用 MVP 還是 MVC,往裡面加一個 View 根本不影響你的結構。

簡單

  • 對原生開發友好:你幾乎不需要學習框架 api 就可以開始使用。
  • 熟悉 react 和 flutter 的非常容易上手

解耦

MVVM 的強大之處在於 UI 和 邏輯 分離,處理邏輯時不需要關心 UI,寫 UI 時不需要管數據從哪獲取。

要更新時,你直接對數據進行修改,就會自動觸發重新渲染。 並不需要擔心性能問題,因為默認情況下,原來的 View 並不會被拋棄掉,僅僅會觸發一次 update 操作。

<code>public class StatefulUserWidget extends StatefulWidget<view> {    private UserBean user = UserDataSource.getInstance().getUser();    public StatefulUserWidget(Context context, Lifecycle lifecycle) {        super(context, lifecycle);    }    @Override    protected State<userwidget> createState(Context context) {        return StateUtils.create(new UserWidget(context, lifecycle, user));    }    @Override    public void initWidget(UserWidget widget) {        widget.setOnClickListener(v -> setState(() -> {            user = UserDataSource.getInstance().getUser();        }));        update();    }    @Override    public void update() {        super.update();        widget.setUser(user);    }}/<userwidget>/<view>/<code>

在 initWidget 方法中對 widget 設置了一個點擊事件,點擊後重新獲取數據,自動觸發 UI 的更新。 其實就是調用了 setState 方法來觸發更新,類似於 react 和 flutter,更新數據的操作需要放到該方法中,否則不會觸發更新。

高複用

本框架的設計思想類似於 flutter 的 "Everything's a Widget",即把所有的東西都視為控件。 各個控件之間保持獨立,容器控件可以組合一個或多個控件,每個控件都有獨立的生命週期。 因此,控件的複用性大大提高。

便捷的生命週期

得益於谷歌新引進的 lifecycle,讓每個 widget 都可以擁有完整的生命週期,甚至數據也可以擁有生命週期。

異步支持 (同步發請求)

對於客戶端編程來說,最麻煩的是各種異步調用和狀態同步。 多線程編程很難,稍有不慎,輕則內存洩漏,重則直接蹦潰。 本框架內部做了處理異步請求,並在 onDestroy 時,自動取消子線程的操作,防止內存洩漏 或者 異步導致的空指針問題。

本庫提供瞭如下方法支持數據修改,各位開發者可自行選擇合適的方法。

  • setState:同步執行數據修改操作(適用於非耗時的數據修改操作,無線程切換性能消耗)
  • setStateAsync:異步執行的數據修改操作,並在UI銷燬時自動停止異步線程
  • setStateAsyncWithCache:類似於 setStateAsync ,對緩存提供支持。

有了它,你可以同步的方式去髮網絡請求。 合併多個請求的數據變得異常輕鬆(比如 先請求a,在請求b,合併結果變成c)。

緩存支持

生活 緩存很難。 一千個應用有一千種緩存。 我見過網上很多緩存方案非常粗糙,大部分是直接在網絡層通過攔截器來做。 因為這樣不用侵入到業務代碼。 但是,這樣做的弊端也很大,不夠靈活。 雖然像 okhttp 這樣的網絡庫提供了對緩存的支持,比如可以設置只使用緩存,或者只使用網絡,但這依然不夠靈活。

如果想精準控制緩存,那就不得不自己在代碼裡為每一個請求都加上緩存的邏輯。 你會發現這就導致相同的緩存邏輯寫了無數遍,這簡直是噩夢。

不過因為本庫有異步支持,所以處理緩存也變得簡單多了。 至於你想怎麼使用緩存,交給你自己判斷吧,我們提供了一個策略接口,你只需要實現它即可。

頁面狀態管理

無數據頁面、 錯誤頁面、 加載中頁面、 下拉刷新、 加載更多 在應用中很常見。

實現起來卻不方便了,常見的做法是 BaseActivity BaseFragment,但我表示不希望看見它們,曾今我覺得 base 是很好的邏輯抽象和封裝,後來發現自從有了 base,遷移和複用幾乎變成了 0。 base 使得它們緊緊的耦合在一起。 如果你不明白我在說什麼,我給你舉個例子:

我想從項目 A 中抽出一個頁面和邏輯差不多的 Activity,以便於在項目 B 中使用,這個時候最常見的就是 複製 XxxActivity.java 到 B 項目,然後後面你懂的。

但本庫對這幾種頁面狀態提供了高度的封裝,你不必再依賴於 Base。 不僅僅是 activity,甚至一個 button,你都可以讓他擁有如上的這幾種狀態。

請求過濾

不知道你是否煩惱過,產品跟你說,用戶可能狂按按鈕,讓你加個判斷,減少不必要的請求。 聽起來需求很簡單,防止重複點擊就行,但可達鴨眉頭一皺,發現事情並不簡單。 一個按鈕防重複點擊也就幾行代碼,但幾十個幾百個按鈕呢? 你說可以抽出一個 BaseButton? 那點擊的如果是個 text 或 fab 這樣的控件呢? 確實 base 可以解決很多重複代碼,但相應的你要把對應的控件全部換成 base,工作量也很大。

本庫貼心的為大家提供了請求過濾器,默認就過濾重複的請求,雖然不是在 UI 上過濾,但同一個 task 的請求是不會重複執行的,這點可以放心。 如果你有其他過濾需求,還可以自定義實現一個過濾器。

重試支持

請求失敗重試也是很常見的需求,但實現並不簡單,基本有2種做法:

  • 如果在代碼層面做,就需要在請求失敗的回調裡重新發起一次,還要記錄次數,很是麻煩。
  • 如果在網絡層做,你就得對網絡層進行一次封裝,提供一個方法設置重試次數。 然而,這種方法弊端很大,不能和業務很好的聯繫。 因為網絡層並不知道什麼時候應該重試,網絡請求失敗就重試? 還是返回內容裡面標識不成功就重試呢?

本庫同樣提供了重試支持,因為有了異步支持,重試對框架來說,就是一個循環,然而這個循環框架都幫你寫好了,你只需告訴框架重試次數和什麼時候應該重試就可以了。

動態屬性設置能力

動態換膚或樣式修改也是一個很常見的需求,然而為了實現這樣的需求,往往需要開發者在代碼裡提前寫好根據配置修改的UI的代碼。

本庫同樣提供了支持,你可以通過一個 json 來對 wiget 進行屬性修改。 所以換個皮膚或改個樣式都是分分鐘的事啦。

單頁面應用(測試)

搞前端的應該很清楚這是什麼,就是所有渲染都是在一個頁面上展示,頁面跳轉都是通過前端路由來控制。 對應到客戶端,就是所有 UI 都在一個 activity 中展示。

這樣做有什麼意義? 安卓插件化最大的問題是四大組件需要提前在 manifest 中註冊,雖然目前有一些開源項目通過底層 hook 方式解決了這個問題,但是以後的安卓版本就不清楚會不會把這個限制了。 而且目前的插件化都需要對資源進行合併,這就使得成功率下降。

如果是單頁面應用,動態下發字節碼執行也變得有可能。 而且這樣成功率理論上接近 100%。 打算有時間嘗試一下。

我的意思就是像前端那樣具有隨時更新的能力,不知道會不會被封殺,逃。。。

快速開始

引入庫

【可選】 添加 java8 支持

<code>android {...    compileOptions {        sourceCompatibility JavaVersion.VERSION_1_8        targetCompatibility JavaVersion.VERSION_1_8    }...}/<code>

添加 maven 倉庫

<code>allprojects {    repositories {        ...        maven { url 'https://jitpack.io' }    }}/<code>

添加依賴

<code>def support_version = '28.0.0'def lifecycle_version = '1.1.1'implementation 'com.github.ittianyu:relight:0.1.0'implementation "com.android.support:appcompat-v7:$support_version"implementation "com.android.support:design:$support_version"// Support library depends on this lightweight importimplementation "android.arch.lifecycle:runtime:$lifecycle_version"/<code>

如果開啟了 java8

<code>// alternately - if using Java8, use the following instead of compilerimplementation "android.arch.lifecycle:common-java8:$lifecycle_version"/<code>

混淆

使用了 xml 支持,必須加入混淆,未使用的可以不加。

<code>-keep class * extends com.ittianyu.relight.widget.Widget {*;}/<code>



分享到:


相關文章: