【问题标题】:Architecture Components: How does the ViewModelProvider know which constructor to call?架构组件:ViewModelProvider 如何知道调用哪个构造函数?
【发布时间】:2017-06-18 19:32:53
【问题描述】:

我一直在阅读 Android 中引入的新架构组件,但我不知道它是如何工作的:

ViewModelProviders.of(Activity).get(Class)

最初我认为它会调用默认构造函数并返回一个 ViewModel 对象,然后您可以将其实例化,例如。根据

的 init() 方法
public class UserProfileViewModel extends ViewModel {
    private String userId;
    private User user;

    public void init(String userId) {
        this.userId = userId;
    }
    public User getUser() {
        return user;
    }
}

摘自指南:https://developer.android.com/topic/libraries/architecture/guide.html

但是,在指南的后面有这个 sn-p:

public class UserProfileViewModel extends ViewModel {
    private LiveData<User> user;
    private UserRepository userRepo;

    @Inject // UserRepository parameter is provided by Dagger 2
    public UserProfileViewModel(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    public void init(String userId) {
        if (this.user != null) {
            // ViewModel is created per Fragment so
            // we know the userId won't change
            return;
        }
        user = userRepo.getUser(userId);
    }

那么 ViewModelProvider 是如何知道调用提供的构造函数的呢?或者它看到只有 1 个构造函数并调用它?例如如果有 2 个构造函数会发生什么?

我尝试挖掘代码,发现:

@Override
        public <T extends ViewModel> T create(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);
        }

ViewModelProviders.java 内部的DefaultFactory 类内部。然而,这让我更加困惑。即使ViewModel 对象没有将应用程序作为参数的构造函数,getConstructor(Application.class) 如何工作?

【问题讨论】:

    标签: java android lifecycle


    【解决方案1】:

    在 sn-p 中有一个条件检查 modelClass 是否为 AndroidViewModel 类型(继承 ViewModel),该构造函数采用 Application 参数。这更像是一种排他性的情况,可以避免 Factory 查找匹配特定参数的构造函数。 此提供程序在创建时查找与提供程序参数匹配的构造函数:

    public class ViewModelParameterizedProvider {
    
        private AtomicBoolean set = new AtomicBoolean(false);
    
        private ViewModelStore viewModelStore = null;
    
    
        static ViewModelParameterizedProvider getProvider() {
            return new ViewModelParameterizedProvider();
        }
    
        @MainThread
        public static ViewModelProvider ofSupportFragment(Fragment fragment, Object... params) {
            return getProvider().of(fragment).with(params);
        }
    
        @MainThread
        public static ViewModelProvider ofActivity(FragmentActivity fragmentActivity, Object... params) {
            return getProvider().of(fragmentActivity).with(params);
        }
    
        @MainThread
        public static ViewModelProvider ofFragment(android.app.Fragment fragment, Object... params) {
            return getProvider().of(fragment).with(params);
        }
    
        private ViewModelParameterizedProvider of(Fragment fragment) {
            checkForPreviousTargetsAndSet();
            viewModelStore = ViewModelStores.of(fragment);
            return this;
        }
    
        private ViewModelParameterizedProvider of(android.app.Fragment fragment) {
            FragmentActivity fragAct = (FragmentActivity) fragment.getActivity();
            return of(fragAct);
        }
    
        private ViewModelParameterizedProvider of(FragmentActivity activity) {
            checkForPreviousTargetsAndSet();
            viewModelStore = ViewModelStores.of(activity);
            return this;
        }
    
    
        private ViewModelProvider with(Object... constructorParams) {
            return new ViewModelProvider(viewModelStore, parametrizedFactory(constructorParams));
        }
    
    
        private void checkForPreviousTargetsAndSet() {
            if (set.get()) {
                throw new IllegalArgumentException("ViewModelStore already has been set. Create new instance.");
            }
            set.set(true);
        }
    
        private ViewModelProvider.Factory parametrizedFactory(Object... constructorParams) {
            return new ParametrizedFactory(constructorParams);
        }
    
    
        private final class ParametrizedFactory implements ViewModelProvider.Factory {
            private final Object[] mConstructorParams;
    
            ParametrizedFactory(Object... constructorParams) {
                mConstructorParams = constructorParams;
            }
    
            @Override
            public <T extends ViewModel> T create(Class<T> modelClass) {
                if (modelClass == null) {
                    throw new IllegalArgumentException("Target ViewModel class can not be null")
                }
                Log.w("ParametrizedFactory", "Don't use callbacks or Context parameters in order to avoid leaks!!")
                try {
                    if (mConstructorParams == null || mConstructorParams.length == 0) {
                        return modelClass.newInstance();
                    } else {
                        Class<?>[] classes = new Class<?>[mConstructorParams.length];
                        for (int i = 0; i < mConstructorParams.length; i++) {
                            classes[i] = mConstructorParams[i].getClass();
                        }
                        return modelClass.getConstructor(classes).newInstance(mConstructorParams);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }
    }
    

    这里是kotlin version。 这里是more read on the subject

    【讨论】:

      猜你喜欢
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多