2015-03-28 14 views
27

Uso Dagger2 per DI nella mia applicazione Android. Ho scoperto che devo scrivere il metodo di iniezione per ogni classe che utilizza il campo @Inject. C'è un modo in cui posso semplicemente iniettare la classe genitore in modo che non debba chiamare inietti su ogni sottoclasse? Prendi Attività ad esempio. Ho un BaseActivity da cui si estende ogni attività. C'è un modo in cui posso semplicemente creare un metodo di iniezione nel componente per BaseActivity e chiamare semplicemente iniettare in BaseActivity onCreate, e i campi @inject nelle attività secondarie vengono iniettati automaticamente?Posso solo iniettare una super classe quando uso dagger2 per l'iniezione di dipendenza?

+0

Potresti aggiungere qualche codice di esempio per mostrare cosa intendi? – nhaarman

risposta

21

Al momento non è possibile. Spiegazione da Gregory Calcio:

Ecco come metodi membri iniezione funzionano:

  1. si può fare un metodo di membri di iniezione per ogni tipo che ha @Inject ovunque nella sua gerarchia di classe. In caso contrario, riceverai un errore .
  2. Tutti i membri di @Inject verranno inseriti nella gerarchia di tipi: il tipo di argomento e tutti i supertipi.
  3. Nessun membro sarà @Inject per sottotipi del tipo di argomento.

Questo problema è stato discusso e herehere, follow-up di questi per gli aggiornamenti. Ma è improbabile che cambi presto, perché Dagger 2 è close to release.

+0

Sembra che abbiano preso questa decisione per un motivo. Ma è ancora un peccato che non lo supportino poiché è un piccolo IMHO contro-intuitivo. Comunque, grazie per la risposta! –

+0

@ Chris.Zou Per supportare l'iniezione in sottoclasse, è necessario eseguire la reflection in fase di esecuzione. Il team di Dagger 2 ha deciso fin da subito di voler evitare di fare cose in fase di esecuzione, poiché è più lento e non si scoprono gli errori finché non si esegue l'app. – vaughandroid

35

Ho riscontrato la stessa situazione. Un modo per facilitare un po 'l'iniezione da un componente comune in tutte le attività è il seguente:

1) Estendere la classe dell'applicazione per poter creare il componente comune e mantenere un riferimento ad esso.

public class ApplicationDagger extends Application { 

    private ApplicationComponent component; 

    @Override 
    public void onCreate(){ 
     super.onCreate(); 
     component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build(); 
    } 

    public ApplicationComponent getComponent(){ 
      return component; 
    } 
} 

2) Creare un DaggerActivity astratta che ottiene la componente comune da parte delle applicazioni e chiama un metodo astratto injectActivity, dando il componente come argomento. Come questo:

public abstract class DaggerActivity extends Activity { 

    @Override 
    public void onCreate(Bundle saved){ 
     super.onCreate(saved); 
     ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent(); 
     injectActivity(component); 
    } 

    public abstract void injectActivity(ApplicationComponent component); 
} 

3) Ultimo, si deve iniettare in realtà ogni Activity estende DaggerActivity. Ma questo può essere fatto con meno sforzi ora, dato che devi implementare il metodo abstract altrimenti otterrai degli errori di compilazione. Andiamo:

public class FirstActivity extends DaggerActivity { 

    @Inject 
    ClassToInject object; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     //initialize your Activity 
    } 

    @Override 
    public void injectActivity(ApplicationComponent component) { 
     component.inject(this); 
    } 
} 

Naturalmente, è ancora necessario dichiarare ogni attività esplicitamente nel componente.

UPDATE: Iniettare @ActivityScope oggetti in frammenti

Ad un certo punto, avevo bisogno di usare custom scopes per associare gli oggetti a un ciclo di vita Activity. Ho deciso di estendere questo post in quanto potrebbe aiutare alcune persone.

Diciamo che avete un @modulo classe ActivityModule e @Subcomponent interfaccia ActivityComponent.

È necessario modificare lo DaggerActivity. Il Activities che estende DaggerActivity dovrebbe implementare il nuovo metodo (modifica della firma).

public abstract class ActivityDagger extends AppCompatActivity { 

    ComponentActivity component; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this)); 
     injectActivity(component); 
     super.onCreate(savedInstanceState); 
    } 

    ActivityComponent getComponent() { 
     return component; 
    } 

    public abstract void injectActivity(ActivityComponent component); 
} 

Poi, una classe FragmentDagger estendentesi Fragment possono essere creati in questo modo:

public abstract class FragmentDagger extends Fragment { 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     ActivityDagger activityDagger = (ActivityDagger) getActivity(); 
     ActivityComponent component = activityDagger.getComponent(); 
     injectFragment(component); 
    } 

    public abstract void injectFragment(ActivityComponent component); 

} 

Quanto al Activities, il Fragments estende FragmentDagger hanno solo un metodo per implementare:

public abstract void injectFragment(ActivityComponent component); 

Dovresti essere in grado di riutilizzare lo Fragments ovunque tu voglia. Si noti che il metodo ActivityDagger in ActivityDagger deve essere chiamato dopo l'istanza del componente. In caso contrario, verrà visualizzato NullPointerException quando viene ricreato lo stato Activity, in quanto verrà chiamato il metododello Fragment.

+0

Grazie per il suggerimento! –

2

si può fare un piccolo hack utilizzando la riflessione:

public class UiInjector { 
 

 
    private static final String METHOD_NAME = "inject"; 
 

 
    private final UIComponent component; 
 

 
    public UiInjector(final UIComponent component) { 
 
     this.component = component; 
 
    } 
 

 
    public void inject(final Object subject) { 
 
     try { 
 
      component.getClass() 
 
        .getMethod(METHOD_NAME, subject.getClass()) 
 
        .invoke(component, subject); 
 
     } catch (final NoSuchMethodException exception) { 
 
      throwNoInjectMethodForType(component, subject.getClass()); 
 
     } catch (final Exception exception) { 
 
      throwUnknownInjectionError(exception); 
 
     } 
 
    } 
 

 
    private void throwNoInjectMethodForType(final Object component, final Class subjectType) { 
 
     throw new RuntimeException(component.getClass().getSimpleName() + 
 
       " doesn't have inject method with parameter type : " + subjectType); 
 
    } 
 

 
    private void throwUnknownInjectionError(final Exception cause) { 
 
     throw new RuntimeException("Unknown injection error", cause); 
 
    } 
 
}

In questo caso, è ancora necessario per scrivere il metodo di iniettare in un componente, ma non hai bisogno di 'iniettare' metodo in ogni attività, frammento, vista, qualunque cosa.

Perché è lavoro? quando usiamo getClass() sul soggetto dell'iniezione otterremo una classe discendente, non base.

Attenzione! Nel caso in cui si usi Proguard, è necessario aggiungere il successivo -keep class <ComponentClass> { *; } alle proprie regole per mantenere i metodi di iniezione come è nel componente

Problemi correlati