ViewModel之自定義構(gòu)造函數(shù)

剛?cè)肟蛹軜?gòu)組件沒(méi)多久,發(fā)現(xiàn)很多基礎(chǔ)性的東西理解起來(lái)是沒(méi)什么問(wèn)題的,但是一到具體使用就各種問(wèn)題,相關(guān)實(shí)踐文章也比較少,更多的只能靠自己解決 = =。今天無(wú)意間了解了AndroidViewModel的一個(gè)使用場(chǎng)景實(shí)現(xiàn)原理,特地記錄下來(lái)。

前言

一開(kāi)始,跟著官方文檔,ViewModel我們是這樣實(shí)現(xiàn)的:

public class MyViewModel entends ViewModel {
   //...
}

然后是創(chuàng)建實(shí)例對(duì)象:

MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);

可以注意到,我們定義的ViewModel是沒(méi)有構(gòu)造函數(shù)的,也就是說(shuō)如果遇到比較復(fù)雜情況下(需要在創(chuàng)建ViewModel時(shí)賦一些初始值),應(yīng)該怎么辦呢?總不能自己加一個(gè)構(gòu)造函數(shù),然后new出來(lái)吧,或者通過(guò)set方法一個(gè)一個(gè)加也行,但是這樣就有點(diǎn)low了。于是,我發(fā)現(xiàn)了一個(gè)類(lèi)AndroidViewModel

AndroidViewModel

AndroidViewModel這個(gè)類(lèi)的定義很簡(jiǎn)單,它繼承自ViewModel,然后添加了一個(gè)application私有屬性:

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}

上面提到,很多時(shí)候我們需要在初始化ViewModel時(shí)需要傳遞一些參數(shù),例如Activity中的一些參數(shù)等,但是根據(jù)ViewModel的生命周期我們知道,ViewModel中是不能傳入Activity等實(shí)例對(duì)象的,因?yàn)樵?code>ViewModel存活的過(guò)程中,Activity是有可能會(huì)被銷(xiāo)毀的。因此,Google爸爸推薦我們傳入application

下面是AndroidViewModel的一個(gè)使用示例,其實(shí)我們只需要將ViewModel改為繼承AndroidViewModel就可以了

public class MyViewModel extends AndroidViewModel {
    
    public MyViewModel(@NonNull Application application) {
        super(application);
    }
    
    //...
}

使用的話(huà)還是一樣的:

MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);

AndroidViewModel實(shí)現(xiàn)原理

到了這里,疑問(wèn)就來(lái)了,我創(chuàng)建實(shí)例的時(shí)候并依然沒(méi)有傳遞任何參數(shù),那么那個(gè)application是怎么來(lái)的呢,下面我們來(lái)看下源碼中是怎么實(shí)現(xiàn)的:

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

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
                                   @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

ViewModelProviders中有很多of的重載方法,這里暫時(shí)只需要關(guān)注上面這兩個(gè)即可,可以看到,application是從我們傳入的activity中獲取到的,并且此時(shí)還創(chuàng)建了一個(gè)factory實(shí)例對(duì)象(劃重點(diǎn),后面會(huì)用到)。默認(rèn)情況下(傳遞的factory為null),那么會(huì)自動(dòng)創(chuàng)建一個(gè)AndroidViewModelFactory ,AndroidViewModelFactoryViewModelProvider中的一個(gè)靜態(tài)內(nèi)部類(lèi)
接著我們來(lái)看get方法:

//ViewModelProvider.java
@NonNull
@MainThread
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);
}

@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 {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

可以看到,最終生成的ViewModel是通過(guò)(mFactory).create方法生成的,這個(gè)factory就是上面所提到的,然后在去看他的create方法實(shí)現(xiàn):

//ViewModelProvider.java
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);
    }
}

轉(zhuǎn)到AndroidViewModelFactory的實(shí)現(xiàn),可以看到默認(rèn)生成的factorycreate方法中使用反射調(diào)用了ViewModel的構(gòu)造函數(shù),至此,整個(gè)過(guò)程就還原了。

自定義ViewModel構(gòu)造函數(shù)

了解了AndroidViewModel的整個(gè)實(shí)現(xiàn)流程后,自定義流程就好辦了,思路是一致的,需要借助一個(gè)Factory來(lái)實(shí)現(xiàn):

public class MyViewModel2 extends ViewModel {


    private String name;

    public MyViewModel2(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //...
}
public class MyViewModelFactory implements ViewModelProvider.Factory {

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass.isAssignableFrom(MyViewModel2.class)) {
            return (T) new MyViewModel2("Tom");
        }
        throw new RuntimeException("unknown class :" + modelClass.getName());
    }

}
MyViewModel2 myViewModel2 = ViewModelProviders.of(this, new MyViewModelFactory()).get(MyViewModel2.class);
Log.d("TAG", "onCreate: name = "+myViewModel2.getName());

大功告成。
雖然這里只是一個(gè)簡(jiǎn)單的例子,但是既然思路已經(jīng)出來(lái)了,剩下的就是八仙過(guò)海各顯神通了。

還在踩坑階段,如有什么解釋不當(dāng)?shù)牡胤綒g迎指正。()

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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