數(shù)據(jù)共享與持久化——ViewModel 的使用與原理

介紹

ViewModel屬于ACC框架組件之一,用以解決數(shù)據(jù)持久與共享問(wèn)題,此外,也將數(shù)據(jù)的相關(guān)行為從UI中分離出來(lái)。

前言

對(duì)于ViewModel的使用以及原理,可能需要對(duì)Lifecycle和LiveData有一些理解,不然可能會(huì)影響對(duì)某些內(nèi)容的理解。以下為可參考資料。

正文

案例

public class MyData extends LiveData<String> {

    private static final String TAG = "T-MyData"; 

    public  MyData(){
        setValue("hi");
        Log.d(TAG, "create new liveData ");
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.d(TAG, "onActive ");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.d(TAG, "onInactive ");
    }

    public void changeValue(String value){
        setValue(value);
    }

}
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "T-MainActivity";

    private TabLayout nav;
    private Fragment nowFragment;

    private Fragment[] fs = new Fragment[]{
            new AFragment(),
            new BFragment()};

    private MViewModel mViewModel;
    MyData data;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, "activity onCreate ");

        nav = findViewById(R.id.nav);
        nav.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                if (tab.getText().equals("A")){
                    nowFragment = fs[0];
                }else {
                    nowFragment = fs[1];
                }
                getSupportFragmentManager().beginTransaction().replace(R.id.container, nowFragment).commit();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
        nav.addTab(nav.newTab().setText("A"));
        nav.addTab(nav.newTab().setText("B"));

        mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
        findViewById(R.id.attack).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyData data = mViewModel.getLiveData();
                data.changeValue(data.getValue() + "~");
            }
        });
    }
}
public class AFragment extends Fragment {

    View mainView;
    private TextView text;

    private MViewModel mViewModel;

    public AFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mainView = inflater.inflate(R.layout.fragment_a, container, false);
        text = mainView.findViewById(R.id.A_text);

        mViewModel = ViewModelProviders.of(getActivity()).get(MViewModel.class);
        mViewModel.getLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                text.setText("A--" + s);
            }
        });

        return mainView;
    }

}
public class BFragment extends Fragment {

    private View mainView;
    private TextView text;

    private MViewModel mViewModel;

    public BFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mainView = inflater.inflate(R.layout.fragment_b, container, false);
        text = mainView.findViewById(R.id.B_text);

        mViewModel = ViewModelProviders.of(getActivity()).get(MViewModel.class);
        mViewModel.getLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                text.setText("B--" + s);
            }
        });
        return mainView;
    }
}
public class MViewModel extends AndroidViewModel {

    private MyData data;

    public MViewModel(Application application) {
        super(application);
        data = new MyData();
    }

    public MyData getLiveData(){
        return data;
    }
}

頁(yè)面如下圖


ViewModel.jpg

描述:LiveData持有String數(shù)據(jù)初始為hi,A和Bfragment分別從ViewModel中獲取LiveData并監(jiān)聽其中數(shù)據(jù),在Activity上有一按鈕,每次點(diǎn)擊更新String數(shù)據(jù)為其本身加上"~"。
行為:?jiǎn)螜C(jī)幾次按鈕,來(lái)回切換A和B按鈕,可以看到數(shù)據(jù)在Fragment間都是最新的(圖不貼,懶),翻轉(zhuǎn)屏幕,再次觀察,日志如下圖


viewmodel日志.jpg

從日志圖中可得到的信息如下:

  • Activity與Fragment被重建
  • LiveData對(duì)Fragment的綁定關(guān)系被重建
  • ViewModel沒有被重建,持有原來(lái)的數(shù)據(jù)對(duì)象
    從運(yùn)行情況可以得知:
  • 數(shù)據(jù)保持共享

ViewModel是如何做到數(shù)據(jù)持久化以及數(shù)據(jù)共享的?以下為講解

提醒

由于版本問(wèn)題,ViewModel對(duì)較低版本的SDK做了兼容,因此在實(shí)現(xiàn)原理上分有兩種做法。在此提前點(diǎn)明情況,方便以下的敘述順序流暢。

原理一(此SKD為27)

入口

mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        // 獲取當(dāng)前程序所依托的Application
        Application application = checkApplication(activity);
        if (factory == null) {
            // 獲取AndroidViewModelFactory,單例
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        // ViewModelStores.of()返回了ViewModelStore
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

這里只要注意,of()返回了ViewModelProvider,其持有ViewModelStore信息和AndroidViewModelFactory。 而ViewModelStore其實(shí)規(guī)劃了自身將被如何存儲(chǔ)

當(dāng)前位置
ViewModelProviders.of()
- ViewModelProvider()
-- ViewModelStores.of()

    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        // 當(dāng)前SDK下,運(yùn)行到這里就返回了,證據(jù)在下一張代碼引用圖
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }
public class FragmentActivity extends BaseFragmentActivityApi16 implements
        ViewModelStoreOwner,

