谷歌架構(gòu)組件(一)ViewModel的使用與分析

ViewModel 是google推出的架構(gòu)組件之一蜜葱,它被設(shè)計用于存儲和管理UI相關(guān)的數(shù)據(jù)侠姑。

背景:

1方便數(shù)據(jù)存儲

以生命周期的方式存儲和管理UI相關(guān)數(shù)據(jù)助析。當屏幕旋轉(zhuǎn)等改變時妄痪,數(shù)據(jù)能夠被恢復(fù)。

2生命周期控制

使用fragment時我們都會對它復(fù)雜的生命周期處理感到痛苦卓起,稍有不慎報各種null異常。ViewModel凹炸,Lifecycle等組件的推出戏阅,可以有效的解決生命周期的處理問題,提高app的穩(wěn)定性啤它。`

3 DataBinding數(shù)據(jù)驅(qū)動UI

從字面意思看ViewModel就是activity fragment的數(shù)據(jù)抽象模型奕筐。我們知道activity fragment是由系統(tǒng)管理,不受app控制蚕键,當內(nèi)存不足時救欧,系統(tǒng)隨時都有可能回收殺死app。所以這也是android app開發(fā)具有挑戰(zhàn)的地方锣光,我們需要在不確定的環(huán)境下保證我們的業(yè)務(wù)流程順利進行笆怠。由于ViewModel設(shè)計是和activity fragment的生命周期是解耦的,所以當activity fragmegnt重新create時誊爹,如果ViewModel已經(jīng)創(chuàng)建過蹬刷,則仍使用原ViewModel。

ViewMode生命周期見下圖:


viewmodel-lifecycle.png

如何使用:

1自定義ViewModel

    class UserModel extends ViewModel{  
      String name;  
      String age;  
      
    } 

2 獲取ViewModel

public class MyActivity extends AppCompatActivity {  
    public void onCreate(Bundle savedInstanceState) {  
        // Create a ViewModel the first time the system calls an activity's onCreate() method.  

            // Re-created activities receive the same MyViewModel instance created by the first activity.  
      
            UserModel model = ViewModelProviders.of(this).get(UserModel.class);  
            
        }  
    } 

因為ViewModel生命周期感知的频丘,所以我們不需要手動釋放办成,使用起來非常方便。

注意事項:

ViewModel不可以持有activity fragment等view的引用搂漠,否則會導(dǎo)致內(nèi)存泄漏迂卢。

讀到這里你可能會有如下疑問:

  1. ViewModel 為什么不可以持有activity fragment等View的引用

  2. ViewModel 如果感知activity fragment的生命周期

  3. ViewModel 如何保存數(shù)據(jù)

接下來我們根據(jù)上述疑問,跟蹤ViewModel 的相關(guān)代碼具體分析桐汤。

111.png

以ViewModelProviders.of(this).get(UserModel.class); 這行代碼作為切入點

@MainThread  
public static ViewModelProvider of(@NonNull Fragment fragment) {  
    FragmentActivity activity = fragment.getActivity();  
    if (activity == null) {  
        throw new IllegalArgumentException(  
                "Can't create ViewModelProvider for detached fragment");  
    }  
    initializeFactoryIfNeeded(activity.getApplication());  
    return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);  
} 

1 創(chuàng)建默認的工廠方法而克,實例化新的ViewModels

2 通過傳入fragment或者acitity參數(shù),new ViewModelProvider實例并返回怔毛。

這里有2個關(guān)鍵的代碼點

1 ViewModelStores.of(fragment)

2 return new ViewModelProvider(…)

針對代碼點1 ViewModelStores.of(fragment) 查看ViewModelStores源碼

    @MainThread  
    public static ViewModelStore of(FragmentActivity activity) {  
        return holderFragmentFor(activity).getViewModelStore();  
    }  

它通過holderFragmentFor函數(shù)返回ViewModelStore對象

繼續(xù)跟蹤holderFragmentFor函數(shù)员萍,找到HolerFragment 這個關(guān)鍵類

從HolerFragment類的代碼中我們找到了ViewModel感知fragment,activity生命周期的原因拣度。

Google在底層默默的create了一個新的HolderFragment 對象碎绎,

由HolderFragment負責(zé)生命周期感知螃壤,當onDestroy()時清理ViewModelStore

持有ViewModelStore對象,setRetainInstance(true) 保證當界面旋轉(zhuǎn)被銷毀再重建時保證mViewModelStore 不被銷毀筋帖。但對于因內(nèi)存被系統(tǒng)殺死后重新進入奸晴,數(shù)據(jù)不會被恢復(fù)。

public class HolderFragment extends Fragment {    
  
      private ViewModelStore mViewModelStore = new ViewModelStore();  
  
      public HolderFragment() {  
        setRetainInstance(true);  
      }  
      
        HolderFragment holderFragmentFor(Fragment parentFragment) {    
                FragmentManager fm = parentFragment.getChildFragmentManager();    
                HolderFragment holder = findHolderFragment(fm);    
                ......
                holder = createHolderFragment(fm);    
                mNotCommittedFragmentHolders.put(parentFragment, holder);    
                return holder;    
            }    
        
    } 

解決了ViewModel如果感知生命周期的問題幕随,我們再分析下ViewModel的數(shù)據(jù)存儲蚁滋。

我們查看ViewModelStore這個類,發(fā)現(xiàn)ViewModel是被存儲在一個HashMap內(nèi)赘淮,所以它是保存在app內(nèi)存中辕录,并沒有做持久化處理。

public class ViewModelStore {  
  
    private final HashMap<String, ViewModel> mMap = new HashMap<>();  
  
    ......

} 

針對代碼點2 return new ViewModelProvider(…)梢卸,我們看下ViewModelProvider走诞。

從代碼中可知ViewModelProvider是對ViewModelStore 和Factory的封裝,當viewModel存著時獲取ViewModel蛤高,如果不存在則調(diào)用工廠方法創(chuàng)建ViewModel蚣旱。

public class ViewModelProvider {  
    private final Factory mFactory;  
    private final ViewModelStore mViewModelStore;  
  
    public ViewModelProvider(ViewModelStore store, Factory factory) {  
        mFactory = factory;  
        this.mViewModelStore = store;  
    }  
  
      
        @NonNull  
        @MainThread  
        public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {  
            ViewModel viewModel = mViewModelStore.get(key);  
      
            if (modelClass.isInstance(viewModel)) {  
                //noinspection unchecked  
                return (T) viewModel;  
            } else {  
                ......  
            }  
      
            viewModel = mFactory.create(modelClass);  
            mViewModelStore.put(key, viewModel);  
            //noinspection unchecked  
            return (T) viewModel;  
        }  
      
    } 

總結(jié):

通過代碼的跟蹤分析我們解決了3大疑問,發(fā)現(xiàn)ViewModel的實現(xiàn)主要依賴于HolderFragment類的實現(xiàn)戴陡。

  • 通過添加新的HolderFragment感知生命周期的變化

  • 通過HolderFragment持有ViewModelStore對象

  • ViewModel存儲在ViewModelStore的hashmap內(nèi)存中塞绿,不做持久化數(shù)據(jù)存儲,當activity fragment處于后臺因內(nèi)存問題被系統(tǒng)殺死后恤批,重新進入后數(shù)據(jù)不會被恢復(fù)异吻。

  • Fragment和Activity作為key訪問獲取ViewModel對象

  • ViewModel不能持有activity,fragment等view的引用喜庞,避免內(nèi)存泄漏

參考
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
android source code 8.0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诀浪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子延都,更是在濱河造成了極大的恐慌雷猪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晰房,死亡現(xiàn)場離奇詭異求摇,居然都是意外死亡,警方通過查閱死者的電腦和手機殊者,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門与境,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人幽污,你說我怎么就攤上這事嚷辅〔疽蹋” “怎么了距误?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵簸搞,是天一觀的道長。 經(jīng)常有香客問我准潭,道長趁俊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任刑然,我火速辦了婚禮寺擂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泼掠。我一直安慰自己怔软,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布择镇。 她就那樣靜靜地躺著挡逼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腻豌。 梳的紋絲不亂的頭發(fā)上家坎,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音吝梅,去河邊找鬼虱疏。 笑死,一個胖子當著我的面吹牛苏携,可吹牛的內(nèi)容都是我干的做瞪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼兜叨,長吁一口氣:“原來是場噩夢啊……” “哼穿扳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起国旷,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矛物,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后跪但,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體履羞,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年屡久,在試婚紗的時候發(fā)現(xiàn)自己被綠了忆首。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡被环,死狀恐怖糙及,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筛欢,我是刑警寧澤浸锨,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布唇聘,位于F島的核電站,受9級特大地震影響柱搜,放射性物質(zhì)發(fā)生泄漏迟郎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一聪蘸、第九天 我趴在偏房一處隱蔽的房頂上張望宪肖。 院中可真熱鬧,春花似錦健爬、人聲如沸控乾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阱持。三九已至,卻和暖如春魔熏,著一層夾襖步出監(jiān)牢的瞬間衷咽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工蒜绽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镶骗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓躲雅,卻偏偏與公主長得像鼎姊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子相赁,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容