Как настроить инъекцию зависимости кинжала с нуля в проекте Android?



Как использовать кинжал? Как настроить Dagger для работы в моем проекте Android?



Я хотел бы использовать кинжал в моем проекте Android, но я нахожу его запутанным.



EDIT: Dagger2 также выходит с 2015 04 15, и это еще более запутанно!



[этот вопрос является "заглушкой", на которую я добавляю свой ответ, поскольку я узнал больше о Dagger1 и узнал больше о Dagger2. Этот вопрос скорее руководство а не "вопрос."]

629   2  

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 @Singleton scoped @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.) введите ваши классы в MainActivity

public 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

    Ничего не найдено.