1 /* 2 * Copyright 2024 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 @file:JvmName("ViewModelProvider") 17 18 package androidx.lifecycle 19 20 import android.app.Application 21 import androidx.annotation.MainThread 22 import androidx.annotation.RestrictTo 23 import androidx.lifecycle.viewmodel.CreationExtras 24 import androidx.lifecycle.viewmodel.CreationExtras.Key 25 import androidx.lifecycle.viewmodel.InitializerViewModelFactory 26 import androidx.lifecycle.viewmodel.ViewModelInitializer 27 import androidx.lifecycle.viewmodel.ViewModelProviderImpl 28 import androidx.lifecycle.viewmodel.internal.JvmViewModelProviders 29 import androidx.lifecycle.viewmodel.internal.ViewModelProviders 30 import java.lang.reflect.InvocationTargetException 31 import kotlin.reflect.KClass 32 33 public actual open class ViewModelProvider 34 private constructor( 35 private val impl: ViewModelProviderImpl, 36 ) { 37 38 /** 39 * Creates a [ViewModelProvider]. This provider generates [ViewModel] instances using the 40 * specified [Factory] and stores them within the [ViewModelStore] of the provided 41 * [ViewModelStoreOwner]. 42 * 43 * @param store `ViewModelStore` where ViewModels will be stored. 44 * @param factory The [Factory] responsible for creating new [ViewModel] instances. 45 * @param defaultCreationExtras Additional data to be passed to the [Factory] during [ViewModel] 46 * creation. 47 */ 48 @JvmOverloads 49 public constructor( 50 store: ViewModelStore, 51 factory: Factory, 52 defaultCreationExtras: CreationExtras = CreationExtras.Empty, 53 ) : this(ViewModelProviderImpl(store, factory, defaultCreationExtras)) 54 55 /** 56 * Creates [ViewModelProvider]. This will create [ViewModel] instances and retain them in the 57 * [ViewModelStore] of the given [ViewModelStoreOwner]. 58 * 59 * This method will use the 60 * [default factory][HasDefaultViewModelProviderFactory.defaultViewModelProviderFactory] if the 61 * owner implements [HasDefaultViewModelProviderFactory]. Otherwise, a [NewInstanceFactory] will 62 * be used. 63 */ 64 public constructor( 65 owner: ViewModelStoreOwner, 66 ) : this( 67 store = owner.viewModelStore, 68 factory = ViewModelProviders.getDefaultFactory(owner), 69 defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner) 70 ) 71 72 /** 73 * Creates a [ViewModelProvider]. This provider generates [ViewModel] instances using the 74 * specified [Factory] and stores them within the [ViewModelStore] of the provided 75 * [ViewModelStoreOwner]. 76 * 77 * @param owner The [ViewModelStoreOwner] that will manage the lifecycle of the created 78 * [ViewModel] instances. 79 * @param factory The [Factory] responsible for creating new [ViewModel] instances. 80 */ 81 public constructor( 82 owner: ViewModelStoreOwner, 83 factory: Factory, 84 ) : this( 85 store = owner.viewModelStore, 86 factory = factory, 87 defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner) 88 ) 89 90 @MainThread getnull91 public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T = 92 impl.getViewModel(modelClass) 93 94 /** 95 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or an 96 * activity), associated with this `ViewModelProvider`. 97 * 98 * The created ViewModel is associated with the given scope and will be retained as long as the 99 * scope is alive (e.g. if it is an activity, until it is finished or process is killed). 100 * 101 * @param modelClass The class of the ViewModel to create an instance of it if it is not 102 * present. 103 * @return A ViewModel that is an instance of the given type `T`. 104 * @throws IllegalArgumentException if the given [modelClass] is local or anonymous class. 105 */ 106 public open operator fun <T : ViewModel> get(modelClass: Class<T>): T = get(modelClass.kotlin) 107 108 @MainThread 109 public actual operator fun <T : ViewModel> get(key: String, modelClass: KClass<T>): T = 110 impl.getViewModel(modelClass, key) 111 112 /** 113 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or an 114 * activity), associated with this `ViewModelProvider`. 115 * 116 * The created ViewModel is associated with the given scope and will be retained as long as the 117 * scope is alive (e.g. if it is an activity, until it is finished or process is killed). 118 * 119 * @param key The key to use to identify the ViewModel. 120 * @param modelClass The class of the ViewModel to create an instance of it if it is not 121 * present. 122 * @return A ViewModel that is an instance of the given type `T`. 123 */ 124 public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T = 125 impl.getViewModel(modelClass.kotlin, key) 126 127 public actual interface Factory { 128 129 /** 130 * Creates a new instance of the given `Class`. 131 * 132 * Default implementation throws [UnsupportedOperationException]. ˆ 133 * 134 * @param modelClass a `Class` whose instance is requested 135 * @return a newly created ViewModel 136 */ 137 public fun <T : ViewModel> create(modelClass: Class<T>): T = 138 ViewModelProviders.unsupportedCreateViewModel() 139 140 /** 141 * Creates a new instance of the given `Class`. 142 * 143 * @param modelClass a `Class` whose instance is requested 144 * @param extras an additional information for this creation request 145 * @return a newly created ViewModel 146 */ 147 public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T = 148 create(modelClass) 149 150 public actual fun <T : ViewModel> create( 151 modelClass: KClass<T>, 152 extras: CreationExtras, 153 ): T = create(modelClass.java, extras) 154 155 public companion object { 156 /** 157 * Creates an [InitializerViewModelFactory] using the given initializers. 158 * 159 * @param initializers the class initializer pairs used for the factory to create simple 160 * view models 161 * @see [InitializerViewModelFactory] 162 */ 163 @JvmStatic 164 public fun from(vararg initializers: ViewModelInitializer<*>): Factory = 165 ViewModelProviders.createInitializerFactory(*initializers) 166 } 167 } 168 169 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 170 public actual open class OnRequeryFactory { onRequerynull171 public actual open fun onRequery(viewModel: ViewModel) {} 172 } 173 174 /** Simple factory, which calls empty constructor on the give class. */ 175 public open class NewInstanceFactory 176 /** 177 * Construct a new [NewInstanceFactory] instance. 178 * 179 * Use [NewInstanceFactory.instance] to get a default instance of [NewInstanceFactory]. 180 */ 181 @Suppress("SingletonConstructor") 182 constructor() : Factory { 183 createnull184 public override fun <T : ViewModel> create(modelClass: Class<T>): T = 185 JvmViewModelProviders.createViewModel(modelClass) 186 187 public override fun <T : ViewModel> create( 188 modelClass: Class<T>, 189 extras: CreationExtras, 190 ): T = create(modelClass) 191 192 public override fun <T : ViewModel> create( 193 modelClass: KClass<T>, 194 extras: CreationExtras, 195 ): T = create(modelClass.java, extras) 196 197 public companion object { 198 private var _instance: NewInstanceFactory? = null 199 200 /** 201 * Retrieve a singleton instance of NewInstanceFactory. 202 * 203 * @return A valid [NewInstanceFactory] 204 */ 205 @JvmStatic 206 public val instance: NewInstanceFactory 207 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 208 get() { 209 if (_instance == null) { 210 _instance = NewInstanceFactory() 211 } 212 return _instance!! 213 } 214 215 /** 216 * A [CreationExtras.Key] used to retrieve the key associated with a requested 217 * [ViewModel]. 218 * 219 * The [ViewModelProvider] automatically includes the key in the [CreationExtras] passed 220 * to [ViewModelProvider.Factory]. This applies to keys generated by either of these 221 * usage patterns: 222 * - `ViewModelProvider.get(key, MyViewModel::class)`: provided `key` is used. 223 * - `ViewModelProvider.get(MyViewModel::class)`: generates a `key` from given `class`. 224 * 225 * @see ViewModelProvider.VIEW_MODEL_KEY 226 */ 227 @JvmField public val VIEW_MODEL_KEY: Key<String> = ViewModelProvider.VIEW_MODEL_KEY 228 } 229 } 230 231 /** 232 * [Factory] which may create [AndroidViewModel] and [ViewModel], which have an empty 233 * constructor. 234 * 235 * @param application an application to pass in [AndroidViewModel] 236 */ 237 public open class AndroidViewModelFactory 238 private constructor( 239 private val application: Application?, 240 // parameter to avoid clash between constructors with nullable and non-nullable 241 // Application 242 @Suppress("UNUSED_PARAMETER") unused: Int, 243 ) : NewInstanceFactory() { 244 245 /** 246 * Constructs this factory. When a factory is constructed this way, a component for which 247 * [ViewModel] is created must provide an [Application] by [APPLICATION_KEY] in 248 * [CreationExtras], otherwise [IllegalArgumentException] will be thrown from [create] 249 * method. 250 */ 251 @Suppress("SingletonConstructor") 252 public constructor() : this(application = null, unused = 0) 253 254 /** 255 * Constructs this factory. 256 * 257 * @param application an application to pass in [AndroidViewModel] 258 */ 259 @Suppress("SingletonConstructor") 260 public constructor(application: Application) : this(application, unused = 0) 261 262 @Suppress("DocumentExceptions") createnull263 override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { 264 return if (application != null) { 265 create(modelClass) 266 } else { 267 val application = extras[APPLICATION_KEY] 268 if (application != null) { 269 create(modelClass, application) 270 } else { 271 // For AndroidViewModels, CreationExtras must have an application set 272 if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) { 273 throw IllegalArgumentException( 274 "CreationExtras must have an application by `APPLICATION_KEY`" 275 ) 276 } 277 super.create(modelClass) 278 } 279 } 280 } 281 282 @Suppress("DocumentExceptions") createnull283 override fun <T : ViewModel> create(modelClass: Class<T>): T { 284 return if (application == null) { 285 throw UnsupportedOperationException( 286 "AndroidViewModelFactory constructed " + 287 "with empty constructor works only with " + 288 "create(modelClass: Class<T>, extras: CreationExtras)." 289 ) 290 } else { 291 create(modelClass, application) 292 } 293 } 294 295 @Suppress("DocumentExceptions") createnull296 private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T { 297 return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) { 298 try { 299 modelClass.getConstructor(Application::class.java).newInstance(app) 300 } catch (e: NoSuchMethodException) { 301 throw RuntimeException("Cannot create an instance of $modelClass", e) 302 } catch (e: IllegalAccessException) { 303 throw RuntimeException("Cannot create an instance of $modelClass", e) 304 } catch (e: InstantiationException) { 305 throw RuntimeException("Cannot create an instance of $modelClass", e) 306 } catch (e: InvocationTargetException) { 307 throw RuntimeException("Cannot create an instance of $modelClass", e) 308 } 309 } else super.create(modelClass) 310 } 311 312 public companion object { 313 private var _instance: AndroidViewModelFactory? = null 314 315 /** 316 * Retrieve a singleton instance of AndroidViewModelFactory. 317 * 318 * @param application an application to pass in [AndroidViewModel] 319 * @return A valid [AndroidViewModelFactory] 320 */ 321 @JvmStatic getInstancenull322 public fun getInstance(application: Application): AndroidViewModelFactory { 323 if (_instance == null) { 324 _instance = AndroidViewModelFactory(application) 325 } 326 return _instance!! 327 } 328 329 /** 330 * A [CreationExtras.Key] to query an application in which ViewModel is being created. 331 */ 332 @JvmField public val APPLICATION_KEY: Key<Application> = CreationExtras.Companion.Key() 333 } 334 } 335 336 public actual companion object { 337 @JvmStatic 338 @Suppress("MissingJvmstatic") createnull339 public actual fun create( 340 owner: ViewModelStoreOwner, 341 factory: Factory, 342 extras: CreationExtras, 343 ): ViewModelProvider = ViewModelProvider(owner.viewModelStore, factory, extras) 344 345 @JvmStatic 346 @Suppress("MissingJvmstatic") 347 public actual fun create( 348 store: ViewModelStore, 349 factory: Factory, 350 extras: CreationExtras 351 ): ViewModelProvider = ViewModelProvider(store, factory, extras) 352 353 @JvmField public actual val VIEW_MODEL_KEY: Key<String> = CreationExtras.Companion.Key() 354 } 355 } 356