Кама 2
Накрая пристигна третата част от поредица статии за Dagger 2!
Преди да прочетете допълнително, силно препоръчвам да се запознаете с първата и втората част.
Като цяло библиотеката съществува от прилично време, но документацията все още е изключително отвратителна. За разработчик, който тепърва започва познанството си с Кинжал, дори бих посъветвал да не разглежда официалната документация в началото, за да не бъде разочарован от този труден и несправедлив свят.
Има, разбира се, моменти, които са горе-долу планирани. Но тук всякакви нови функции са описани по такъв начин, че чрез проби и грешки, влизайки в генерирания код, аз самият разбера как работи всичко. За щастие добрите хора пишат добри статии, но дори понякога не дават ясен и ясен отговор веднага.
Така че, спрете да се заяждате и продължете към нови знания!
Квалификационна анотация
Често се случва, че трябва да предоставим няколко обекта от един и същи тип. Например искаме да имаме два Executor в системата: единият с резба, а другият с CachedThreadPool. В този случай на помощ идва „анотация на квалификатора“. Това е персонализирана анотация, в която има анотация @Qualifier. Звучи малко като масло, но примерът е много по-опростен.
Като цяло Dagger2 ни предоставя една готова „анотация на квалификатора“, което може би е напълно достатъчно в ежедневието:
Сега нека видим как изглежда всичко в битка:
В резултат на това имаме два различни екземпляра (единичен изпълнител, мултиизпълнител) от един и същи клас (изпълнител). Какво ни трябва!
Имайте предвид, че обекти от един и същи клас с @Named анотация могат да бъдат предоставени както от напълно различни и независими компоненти, така и от зависими един от друг.
Мързелива инициализация
Един от най-често срещаните ни проблеми в развитието е дългият старт на приложението. Обикновено причината е една - зареждаме твърде много и инициализираме при стартиране. Освен това Dagger2 изгражда графика на зависимост върху основната нишка. И често не всички обекти, проектирани от Dagger, са необходими веднага. Следователно библиотеката ни дава възможност да отложим инициализацията на обекта до първото извикване, използвайки интерфейсите Provider <> и Lazy <> .
Нека веднага насочим вниманието си към пример:
Нека започнем с доставчика singleExecutorProvider. Преди първият извикване на singleExecutorProvider.get () Кинжал не инициализира съответния изпълнител. Но със всяко следващо обаждане singleExecutorProvider.get () ще бъде създаден нов екземпляр. Така че singleExecutor и singleExecutor2 са два различни обекта. Това поведение по същество е идентично с поведението необхванат обект.
Сега Lazy multiExecutorLazy и Lazy multiExecutorLazyCopy. Dagger2 инициализира съответния Изпълнител само когато първо извикване на multiExecutorLazy.get () и multiExecutorLazyCopy.get (). След това Dagger кешира инициализираните стойности за всеки Мързелив <> и при второто извикване на multiExecutorLazy.get () и multiExecutorLazyCopy.get () произвежда кеширани обекти.
По този начин multiExecutor и multiExecutor2 се отнасят за един обект, а multiExecutor3 към втори обект.
Но ако добавим анотацията @Singleton към метода provideMultiThreadExecutor () в AppModule, тогава обектът ще бъде кеширан за цялото дърво на зависимостите, а multiExecutor, multiExecutor2, multiExecutor3 ще се позове на един предмет.
Бъди внимателен.
Асинхронно натоварване
Разгледайте @Singleton и мързеливия интерфейс в AppModule. Lazy просто гарантира, че обектът с тежка категория ще бъде инициализиран, когато поискаме и след това кеширан.
Но какво, ако искаме всеки път да получаваме нов екземпляр на този „тежък“ обект? Тогава си струва да промените малко AppModule:
За метода provideHeavyExternalLibrary () премахнахме обхват, и в provideHeavyExternalLibraryObservable (окончателен доставчик heavyExternalLibraryLazy) използва Provider вместо Lazy. По този начин heavyExternalLibrary и heavyExternalLibraryCopy в MainActivity са различни обекти.
Или дори можете да преместите целия процес на инициализиране на дървото на зависимостите на заден план. Как питате? Много лесно. Първо, нека да разгледаме как беше:
Сега нека да разгледаме актуализирания метод на void setupActivityComponent () (с моите редакции в RxJava):
Нови интересни функции
Кинжал има нови интересни функции, които обещават да улеснят живота ни. Но да разберем как всичко работи и какво ни дава всичко не беше лесна задача.
Е, да започнем!
@ Многократна употреба
Интересна анотация. Позволява ви да спестявате памет, но в същото време всъщност не се ограничава до никакъв обхват, което го прави много удобен за повторно използване на зависимости във всякакви компоненти. Тоест, това е кръстоска между обхвата и отмяната .
На доковете те пишат много важна точка, която по някакъв начин не удря окото от първия път: "За всеки компонент, който използва зависимостта @Reusable, тази зависимост се кешира отделно". И моето допълнение: "За разлика от анотациите на обхвата, когато обектът се кешира при създаването и неговият екземпляр се използва от дъщерни и зависими компоненти"
А сега само пример за разбиране на всичко:
Нашият основен компонент.
AppComponent има два подкомпонента. Забелязали ли сте тази конструкция - FirstComponent.Builder? Ще поговорим за нея малко по-късно.
Сега нека разгледаме UtilsModule .
NumberUtils с @Reusable анотация и оставете StringUtils без обхват .
След това имаме два подкомпонента .
Както виждаме, FirstComponent инжектира само в MainActivity, а SecondComponent - във SecondActivity и ThirdActivity .
Да видим кода.
Накратко за навигацията. От MainActivity стигаме до SecondActivity и след това до ThirdActivity. Сега въпрос. Когато вече сме на третия екран, колко обекта NumberUtils и StringUtils ще бъдат създадени?
Тъй като StringUtils не е обхванат, ще бъдат създадени три екземпляра, т.е. нов обект се създава с всяка инжекция. Ние го знаем.
Но ще има два обекта NumberUtils - единият за FirstComponent, а другият за SecondComponent. И тук отново ще дам основната идея за @Reusable от документацията: "За всеки компонент, който използва зависимостта @Reusable, тази зависимост се кешира отделно! ", За разлика от анотацията на обхвата, където обектът се кешира при създаване и неговият екземпляр се използва от дъщерни и зависими компоненти
Но самите служители на Google предупреждават, че ако имате нужда от уникален обект, който също може да бъде променлив, използвайте само пояснения с обхват.
Също така ще дам линк към въпроса за сравнението на @Singleton и @Reusable със SO.
@ Subcomponent.Builder
Функция, която прави кода по-хубав.
Преди това, за да създадем @Subcomponent, трябваше да напишем нещо подобно:
Това, което не ми хареса в този подход, беше, че родителският компонент беше зареден с ненужни знания за модулите, които използват дъщерните подкомпоненти. Е, плюс предаването на голям брой аргументи не изглежда много хубаво, защото има модел за това Строител.
Сега е по-красиво:
Създаването на FirstComponent сега изглежда така:
Сега имаме възможността да правим така:
AuthManager идва от друг модул като Singleton .
Сега нека да разгледаме набързо генерирания код AuthModule_CurrentUserFactory (в студиото просто поставете курсора върху currentUser и натиснете Ctrl + B):
И ако добавите статичен към currentUser:
Имайте предвид, че в статичната опция няма AuthModel. Така че статичният метод се дърпа от компонента директно, заобикаляйки модула. И ако в модула има само един статичен метод, тогава екземплярът на модула дори не е създаден.
Спестявания и минус ненужни разговори. Всъщност имаме подобрение в производителността.
Те също така пишат, че извикването на статичен метод е с 15–20% по-бързо от извикването на подобен нестатичен метод. Ако греша, iamironz ще ме поправи. Той знае със сигурност и ако се наложи, ще мери.
@Binds + Конструктор за инжектиране
Мега удобен пакет, който значително намалява стандартния код. В зората на изучаването на Кинжал не разбрах защо са необходими инжекции с конструктор. Какво идва от къде. И тогава имаше @Binds. Но всъщност е доста просто. Благодаря за помощта Владимир Тагаков и тази статия.
Нека разгледаме типична ситуация. Има интерфейс на Presenter и неговото изпълнение:
Ние като бели хора ще предоставим цялото нещо в модула и ще инжектираме презентационния интерфейс в дейността:
Да кажем, че нашият FirstPresenter се нуждае от помощни класове, на които той делегира част от работата. За да направите това, трябва да създадете още два метода в модула, които ще предоставят нови класове, след това да промените конструктора FirstPresenter и следователно да актуализирате съответния метод в модула.
Модулът ще бъде по следния начин:
И така всеки път, ако трябва да добавите клас и да го „споделите“ с други. Модулът се замърсява много бързо. И някак твърде много код, не мислите ли? Но има решение, което значително намалява кода.
Първо, ако трябва да създадем зависимост и да дадем готов клас, а не интерфейс (HelperClass1 и HelperClass2), можем да прибегнем до инжектиране на конструктор.
Ще изглежда така:
Обърнете внимание, че анотацията @FirstScope е добавена към класовете, така че Dagger разбира към кое дърво на зависимости да присвои тези класове.
Сега можем безопасно да премахнем HelperClass1 и HelperClass2 от модула:
Как можете допълнително да намалите кода в модула? Нека приложим @Binds тук:
И в FirstPresenter, нека инжектираме конструктора:
Какви са иновациите тук?
HelperModule стана абстрактен за нас, точно като метода provideFirstPresenter. Анотацията @Provide е премахната от provideFirstPresenter, но е добавена @Binds. И в аргументите предаваме ненужни зависимости, но конкретно изпълнение!
FirstPresenter добави анотация на обхвата - @FirstScope, чрез която Dagger разбира къде да присвои този клас. Също така, анотацията @Inject е добавена към конструктора. .
Много по-чист и лесен за добавяне на нови зависимости!
Това, което още не е казано
Достъпни референции. Ако паметта наистина е проблем. С помощта на подходящи анотации маркираме предметите, които можем да жертваме, когато липсва памет. Ето хак.
В документите (подраздел Разрешими препратки) всичко е ясно описано, колкото и да е странно.
Тестване. Разбира се, Dagger не е необходим за Unit тестване. Но за функционални, интеграционни и потребителски тестове, възможността за заместване на определени модули може да бъде полезна. Artem_zin покрива тази тема много хладно в своята статия и пример. Документацията има подчертан раздел за тестване. Но отново, служителите на Google не могат да опишат правилно как да заменят компонент. Как правилно да създавате фалшиви модули и да ги замествате. За да заместя компонент (отделни модули), използвам метода на Артем. Да, бих искал да бъде възможно да се създаде тестов компонент в отделен клас и тестови модули в отделни класове и да се свърже всичко това красиво в тестовия файл на приложението. Може би някой знае?
@BindsOptionalOf. Работи заедно с Optional от Java 8 или Guava, което затруднява тази функция за нас. Ако се интересувате, можете да намерите описание в края на документацията.
Това е! Изглежда, че успяхме да подчертаем всички моменти. Ако сте пропуснали нещо или не сте го описали достатъчно, пишете! Нека да го оправим. Също така препоръчвам групата Dagger2 в Telegram, където вашите въпроси няма да останат без отговор.
Също така, правилното използване на библиотеката е много свързано с изчистената архитектура. И така, ето вашата архитектурна група. И да, скоро за AndroidDevPodcast се планира издание с тематична кинжал. следете новините!
- My Log Home iLWP тапет или произведения на изкуството
- Влияние на планетите върху здравето и характера - Аюрведа Плюс
- един - -, - -
- Шеги за врата
- 1 Какви научни открития са доказали, че атомът