Как настроить инъекцию зависимости кинжала с нуля в проекте Android?
Как использовать кинжал? Как настроить Dagger для работы в моем проекте Android?
Я хотел бы использовать кинжал в моем проекте Android, но я нахожу его запутанным.
EDIT: Dagger2 также выходит с 2015 04 15, и это еще более запутанно!
[этот вопрос является "заглушкой", на которую я добавляю свой ответ, поскольку я узнал больше о Dagger1 и узнал больше о Dagger2. Этот вопрос скорее руководство а не "вопрос."]
2 ответов:
руководство для Кинжал 2.x(Пересмотренное Издание 6):
шаги являются следующие:
1.) добавить
Daggerнаbuild.gradleфайлы:
- верхний уровень построить.gradle:
.
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //added apt for source code generation } } allprojects { repositories { jcenter() } }
- уровень приложение построить.gradle:
.
apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' //needed for source code generation android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "your.app.id" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { apt 'com.google.dagger:dagger-compiler:2.7' //needed for source code generation compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.google.dagger:dagger:2.7' //dagger itself provided 'org.glassfish:javax.annotation:10.0-b28' //needed to resolve compilation errors, thanks to tutplus.org for finding the dependency }2.) создать свой
AppContextModuleкласс, который предоставляет зависимости.@Module //a module could also include other modules public class AppContextModule { private final CustomApplication application; public AppContextModule(CustomApplication application) { this.application = application; } @Provides public CustomApplication application() { return this.application; } @Provides public Context applicationContext() { return this.application; } @Provides public LocationManager locationService(Context context) { return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } }3.) создать
AppContextComponentкласс, который предоставляет интерфейс для получения классов, которые можно вводить.public interface AppContextComponent { CustomApplication application(); //provision method Context applicationContext(); //provision method LocationManager locationManager(); //provision method }3.1.) вот как бы вы создали модуль с реализацией:
@Module //this is to show that you can include modules to one another public class AnotherModule { @Provides @Singleton public AnotherClass anotherClass() { return new AnotherClassImpl(); } } @Module(includes=AnotherModule.class) //this is to show that you can include modules to one another public class OtherModule { @Provides @Singleton public OtherClass otherClass(AnotherClass anotherClass) { return new OtherClassImpl(anotherClass); } } public interface AnotherComponent { AnotherClass anotherClass(); } public interface OtherComponent extends AnotherComponent { OtherClass otherClass(); } @Component(modules={OtherModule.class}) @Singleton public interface ApplicationComponent extends OtherComponent { void inject(MainActivity mainActivity); }внимание:: вы должны предоставить
@Scopeаннотация (как@Singletonили@ActivityScope) на модуля@Providesаннотированный метод для получения области видимости поставщик внутри вашего сгенерированного компонента, в противном случае он будет unscoped, и вы получите новый экземпляр каждый раз, когда вы вводите.3.2.) создайте компонент области приложения, который указывает, что вы можете ввести (это то же самое, что и
injects={MainActivity.class}в Кинжале 1.x):@Singleton @Component(module={AppContextModule.class}) //this is where you would add additional modules, and a dependency if you want to subscope public interface ApplicationComponent extends AppContextComponent { //extend to have the provision methods void inject(MainActivity mainActivity); }3.3.) для зависимостей, которые вы можете создайте с помощью конструктора самостоятельно и не хотите переопределять с помощью
@Module(например, вы используете построить ароматы вместо того, чтобы изменить тип реализации), вы можете использовать@Injectаннотированный конструктор.public class Something { OtherThing otherThing; @Inject public Something(OtherThing otherThing) { this.otherThing = otherThing; } }также, если вы используете
@Injectконструктор, вы можете использовать инъекцию поля без явного вызоваcomponent.inject(this):public class Something { @Inject OtherThing otherThing; @Inject public Something() { } }эти
@Injectклассы конструктора автоматически добавляются в компонент той же области без необходимости явно указывать их в модуле.A
@Singletonscoped@Injectконструктор класса будет будьте замечены в@Singletonкомпоненты с областью действия.@Singleton // scoping public class Something { OtherThing otherThing; @Inject public Something(OtherThing otherThing) { this.otherThing = otherThing; } }3.4.) после того, как вы определили конкретную реализацию данного интерфейса, вот так:
public interface Something { void doSomething(); } @Singleton public class SomethingImpl { @Inject AnotherThing anotherThing; @Inject public SomethingImpl() { } }вам нужно будет "привязать" конкретную реализацию к интерфейсу с помощью
@Module.@Module public class SomethingModule { @Provides Something something(SomethingImpl something) { return something; } }короткая рука для этого, так как Кинжал 2.4 является следующим:
@Module public abstract class SomethingModule { @Binds abstract Something something(SomethingImpl something); }4.) создать
Injectorкласс для обработки компонента уровня приложения (it заменяет монолитныйObjectGraph)(Примечание:
Rebuild ProjectсоздатьDaggerApplicationComponentкласс builder с использованием APT)public enum Injector { INSTANCE; ApplicationComponent applicationComponent; private Injector(){ } static void initialize(CustomApplication customApplication) { ApplicationComponent applicationComponent = DaggerApplicationComponent.builder() .appContextModule(new AppContextModule(customApplication)) .build(); INSTANCE.applicationComponent = applicationComponent; } public static ApplicationComponent get() { return INSTANCE.applicationComponent; } }5.) создать свой
CustomApplicationклассpublic class CustomApplication extends Application { @Override public void onCreate() { super.onCreate(); Injector.initialize(this); } }6.) добавить
CustomApplicationнаAndroidManifest.xml.<application android:name=".CustomApplication" ...7.) введите ваши классы в
MainActivitypublic class MainActivity extends AppCompatActivity { @Inject CustomApplication customApplication; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Injector.get().inject(this); //customApplication is injected from component } }8.) наслаждайтесь!
+1.) вы можете указать
Scopeдля ваших компонентов, с помощью которых вы можете создать компоненты с областью действия. Subscopes позволяют обеспечить зависимости, что вам нужно, только лишь для данной подобласти, а не во всем приложении. Как правило, каждое действие получает свой собственный модуль с этой установкой. Обратите внимание, что поставщик с областью действия существует на компонент, что означает, что для сохранения экземпляра для этого действия сам компонент должен пережить изменение конфигурации. Например, он может выжить черезonRetainCustomNonConfigurationInstance(), или объема раствора.для получения дополнительной информации о подписке, проверьте руководство от Google. Также, пожалуйста, смотрите этот сайт о методах обеспечения и раздел зависимостей компонентов) и здесь.
чтобы создать пользовательскую область, необходимо указать аннотацию квалификатора области:
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface YourCustomScope { }создать подобласти, нужно указать область ваш компонент, и укажите
ApplicationComponentкак его зависимость. Очевидно, нужно указать подобласти о методах поставщика модуль.@YourCustomScope @Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class}) public interface YourCustomScopedComponent extends ApplicationComponent { CustomScopeClass customScopeClass(); void inject(YourScopedClass scopedClass); }и
@Module public class CustomScopeModule { @Provides @YourCustomScope public CustomScopeClass customScopeClass() { return new CustomScopeClassImpl(); } }обратите внимание:один компонент с областью действия можно указать как зависимость. Думать о нем именно как множественное наследование не поддерживается в Java.
+2.) о
@Subcomponent: по сути, ограниченном@Subcomponentможет заменить компонентную зависимость; но вместо использования компоновщика, предоставленного обработчиком аннотаций, вам нужно будет использовать метод фабрики компонентов.значит так:
@Singleton @Component public interface ApplicationComponent { } @YourCustomScope @Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class}) public interface YourCustomScopedComponent extends ApplicationComponent { CustomScopeClass customScopeClass(); void inject(YourScopedClass scopedClass); }становится этот:
@Singleton @Component public interface ApplicationComponent { YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule); } @Subcomponent(modules={CustomScopeModule.class}) @YourCustomScope public interface YourCustomScopedComponent { CustomScopeClass customScopeClass(); }и так:
DaggerYourCustomScopedComponent.builder() .applicationComponent(Injector.get()) .customScopeModule(new CustomScopeModule()) .build();становится этот:
Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());+3.): пожалуйста, проверьте другие вопросы переполнения стека относительно Dagger2, а также, они предоставляют много информации. Например, моя текущая структура Dagger2 указана в ответ.
спасибо
Спасибо за гидов в Github,TutsPlus,Джо Стил,Froger MCS и Google.
для этого пошаговое руководство по миграции я нашел после написания этого поста.
и объем объяснением Кирилл.
еще больше информация в официальная документация.
руководство для Кинжал 1.x:
шаги являются следующие:
1.) добавить
Daggerдо в зависимостиdependencies { compile fileTree(dir: 'libs', include: ['*.jar']) ... compile 'com.squareup.dagger:dagger:1.2.2' provided 'com.squareup.dagger:dagger-compiler:1.2.2'и
packaging-optionчтобы предотвратить ошибку оduplicate APKs.android { ... packagingOptions { // Exclude file to avoid // Error: Duplicate files during packaging of APK exclude 'META-INF/services/javax.annotation.processing.Processor' } }2.) создать
Injectorкласс для обработкиObjectGraph.public enum Injector { INSTANCE; private ObjectGraph objectGraph = null; public void init(final Object rootModule) { if(objectGraph == null) { objectGraph = ObjectGraph.create(rootModule); } else { objectGraph = objectGraph.plus(rootModule); } // Inject statics objectGraph.injectStatics(); } public void init(final Object rootModule, final Object target) { init(rootModule); inject(target); } public void inject(final Object target) { objectGraph.inject(target); } public <T> T resolve(Class<T> type) { return objectGraph.get(type); } }3.) Создать
RootModuleчтобы связать ваши будущие модули вместе. Обратите внимание, что вы должны включитьinjectsуказать каждый класс, в котором вы будете использовать@Injectаннотации, потому что в противном случае Кинжал бросаетRuntimeException.@Module( includes = { UtilsModule.class, NetworkingModule.class }, injects = { MainActivity.class } ) public class RootModule { }4.) Если у вас есть другие субмодули в ваших модулях, указанных в вашем корне, создайте модули для них:
@Module( includes = { SerializerModule.class, CertUtilModule.class } ) public class UtilsModule { }5.) создайте конечные модули, которые получают зависимости в качестве параметров конструктора. В моем случае не было круговой зависимости, поэтому я не знаю, Может ли Кинжал решить это, но я считаю это маловероятным. Параметры конструктора также должны обеспечьте в модуле кинжалом, если вы укажете
complete = falseтогда это может быть и в других модулях тоже.@Module(complete = false, library = true) public class NetworkingModule { @Provides public ClientAuthAuthenticator providesClientAuthAuthenticator() { return new ClientAuthAuthenticator(); } @Provides public ClientCertWebRequestor providesClientCertWebRequestor(ClientAuthAuthenticator clientAuthAuthenticator) { return new ClientCertWebRequestor(clientAuthAuthenticator); } @Provides public ServerCommunicator providesServerCommunicator(ClientCertWebRequestor clientCertWebRequestor) { return new ServerCommunicator(clientCertWebRequestor); } }6.) Продлить
Applicationи инициализацииInjector.@Override public void onCreate() { super.onCreate(); Injector.INSTANCE.init(new RootModule()); }7.) В вашем
MainActivityвызовите инжектор вonCreate()метод.@Override protected void onCreate(Bundle savedInstanceState) { Injector.INSTANCE.inject(this); super.onCreate(savedInstanceState); ...8.) Использовать
@Injectв своемMainActivity.public class MainActivity extends ActionBarActivity { @Inject public ServerCommunicator serverCommunicator; ...если вы получаете ошибку
no injectable constructor found, убедитесь, что вы не забыли@ProvidesПримечание.
Comments