Aby ułatwić pisanie aplikacji postanowiłem spróbować znaleźć i użyć framework do Dependency Injection.
Po szybkich poszukiwaniach natrafiłem na framework Dagger. Framework ten jest przystosowany do użycia go w aplikacjach mobilnych. Dagger działa inaczej niż większość frameworków DI, które działają na refleksjach. Dagger tworzy klasy, które będzie wstrzykiwał, na etapie kompilacji. Podejście to tworzy pewne ograniczenia, ale jest szybsze i mniej pamięciożerne, co jest zdecydowanie przydatne w przypadku aplikacji mobilnych.
Po tym wyborze przystąpiłem do konfiguracji frameworka, ale naszła mnie pewna myśl… Moja aplikacja będzie dosyć prosta. Czy faktycznie potrzebuję frameworka DI? Po chwili namysłu stwierdziłem, iż samodzielnie stworzę coś na wzór frameworka DI. Oczywiście prostszego i bardziej ubogiego ale mam nadzieję, iż wystarczającego
Podszedłem do tego w następujący sposób:
- Stworzyłem w Javie klasę dziedziczącą po klasie Application, ze statycznym dostępem do contextu.
public class ApplicationContext extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getAppContext() {
return context;
}
}
W Scali nie są dostępne metody statyczne w klasach, a Object Scalowy nie mógłby być zainicjalizowany przez system. W związku z tym byłem zmuszony stworzyć powyższą klasę Javową, aby zyskać dostęp do kontekstu aplikacji.
- Dodałem odniesienie do powyższej klasy w pliku AndroidMnifest.xml w atrybucie android:name dla tagu application. Wygląda to mniej więcej tak: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pl.lantkowiak.sdm"> ... <application ... android:name="pl.lantkowiak.sdm.di.ApplicationContext"> ... </application> </manifest>
- Stworzyłem obiekt, w którym tworzone są instancje, które będą 'wstrzykniętę’ oraz metodę służącą do pobrania tych instancji.
object ApplicationModule {
private val instances = collection.mutable.Map[Class[_], Any]()
{
instances.put(classOf[DocumentDao], documentDAO())
}
def wire[T](clazz: Class[T]): T = {
instances.get(clazz).asInstanceOf[Some[T]].get
}
private def documentDAO(): DocumentDao = {
new DocumentDaoBean(ApplicationContext.getAppContext)
}
}
Obiekt zawiera w sobie mapę, która zawiera mapowanie klasy na jej instancje. Mapa ta jest uzupełniania w bloku inizjalizującym – tutaj będę musiał dodać wszystkie klasy wraz z instancjami, do których będę chciał mieć dostęp w ten sposób.
Metoda wire pobiera instancje z mapy oraz ją zwraca. - Samo 'wstrzyknięcię’ robimy w poniższy sposób:
private lazy val documentDao = wire(classOf[DocumentDao])
Oczywiście powyższe podejscie będzię wstrzykiwać zawsze tą samą instancje, ale na potrzeby mojej aplikacji powinno być to całkowicie wystarczające.