既然FragmentActivity實(shí)現(xiàn)了ViewModelStoreOwner,那么對(duì)應(yīng)的獲取方式如下

當(dāng)前位置
FragmentActivity. getViewModelStore()

    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        // 為空新建
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }

可見,F(xiàn)ragmentActivity自身是持有ViewModelStore

以上構(gòu)造出了ViewModelProvider,緊接著去獲取具體的ViewModel

當(dāng)前位置ViewModelProvider
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        // 類名
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // 從ViewModelStore中獲取viewModel
        ViewModel viewModel = mViewModelStore.get(key);
        
        // 獲取到,返回
        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        } else {
            if (viewModel != null) {
            }
        }
        // 創(chuàng)建viewModel,factory為AndroidViewModelFactory
        viewModel = mFactory.create(modelClass);
        // 將ViewModel與類名綁定,并保存
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

以上代碼就獲取到了具體的ViewModel,其中AndroidViewModelFactory.create()代碼僅僅是通過(guò)反射創(chuàng)建了ViewModel實(shí)例并捕捉了異常。先做個(gè)小結(jié):

  • ViewModelProvider持有ViewModelStore信息
  • VIewModelStore規(guī)劃自身的提取方式并持有ViewModel信息
  • 通過(guò)類作為key獲取具體的ViewModel實(shí)現(xiàn)共享

我們知道,在屏幕旋轉(zhuǎn)時(shí),如果沒有對(duì)Activity做相應(yīng)的配置更變?cè)O(shè)置,Activity是會(huì)被重建的,而Activity被銷毀時(shí),相應(yīng)持有的數(shù)據(jù)理應(yīng)被釋放。那ViewModel是如何逃過(guò)一劫的?

思考

在源碼看到這的時(shí)候,實(shí)際上正面線索已無(wú)法跟蹤,因?yàn)樵邶嫶蟮腁ctivity架構(gòu)之中,很難快速地找到事件發(fā)源地。那現(xiàn)在,如何找到ViewModel持久的線索呢?在當(dāng)前條件下,F(xiàn)ragmentActivty是直接持有了ViewModelStore的信息,那么在配置更變需要重建時(shí),必定要對(duì)ViewModelStore做一些處理,因此選擇了跟蹤FragmentActity.mViewModelStore。

果然,定位到了以下位置

FragmentActivity

    public final Object onRetainNonConfigurationInstance() {
       .......
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }

在配置更改需要重建頁(yè)面的時(shí)候,系統(tǒng)會(huì)去保存現(xiàn)場(chǎng)以便恢復(fù),在這個(gè)函數(shù)中,ViewModelStore被作為狀態(tài)之一被保存在NonConfigurationInstances之中。依次類推,有保存就有取出,繼續(xù)跟蹤,定位到了以下位置

FragmentActivity

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        .....
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mViewModelStore = nc.viewModelStore;
        }
      .....
  }

可見,在生命周期onCreate,備份的ViewModelStore被取出。因此,當(dāng)重建后,再次通過(guò)ViewModelProvider.get()去獲取ViewModel時(shí)候,會(huì)直接獲取到此ViewModelStore并取出ViewModel,不會(huì)再通過(guò)AndroidVIewModelFactory重建ViewModel。

這里小結(jié)一下:

  • 在配置更變需要重建頁(yè)面時(shí),ViewModelStore會(huì)在重建前交由NonConfigurationInstances保管,并在重建后取出恢復(fù)。

以上,就是ViewModel在高SDK下的數(shù)據(jù)共享與持久化的原理。

接下來(lái),是適配低版本的。

原理二 (實(shí)例SDK為25)

之前說(shuō)過(guò),ViewModelStore規(guī)劃了自身將被如何存儲(chǔ),而且差異也在于低版本的SDK的Activity的各父類并不是ViewModelStoreOwner,回看代碼ViewModelStore.of()

    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        // 當(dāng)前SDK下,運(yùn)行到此返回
        return holderFragmentFor(activity).getViewModelStore();
    }

以上代碼從HolderFragment取得ViewModelStore

當(dāng)前位置
HolderFragment

    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

當(dāng)前位置
HolderFragement.HolderFragmentManager

        HolderFragment holderFragmentFor(FragmentActivity activity) {
            FragmentManager fm = activity.getSupportFragmentManager();
            // 查找合適的HolderFragment
            HolderFragment holder = findHolderFragment(fm);
            // 查找到返回
            if (holder != null) {
                return holder;
            }
            // 通過(guò)key(activity)取出HolderFragment
            holder = mNotCommittedActivityHolders.get(activity);
           // 取到返回
            if (holder != null) {
                return holder;
            }
            
            if (!mActivityCallbacksIsAdded) {
                mActivityCallbacksIsAdded = true;
                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
            }
            // 注入HolderFragment
            holder = createHolderFragment(fm);
            // 綁定HolderFragment和key
            mNotCommittedActivityHolders.put(activity, holder);
            return holder;
        }

