Android開發 ViewModel_2_了解多種自定義實例方式 (Factory 與 key)


前言

  ViewModel的雖然可以以很簡單方便的 ViewModelProviders.of(getActivity()).get(ViewModel.class); 但是這只是google提供的簡單構建,你需要引入下面的庫才能使用:

implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"

但是!遠遠不止這樣。l還可以使用ViewModelProvider.Factory自定義實例ViewModel(其實繼承工廠類實例ViewModel才是google推薦的,這點在Android studio的模板代碼里可以看到)

為什么要學習ViewModel自定義實例?

  那么你可能會疑問了,使用 ViewModelProviders.of(getActivity()).get(ViewModel.class); 創建ViewModel這么簡單輕松,為什么還要自己構建Factory呢。原因有以下幾點:

  1. 如果你繼承的ViewModel類是有參構造,那么上面這個方式是不支持在實例ViewModel的傳參的,所以需要自己構建Factory
  2. 一個Activity可能根據業務會有多個不同的ViewModel,那么根據不同的業務下配置不同的ViewModel,需要自己構建Factory類,來根據類別返回不同的ViewModel給外部(這也是工廠模式的核心,解決接口選擇的問題)

了解ViewModel的構建原理

  因為后續的使用ViewModelProvider.Factory創建ViewModel,所以我們必需了解一些些ViewModel構建、保存、移除原理。好在有一個參照物可以閱讀理解,那就是上面提到以 ViewModelProviders 形式的構建ViewModel。對照着它看基本上就能明白一些東西了。

看看 ViewModelProviders.of() 干了啥事情

我們點擊進入源碼后可以看到如下代碼

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

恩,of又去調用了一個重載的of,但是傳入了一個activity或者Fragment 和 一個null ,我們看看這個2個參數的of干了什么

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity); //源碼在這里檢查了一下activity的application
        if (factory == null) {
            //它拿application去創建了一個Factory類
            //這個factory其實就是構建ViewModel的工廠類
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); 
        }
         //實例化了一個ViewModelProvider,
         //在activity里取得ViewModelStore(這個東西是用於保存ViewModel使用的),也傳入了
         //並且也把Factory類一起傳入
         //其實ViewModelStore和factory傳入后就直接成為成員變量緩存了,等待你調用get方法來創建ViewModel
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

我們可以看看ViewModelProvider.AndroidViewModelFactory.getInstance(application) 這行代碼

    private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

可以看到,直接給你了一個有Application的全局單例的Factory類。

接下來看看ViewModelProviders.of().get()    get方法干了啥事

  get()方法其實就是實現了 factory 創建 與 ViewModelStore保存 ViewModel,並且返回ViewModel。 我們可以看看下面這些代碼

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName(); //獲取了傳入的實現的ViewModel的類名
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //因為我們沒有傳入key,所以這里直接用類名取代的成為了key
        //然后就進入下面這個重載的get方法里進行實現了
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass); 
    }

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);//去查找這個ViewModelStore 對應key的保存viewModel

        if (modelClass.isInstance(viewModel)) { //檢查一下這個viewModel是不是已經實例化,不是null
            //noinspection unchecked
            return (T) viewModel;  //如果已經實例化,不等於null就直接返回
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //下面就用傳入的Factory類來實例化你需要的ViewModel
        if (mFactory instanceof KeyedFactory) { //這里檢查是不是帶key
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass); //這里的create使用了反射的形式創建實例
        }
        mViewModelStore.put(key, viewModel); //然后存一下ViewModel
        //noinspection unchecked
        return (T) viewModel; //返回viewModel
    }

移除緩存的ViewModel

  其實蛛絲馬跡就在ViewModelStore

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() { //這里清理了當前Activity或者Fragment的所有ViewModel,這個方法是在Activity或者Fragment在銷毀的時候有調用的
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

 

使用ViewModelProvider.Factory實現創建ViewModel

 使用反射創建無參實例(在上面的源碼上可以看到的創建方式,這樣一個Factory就可以傳入繼承ViewModel的泛型,加以實例)

public class MainViewModelFactory implements ViewModelProvider.Factory {
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        try {
            return modelClass.newInstance(); //使用newInstance反射實例ViewModel,並且傳出去
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;

    }
}

使用MainViewModelFactory創建ViewModel,注意!千萬別自己就使用Factory直接創建ViewModel,因為上面的get()源碼里說明了,這個ViewModel是還需要保存到ViewModelStore里面的,而ViewModelStore的put方法是私有方法。所以我們還是需要交給ViewModelProviders.of().get();來創建讓它幫我們保存到ViewModelStore里,代碼如下:

    private MainViewModel mMainViewModel;
    private void initViewModel() {
        MainViewModelFactory mainViewModelFactory = new MainViewModelFactory();
        mMainViewModel = ViewModelProviders.of(this, mainViewModelFactory).get(MainViewModel.class);
    }

使用反射創建有參實例

代碼如下:

public class MainViewModelFactory4 implements ViewModelProvider.Factory {
    private String mValue;

    public MainViewModelFactory4(String value){
        mValue = value;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        try {
            Class[] parameterTypeArray = new Class[]{String.class};
            return modelClass.getConstructor(parameterTypeArray).newInstance(mValue);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;

    }
}

還是跟上面一樣使用ViewModelProviders.of().get();來創建。

簡單暴力直接實例目標ViewModel

public class MainViewModelFactory implements ViewModelProvider.Factory {
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        MainViewModel mainViewModel = new MainViewModel();
        mainViewModel.setContent("設置初始化值"); 
        return (T)mainViewModel;

    }
}

還是跟上面一樣使用ViewModelProviders.of().get();來創建。

使用KEY創建ViewModel

這沒啥好說的,就是根據不同的key創建獨立數據與內存地址的ViewModel,他們之間數據不互通。

 

        mMainViewModel1 = ViewModelProviders.of(this).get("1", MainViewModel.class);
        mMainViewModel2 = ViewModelProviders.of(this).get("2", MainViewModel.class);
        mMainViewModel3 = ViewModelProviders.of(this).get("3", MainViewModel.class);

 

關於ViewModelProvider.NewInstanceFactory

也是繼承Factory,然后幫你實現了newInstance();如果你只是簡簡單單實例一個ViewModel,可以直接繼承這個,我們可以看源碼:

/**
     * Simple factory, which calls empty constructor on the give class.
     */
    public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

關於ViewModelProvider.AndroidViewModelFactory

帶application的工廠類,你需要配置一個需要application的ViewModel,可以直接使用這個單例的AndroidViewModelFactory。可以看源碼(下面的代碼上面已經貼了一部分了,這里貼全),需要一個構造方式是傳入application的ViewModel:

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }

 

End


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM