• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.lifecycle;
18 
19 import android.app.Application;
20 
21 import androidx.annotation.MainThread;
22 import androidx.annotation.NonNull;
23 
24 import java.lang.reflect.InvocationTargetException;
25 
26 /**
27  * An utility class that provides {@code ViewModels} for a scope.
28  * <p>
29  * Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained
30  * from {@link androidx.lifecycle.ViewModelProviders} class.
31  */
32 @SuppressWarnings("WeakerAccess")
33 public class ViewModelProvider {
34 
35     private static final String DEFAULT_KEY =
36             "androidx.lifecycle.ViewModelProvider.DefaultKey";
37 
38     /**
39      * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
40      */
41     public interface Factory {
42         /**
43          * Creates a new instance of the given {@code Class}.
44          * <p>
45          *
46          * @param modelClass a {@code Class} whose instance is requested
47          * @param <T>        The type parameter for the ViewModel.
48          * @return a newly created ViewModel
49          */
50         @NonNull
create(@onNull Class<T> modelClass)51         <T extends ViewModel> T create(@NonNull Class<T> modelClass);
52     }
53 
54     private final Factory mFactory;
55     private final ViewModelStore mViewModelStore;
56 
57     /**
58      * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
59      * {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
60      *
61      * @param owner   a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
62      *                retain {@code ViewModels}
63      * @param factory a {@code Factory} which will be used to instantiate
64      *                new {@code ViewModels}
65      */
ViewModelProvider(@onNull ViewModelStoreOwner owner, @NonNull Factory factory)66     public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
67         this(owner.getViewModelStore(), factory);
68     }
69 
70     /**
71      * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
72      * {@code Factory} and retain them in the given {@code store}.
73      *
74      * @param store   {@code ViewModelStore} where ViewModels will be stored.
75      * @param factory factory a {@code Factory} which will be used to instantiate
76      *                new {@code ViewModels}
77      */
ViewModelProvider(@onNull ViewModelStore store, @NonNull Factory factory)78     public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
79         mFactory = factory;
80         this.mViewModelStore = store;
81     }
82 
83     /**
84      * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
85      * an activity), associated with this {@code ViewModelProvider}.
86      * <p>
87      * The created ViewModel is associated with the given scope and will be retained
88      * as long as the scope is alive (e.g. if it is an activity, until it is
89      * finished or process is killed).
90      *
91      * @param modelClass The class of the ViewModel to create an instance of it if it is not
92      *                   present.
93      * @param <T>        The type parameter for the ViewModel.
94      * @return A ViewModel that is an instance of the given type {@code T}.
95      */
96     @NonNull
97     @MainThread
get(@onNull Class<T> modelClass)98     public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
99         String canonicalName = modelClass.getCanonicalName();
100         if (canonicalName == null) {
101             throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
102         }
103         return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
104     }
105 
106     /**
107      * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
108      * an activity), associated with this {@code ViewModelProvider}.
109      * <p>
110      * The created ViewModel is associated with the given scope and will be retained
111      * as long as the scope is alive (e.g. if it is an activity, until it is
112      * finished or process is killed).
113      *
114      * @param key        The key to use to identify the ViewModel.
115      * @param modelClass The class of the ViewModel to create an instance of it if it is not
116      *                   present.
117      * @param <T>        The type parameter for the ViewModel.
118      * @return A ViewModel that is an instance of the given type {@code T}.
119      */
120     @NonNull
121     @MainThread
get(@onNull String key, @NonNull Class<T> modelClass)122     public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
123         ViewModel viewModel = mViewModelStore.get(key);
124 
125         if (modelClass.isInstance(viewModel)) {
126             //noinspection unchecked
127             return (T) viewModel;
128         } else {
129             //noinspection StatementWithEmptyBody
130             if (viewModel != null) {
131                 // TODO: log a warning.
132             }
133         }
134 
135         viewModel = mFactory.create(modelClass);
136         mViewModelStore.put(key, viewModel);
137         //noinspection unchecked
138         return (T) viewModel;
139     }
140 
141     /**
142      * Simple factory, which calls empty constructor on the give class.
143      */
144     public static class NewInstanceFactory implements Factory {
145 
146         @SuppressWarnings("ClassNewInstance")
147         @NonNull
148         @Override
create(@onNull Class<T> modelClass)149         public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
150             //noinspection TryWithIdenticalCatches
151             try {
152                 return modelClass.newInstance();
153             } catch (InstantiationException e) {
154                 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
155             } catch (IllegalAccessException e) {
156                 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
157             }
158         }
159     }
160 
161     /**
162      * {@link Factory} which may create {@link AndroidViewModel} and
163      * {@link ViewModel}, which have an empty constructor.
164      */
165     public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
166 
167         private static AndroidViewModelFactory sInstance;
168 
169         /**
170          * Retrieve a singleton instance of AndroidViewModelFactory.
171          *
172          * @param application an application to pass in {@link AndroidViewModel}
173          * @return A valid {@link AndroidViewModelFactory}
174          */
175         @NonNull
getInstance(@onNull Application application)176         public static AndroidViewModelFactory getInstance(@NonNull Application application) {
177             if (sInstance == null) {
178                 sInstance = new AndroidViewModelFactory(application);
179             }
180             return sInstance;
181         }
182 
183         private Application mApplication;
184 
185         /**
186          * Creates a {@code AndroidViewModelFactory}
187          *
188          * @param application an application to pass in {@link AndroidViewModel}
189          */
AndroidViewModelFactory(@onNull Application application)190         public AndroidViewModelFactory(@NonNull Application application) {
191             mApplication = application;
192         }
193 
194         @NonNull
195         @Override
create(@onNull Class<T> modelClass)196         public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
197             if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
198                 //noinspection TryWithIdenticalCatches
199                 try {
200                     return modelClass.getConstructor(Application.class).newInstance(mApplication);
201                 } catch (NoSuchMethodException e) {
202                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);
203                 } catch (IllegalAccessException e) {
204                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);
205                 } catch (InstantiationException e) {
206                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);
207                 } catch (InvocationTargetException e) {
208                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);
209                 }
210             }
211             return super.create(modelClass);
212         }
213     }
214 }
215