以上代碼代碼可知,HolderFragment靜態(tài)對(duì)象HolderFragmentManager,持有HolderFragment對(duì)象與key的對(duì)應(yīng)管理。這里因?yàn)镠olderFragment被注入,需要看一下初始工作。

當(dāng)前位置HolderFragment
    private ViewModelStore mViewModelStore = new ViewModelStore();

    public HolderFragment() {
        setRetainInstance(true);
    }

新的HolderFragment新建時(shí)自身持有了ViewMolderStore,之前通過(guò)ViewModelStores.of()獲取的,就是這個(gè)ViewMolderStore。

能區(qū)別出,在原理一種,具有生命周期的對(duì)象,本身會(huì)持有ViewModelStore,而在原理二中,會(huì)通過(guò)注入HolderFrament,去間接持有ViewModelStore。其他流程是一致的。

現(xiàn)在,就還剩一個(gè)問(wèn)題,簡(jiǎn)介持有的ViewModelStore,如何保持持久化?

注意到,在初始化HolderFragment是,設(shè)置了mRetainInstance,如下

    /**
     * Control whether a fragment instance is retained across Activity
     * re-creation (such as from a configuration change).  This can only
     * be used with fragments not in the back stack.  If set, the fragment
     * lifecycle will be slightly different when an activity is recreated
     */
    public void setRetainInstance(boolean retain) {
        mRetainInstance = retain;
    }

注釋大意為:在Activity 銷毀-重建時(shí)控制是否是有fragment實(shí)例。僅在fragment不在back stack時(shí)生效。當(dāng)mRetainInstance設(shè)置為trues時(shí),生命周期表現(xiàn)行為與重建時(shí)有輕微不同。

簡(jiǎn)單來(lái)說(shuō),HolderFragment并沒有被銷毀,而當(dāng)再次通過(guò)key去取出對(duì)應(yīng)的HolderFragment時(shí),就能取出。

至于HolderFragment為什么沒有被銷毀,那就需要了解FragmentManager如何去管理Fragment了,這就扯遠(yuǎn)了。

總結(jié)

通過(guò)以上的梳理分析,算是講明了ViewModel的數(shù)據(jù)如何共享以及持久化,一下為要點(diǎn):

  • 通過(guò)ViewModelProvider持有ViewModelStore和Factory,并主要用來(lái)獲取對(duì)應(yīng)的ViewModelStore
  • ViewModelStore存儲(chǔ)了key-value形勢(shì)的類與ViewModel的對(duì)應(yīng),實(shí)現(xiàn)了數(shù)據(jù)的共享,F(xiàn)actory負(fù)責(zé)在需要時(shí)創(chuàng)建出ViewModel
  • ViewModelStore被Activity或Fragment持有, 或通過(guò)注入HolderFragment間接持有
  • Activity因配置原因銷毀-重建時(shí),ViewModelStore被NonConfigurationInstances保存或被HolderFragment保存,再此需求時(shí)從從保存處恢復(fù)。

簡(jiǎn)單原理圖


ViewModel原理-2.png

細(xì)節(jié)

當(dāng)前位置
HolderFragment

    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sHolderFragmentManager.holderFragmentCreated(this);
    }


當(dāng)前位置
HolderFragment.HolderFragmentManager

        void holderFragmentCreated(Fragment holderFragment) {
            // 獲取作為依托的父fragment
            Fragment parentFragment = holderFragment.getParentFragment();
            if (parentFragment != null) {
                // 釋放父fragment
                mNotCommittedFragmentHolders.remove(parentFragment);
                parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
                        mParentDestroyedCallback);
            } else {
                //釋放Activity
                mNotCommittedActivityHolders.remove(holderFragment.getActivity());
            }
        }

在Activity銷毀-重建狀態(tài)下,雖然ViewModelStore跟隨HolderFragment被保存了,但是此時(shí)的與HolderFragment綁定的Activity或Fragment已不再是當(dāng)時(shí)候的對(duì)象,因此,會(huì)存在內(nèi)存泄漏問(wèn)題。因此,在HolderFragment生命周期onCreate()里解決這一問(wèn)題。

注意到,HolderFragment與Activity或Fragment間的對(duì)應(yīng)關(guān)系鏈已不存在,那么再去獲取對(duì)應(yīng)的HolderFragment是,會(huì)通過(guò)holderFragmentFor() ->findHolderFragment() 找到,如下圖


viewModel獲取Holder.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容