Ich verwende Dagger2 für DI in meiner Android-Anwendung. Ich habe festgestellt, dass ich für jede Klasse, die das @Inject-Feld verwendet, eine Inject-Methode schreiben muss. Gibt es eine Möglichkeit, dass ich einfach die übergeordnete Klasse injizieren kann, damit ich nicht inject für jede Unterklasse aufrufen muss? Nehmen Sie zum Beispiel Aktivität. Ich habe ein BaseActivity
von dem jede Aktivität ausgeht. Gibt es eine Möglichkeit, einfach eine Inject-Methode in der Komponente für BaseActivity zu erstellen und einfach inject in BaseActivitys onCreate aufzurufen, und @inject-Felder in Unteraktivitäten werden automatisch injiziert?
Kann ich einfach die Superklasse injizieren, wenn ich dagger2 für die Abhängigkeitsinjektion verwende?
Gordak
Ich bin auf die gleiche Situation gestoßen. Eine Möglichkeit, die Injektion einer gemeinsamen Komponente in allen Aktivitäten etwas zu erleichtern, ist die folgende:
1) Erweitern Sie die Application-Klasse, um die gemeinsame Komponente erstellen und einen Verweis darauf beibehalten zu können.
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) Erstellen Sie eine abstrakte DaggerActivity, die die gemeinsame Komponente von Application erhält und eine abstrakte Methode aufruft injectActivity
, wobei die Komponente als Argument angegeben wird. So was:
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) Zuletzt müssen Sie tatsächlich jeden spritzen Activity
verlängern DaggerActivity
. Aber das geht jetzt mit weniger Aufwand, da man das umsetzen muss abstract
-Methode andernfalls erhalten Sie Kompilierungsfehler. Auf geht’s:
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);
}
}
Natürlich müssen Sie trotzdem jede Aktivität explizit in Ihrer Komponente deklarieren.
UPDATE: Injizieren von @ActivityScope-Objekten in Fragmente
Irgendwann musste ich konsumieren benutzerdefinierte Bereiche Objekte an ein binden
Activity
Lebenszyklus. Ich habe mich entschieden, diesen Beitrag zu erweitern, da er einigen Leuten helfen könnte.
Nehmen wir an, Sie haben eine @Modul Klasse ActivityModule
und ein @Unterkomponente Schnittstelle ActivityComponent
.
Sie müssten die ändern DaggerActivity
. Das Activities
verlängern DaggerActivity
müssten die neue Methode implementieren (Änderung der Signatur).
public abstract class ActivityDagger extends AppCompatActivity {
ActivityComponent 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);
}
Dann eine Klasse FragmentDagger
verlängern Fragment
kann so erstellt werden:
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);
}
Wie für die Activities
das Fragments
verlängern FragmentDagger
haben nur eine Methode zu implementieren:
public abstract void injectFragment(ActivityComponent component);
Sie sollten in der Lage sein, wiederzuverwenden Fragments
wo immer Sie wollen. Beachten Sie, dass die Methode super.onCreated()
in ActivityDagger
sollte nach der Komponenteninstanziierung aufgerufen werden. Sonst bekommt man NullPointerException wenn der Activity
Zustand wird neu erstellt, weil die Methode super.onCreate()
des Fragment
wird angerufen werden.
Kirill Bojarschinow
Es ist derzeit nicht möglich. Erklärung von Gregory Kick (ab hier):
So funktionieren die Injektionsmethoden für Mitglieder:
- Sie können eine Injektionsmethode für Mitglieder für jeden Typ erstellen, der vorhanden ist
@Inject
irgendwo in seiner Klassenhierarchie. Wenn dies nicht der Fall ist, erhalten Sie eine Fehlermeldung.- Alle
@Inject
ed Members in der gesamten Typhierarchie werden eingefügt: der Argumenttyp und alle Supertypen.- Es werden keine Mitglieder sein
@Inject
ed für Untertypen des Argumenttyps.
Dieses Thema wurde diskutiert hier und hier, folgen Sie diesen für Updates. Aber es ist unwahrscheinlich, dass sich das bald ändert, denn Dagger 2 ist es kurz vor der Freigabe.
-
Es scheint, dass sie diese Entscheidung aus einem bestimmten Grund getroffen haben. Aber es ist immer noch schade, dass sie dies nicht unterstützen, da es meiner Meinung nach ein wenig kontraintuitiv ist. Trotzdem danke für die Antwort!
– Chris.Zou
5. April 2015 um 5:19 Uhr
-
@Chris.Zou Um die Injektion von Unterklassen zu unterstützen, müssten Sie zur Laufzeit reflektieren. Das Dagger 2-Team hat schon früh entschieden, dass es Dinge zur Laufzeit vermeiden wollte, da es langsamer ist und Sie Fehler erst erfahren, wenn Sie die App ausführen.
– vaughandroid
22. Juni 2015 um 11:24 Uhr
Sie können einen kleinen Hack mit Reflektion machen:
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 diesem Fall müssen Sie immer noch die Inject-Methode in eine Komponente schreiben, aber Sie brauchen die ‘Inject’-Methode nicht in jeder Aktivität, jedem Fragment, jeder Ansicht oder was auch immer.
Warum ist es Arbeit? wenn wir verwenden getClass()
auf Injektionssubjekt erhält eine Nachkommenklasse, keine Basis.
Vorsicht! Falls Sie Proguard verwenden, müssen Sie als nächstes hinzufügen
-keep class <ComponentClass> { *; }
an Ihre Regeln anpassen, um die Inject-Methoden unverändert in der Komponente beizubehalten
-
Der Dolch soll zur Kompilierzeit überprüft werden, also wird dies diesen Zweck zunichte machen …
– AA_PV
9. Juni 2020 um 12:29 Uhr
Könnten Sie einen Beispielcode hinzufügen, um zu zeigen, was Sie meinen?
– nhaarman
28. März 2015 um 11:49 Uhr
Nur eine Frage (wenn ich Ihre Frage richtig verstanden habe), warum definieren Sie das Injektionsfeld nicht in der Basisaktivität und verwenden es in der untergeordneten Aktivität. Jeder solche Fall, in dem Sie möchten, dass Ihre “Inject-Felder in Unteraktivitäten automatisch injiziert werden”
– JSONParser
10. Oktober 2019 um 4:57 Uhr