La classe Application a Android¶
La classe Application és la classe base que manté l'estat global de tota l'aplicació Android. Es crea abans que qualsevol Activity, Service o BroadcastReceiver, i es destrueix només quan el procés de l'aplicació finalitza.
Crear una classe hereva d'Application permet executar codi d'inicialització una sola vegada a l'inici de l'aplicació i compartir estat o configuracions entre tots els components.
Documentació oficial: Application | Android Developers
1. Cicle de vida de la classe Application¶
La classe Application té un cicle de vida molt senzill:
onCreate()— Es crida quan es crea l'aplicació, abans que qualsevol altre component. Es el punt d'entrada principal per a inicialitzacions globals.onTerminate()— Només es crida en entorns d'emulació. No es garanteix que s'executi en dispositius reals.onLowMemory()— Es crida quan el sistema té poca memòria.onConfigurationChanged()— Es crida quan la configuració del dispositiu canvia (rotació, idioma, etc.).
Warning
El mètode onCreate() d'Application s'executa al fil principal (Main Thread). Evita fer operacions llargues aquí, ja que retardarà l'inici de l'aplicació.
2. Crear una classe hereva d'Application¶
Pas 1: Crear la classe¶
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Inicialitzacions globals aquí
}
}
Pas 2: Registrar-la al AndroidManifest.xml¶
Cal indicar al sistema quina classe Application utilitzar mitjançant l'atribut android:name a l'etiqueta <application>:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exemple.app">
<application
android:name=".MyApp"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<!-- Activities, Services, etc. -->
</application>
</manifest>
Info
Si no registres la classe al manifest, Android utilitzarà la classe Application per defecte i el teu codi no s'executarà.
3. Casos d'ús principals¶
3.1. Inicialització de llibreries globals¶
Moltes llibreries necessiten inicialitzar-se una sola vegada a l'inici de l'aplicació. Application.onCreate() és el lloc ideal per fer-ho.
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Inicialitzar Firebase
FirebaseApp.initializeApp(this)
// Inicialitzar una llibreria d'analytics
Analytics.init(this, "API_KEY")
}
}
3.2. Estat global compartit entre components¶
Es pot utilitzar la classe Application per emmagatzemar variables globals accessibles des de qualsevol Activity o Fragment.
class MyApp : Application() {
// Instància global de Retrofit
lateinit var retrofit: Retrofit
private set
// Estat global
var isUserLoggedIn: Boolean = false
override fun onCreate() {
super.onCreate()
retrofit = Retrofit.Builder()
.baseUrl("https://api.exemple.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Per accedir-hi des d'una Activity o Fragment:
// Des d'una Activity
val app = application as MyApp
val retrofit = app.retrofit
// Des d'un Fragment
val app = requireActivity().application as MyApp
val isLoggedIn = app.isUserLoggedIn
3.3. Configuració de temes o idioma¶
Es pot aplicar una configuració global de tema o idioma abans que es creï cap Activity:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Aplicar mode fosc segons preferències guardades
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val isDarkMode = prefs.getBoolean("dark_mode", false)
AppCompatDelegate.setDefaultNightMode(
if (isDarkMode) AppCompatDelegate.MODE_NIGHT_YES
else AppCompatDelegate.MODE_NIGHT_NO
)
}
}
3.4. Injecció de dependències amb Hilt¶
Quan s'utilitza Hilt per a la injecció de dependències, cal anotar la classe Application amb @HiltAndroidApp:
@HiltAndroidApp
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Hilt s'inicialitza automàticament
}
}
Aquesta anotació genera el codi necessari perquè Hilt pugui gestionar la injecció de dependències a tota l'aplicació.
3.5. Gestió del cicle de vida de processos¶
Es pot observar quan l'aplicació passa a primer pla o a segon pla utilitzant ProcessLifecycleOwner:
class MyApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForeground() {
// L'aplicació ha passat a primer pla
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackground() {
// L'aplicació ha passat a segon pla
}
}
Info
Cal afegir la dependència androidx.lifecycle:lifecycle-process al fitxer build.gradle per utilitzar ProcessLifecycleOwner.
3.6. Gestió d'errors globals¶
Es pot definir un gestor d'excepcions no capturades per registrar errors fatals abans que l'aplicació es tanqui:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// Registrar l'error (per exemple, enviar-lo a un servei de crashlytics)
Log.e("MyApp", "Error no capturat al fil ${thread.name}", throwable)
// Finalitzar el procés
android.os.Process.killProcess(android.os.Process.myPid())
}
}
}
4. Exemple complet¶
Exemple d'una classe Application que inicialitza Retrofit, gestiona l'estat de sessió i configura el tema:
class MyApp : Application() {
lateinit var retrofit: Retrofit
private set
lateinit var apiService: ApiService
private set
var isUserLoggedIn: Boolean = false
private set
override fun onCreate() {
super.onCreate()
// Configurar tema
configurarTema()
// Inicialitzar Retrofit
inicialitzarRetrofit()
// Comprovar sessió
comprovarSessio()
}
private fun configurarTema() {
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val isDarkMode = prefs.getBoolean("dark_mode", false)
AppCompatDelegate.setDefaultNightMode(
if (isDarkMode) AppCompatDelegate.MODE_NIGHT_YES
else AppCompatDelegate.MODE_NIGHT_NO
)
}
private fun inicialitzarRetrofit() {
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl("https://api.exemple.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(ApiService::class.java)
}
private fun comprovarSessio() {
val prefs = getSharedPreferences("auth", MODE_PRIVATE)
isUserLoggedIn = prefs.contains("token")
}
fun tancarSessio() {
isUserLoggedIn = false
getSharedPreferences("auth", MODE_PRIVATE).edit().clear().apply()
}
}
5. Bones practiques¶
-
No abusar d'Application com a "bossa global": Emmagatzemar massa estat a
Applicationdificulta el testing i crea acoblaments forts entre components. PrefereixViewModelper a estat de la UI i injecció de dependencies per a serveis compartits. -
Preferir injecció de dependencies: Eines com Hilt o Koin gestionen millor les dependencies globals que variables dins d'
Application. -
Mantenir
onCreate()lleuger: Inicialitzacions pesades (com accés a base de dades o xarxa) s'han de fer en segon pla amb coroutines oWorkManager. -
No guardar referencies a Activities o Fragments: L'objecte
Applicationviu durant tota l'execució. Si guarda referencies a components amb cicle de vida curt, es produiran fugues de memoria. -
Utilitzar
AndroidViewModelper accedir a Application des d'un ViewModel: En lloc d'accedir directament a la instancia d'Application, utilitzaAndroidViewModelque repApplicational constructor de manera segura. Consulta la documentació sobre Application ViewModel.
Warning
La classe Application no sobreviu a la mort del procés. Si el sistema operatiu mata el procés per alliberar memoria, tot l'estat emmagatzemat a Application es perdrà. Utilitza SharedPreferences, Room o DataStore per persistir dades importants.