Dagger 2 per sviluppatori Android inesperti. Pugnale 2. Livello avanzato. Parte 1
Questo articolo è la sesta parte di una serie di articoli destinati, secondo l'autore, a coloro che non riescono a capire come gestire l' iniezione di dipendenza e il framework Dagger 2 , o semplicemente lo faranno. Scritto originariamente il 23 dicembre 2017. Traduzione gratis.

Questo è il sesto articolo della serie Dagger 2 per sviluppatori Android principianti. ... Se non hai letto i precedenti, allora sei qui .
Dopo aver analizzato un semplice esempio di utilizzo delle annotazioni
Ma ho incluso un punto di interruzione dell'articolo (Sheckpoint). In questi luoghi puoi fare una breve pausa e distrarti. Penso che questo sarà utile per chi non conosce Dagger 2 e Dependency Injection (DI).
Finora, abbiamo esaminato i progetti Java comuni negli esempi. Spero che la maggior parte di voi ora abbia una comprensione di DI e di come Dagger 2 consenta l'implementazione di DI. Ora tuffiamoci in un'applicazione Android di esempio e proviamo a utilizzare Dagger 2 in questo progetto.
Per mettere tutto in un unico posto, come nei laboratori di codice di Google, ho creato un ramo kickstart . Il nostro obiettivo sarà quello di rimuovere i forti legami in questo progetto. Parti della soluzione saranno in rami separati di questo progetto.
...
Oltre alle dipendenze discusse in precedenza, aggiungi quanto segue:
...

Le dipendenze di alto livello sono evidenziate in verde, il che significa che non sono necessarie per nessun'altra dipendenza, ma hanno bisogno di alcune delle dipendenze.
Come leggo questo diagramma? Ad esempio, in
Per ottenere utenti casuali utilizzando l'API di cui hai bisogno
Prenditi un momento per guardare il codice in
(punto di controllo)
...
Ciò significa che Component conterrà solo quelle dipendenze che sono contrassegnate in verde nel grafico delle dipendenze, ovvero
Primo -
Creiamo

Per implementare la comunicazione tra i moduli, l'attributo è obbligatorio
Quali moduli devi collegare?

Come abbiamo collegato i moduli tra loro utilizzando l'attributo
Considerando le esigenze del componente creato (metodi
Ora in
(punto di controllo)
...
Ogni volta che chiami annotazione
annotazione annotazione
Questa annotazione ci aiuterà a distinguere il contesto. Aggiungiamo questa annotazione ai metodi, senza dimenticare l'attributo. Alternativa all'annotazione
Per sostituire l'annotazione
Abbiamo anche esplorato 3 nuove annotazioni. Il primo è

Questo è il sesto articolo della serie Dagger 2 per sviluppatori Android principianti. ... Se non hai letto i precedenti, allora sei qui .
Serie di articoli
- Dagger 2 per sviluppatori Android inesperti. Introduzione.
- Dagger 2 per sviluppatori Android inesperti. Iniezione di dipendenza. Parte 1 .
- Dagger 2 per sviluppatori Android inesperti. Iniezione di dipendenza. Parte 2 .
- Dagger 2 per sviluppatori Android inesperti. Pugnale 2.Parte 1 .
- Dagger 2 per sviluppatori Android inesperti. Pugnale 2.Parte 2 .
- Dagger 2 per sviluppatori Android inesperti. Pugnale 2. Livello avanzato.
Parte 1 (sei qui) . - Dagger 2 per sviluppatori Android inesperti. Pugnale 2. Livello avanzato.
Parte 2.
All'inizio della serie
Abbiamo analizzato la classe generata da Dagger 2 e osservato come Dagger 2 utilizza il pattern Builder per fornire le dipendenze richieste.Dopo aver analizzato un semplice esempio di utilizzo delle annotazioni
@Module
e@Provides
...Prefazione
Questo articolo potrebbe sembrarti un po' grande. Di solito i miei articoli non superano gli 800 caratteri. Volevo scomporlo in parti più piccole, ma il motivo per cui l'articolo è così grande è che se rimani nel mezzo per molto tempo quando risolvi il problema delle dipendenze rigide , allora hai la possibilità di perderti.Ma ho incluso un punto di interruzione dell'articolo (Sheckpoint). In questi luoghi puoi fare una breve pausa e distrarti. Penso che questo sarà utile per chi non conosce Dagger 2 e Dependency Injection (DI).
Home Android

Finora, abbiamo esaminato i progetti Java comuni negli esempi. Spero che la maggior parte di voi ora abbia una comprensione di DI e di come Dagger 2 consenta l'implementazione di DI. Ora tuffiamoci in un'applicazione Android di esempio e proviamo a utilizzare Dagger 2 in questo progetto.
Per mettere tutto in un unico posto, come nei laboratori di codice di Google, ho creato un ramo kickstart . Il nostro obiettivo sarà quello di rimuovere i forti legami in questo progetto. Parti della soluzione saranno in rami separati di questo progetto.
Descrizione del progetto
Questo è un progetto molto semplice. In esso, otterremo utenti casuali utilizzando l' API Utenti casuali e li mostreremo inRecyclerView
... Non passerò molto tempo a spiegare il progetto, lo spiegherò in modo astratto. Ma, per favore, smonta il codice con attenzione in modo che l'introduzione di Dagger 2 nel progetto sia il più chiara e semplice possibile per te.# Classi e pacchetti
MainActivity.java
- Effettua richieste all'API e mostra gli articoli ricevuti inRecyclerView
...- Pacchetto
Model
- POJO per la risposta API, creato utilizzando lo schema JSON per POJO RandomUsersAdapter.java
-Adapter
perRecyclerView
dipendenze
Le seguenti librerie verranno utilizzate per implementare le funzioni del progetto:Retrofit
- per le chiamate APIGsonBuilder
eGson
- lavorare con JSONHttpLoggingInterceptor
- per la registrazione delle operazioni di reteOkHttpClient
- cliente perRetrofit
Picasso
- lavorare con le immagini inAdapter
MainActivity
le dipendenze sono presenti. E ogni volta che creiMainActivity
le dipendenze verranno istanziate più e più volte.public class MainActivity extends AppCompatActivity {
Retrofit retrofit;
RecyclerView recyclerView;
RandomUserAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
Timber.plant(new Timber.DebugTree());
HttpLoggingInterceptor httpLoggingInterceptor = new
HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NonNull String message) {
Timber.i(message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.addInterceptor(httpLoggingInterceptor)
.build();
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://randomuser.me/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
populateUsers();
}
private void initViews() {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void populateUsers() {
Call<RandomUsers> randomUsersCall = getRandomUserService().getRandomUsers(10);
randomUsersCall.enqueue(new Callback<RandomUsers>() {
@Override
public void onResponse(Call<RandomUsers> call, @NonNull Response<RandomUsers> response) {
if(response.isSuccessful()) {
mAdapter = new RandomUserAdapter();
mAdapter.setItems(response.body().getResults());
recyclerView.setAdapter(mAdapter);
}
}
@Override
public void onFailure(Call<RandomUsers> call, Throwable t) {
Timber.i(t.getMessage());
}
});
}
public RandomUsersApi getRandomUserService(){
return retrofit.create(RandomUsersApi.class);
}
}
(punto di controllo)...
Problemi esistenti
Se guardiMainActivity
allora noterai i seguenti problemi:# Inizializzazione goffa degli oggetti
Quando guardi il metodoonCreate()
quindi potresti trovare un'inizializzazione goffa al suo interno. Certo, possiamo continuare a inizializzare gli oggetti in questo metodo in questo modo, ma è meglio trovare il modo giusto per risolvere il problema.# Testabilità
Dobbiamo anche trovare un modo per testare il nostro codice. EPicasso
dentroAdapter
interferisce anche con la testabilità. Sarebbe bello passare questa dipendenza attraverso il costruttore.public class RandomUserAdapter extends RecyclerView.Adapter<RandomUserAdapter.RandomUserViewHolder> {
private List<Result> resultList = new ArrayList<>();
public RandomUserAdapter() {
}
@Override
public RandomUserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_random_user,
parent, false);
return new RandomUserViewHolder(view);
}
@Override
public void onBindViewHolder(RandomUserViewHolder holder, int position) {
Result result = resultList.get(position);
holder.textView.setText(String.format("%s %s", result.getName().getFirst(),
result.getName().getLast()));
Picasso.with(holder.imageView.getContext())
.load(result.getPicture().getLarge())
.into(holder.imageView);
}
......
Complichiamo un po' l'esempio
Dipendenze presentate sopra nella classeMainActivity
, erano necessari solo per approfondire un po' il progetto e sentirsi a proprio agio. Se vai più in profondità, come in ogni progetto reale, ci saranno più dipendenze. Aggiungiamone altri.Oltre alle dipendenze discusse in precedenza, aggiungi quanto segue:
File
- per memorizzare la cacheCache
- per la cache di reteOkHttp3Downloader
- bootloader usandoOkHttpClient
per caricare immaginiPicasso
- per l'elaborazione di immagini dalla rete
public class MainActivity extends AppCompatActivity {
Retrofit retrofit;
RecyclerView recyclerView;
RandomUserAdapter mAdapter;
Picasso picasso;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
Timber.plant(new Timber.DebugTree());
File cacheFile = new File(this.getCacheDir(), "HttpCache");
cacheFile.mkdirs();
Cache cache = new Cache(cacheFile, 10 * 1000 * 1000); //10 MB
HttpLoggingInterceptor httpLoggingInterceptor = new
HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NonNull String message) {
Timber.i(message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.cache(cache)
.addInterceptor(httpLoggingInterceptor)
.build();
OkHttp3Downloader okHttpDownloader = new OkHttp3Downloader(okHttpClient);
picasso = new Picasso.Builder(this).downloader(okHttpDownloader).build();
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://randomuser.me/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
populateUsers();
}
private void initViews() {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void populateUsers() {
Call<RandomUsers> randomUsersCall = getRandomUserService().getRandomUsers(10);
randomUsersCall.enqueue(new Callback<RandomUsers>() {
@Override
public void onResponse(Call<RandomUsers> call, @NonNull Response<RandomUsers> response) {
if(response.isSuccessful()) {
mAdapter = new RandomUserAdapter(picasso);
mAdapter.setItems(response.body().getResults());
recyclerView.setAdapter(mAdapter);
}
}
@Override
public void onFailure(Call<RandomUsers> call, Throwable t) {
Timber.i(t.getMessage());
}
});
}
public RandomUsersApi getRandomUserService(){
return retrofit.create(RandomUsersApi.class);
}
}
(punto di controllo)...
Grafico delle dipendenze
Un grafico delle dipendenze non è altro che un diagramma che spiega le dipendenze tra le classi. La formazione di un tale grafico rende l'implementazione più comprensibile (lo vedrai verso la fine). Dai un'occhiata al grafico delle dipendenze per il nostro progetto.
Le dipendenze di alto livello sono evidenziate in verde, il che significa che non sono necessarie per nessun'altra dipendenza, ma hanno bisogno di alcune delle dipendenze.
Come leggo questo diagramma? Ad esempio, in
Picasso
due dipendenze -OkHttp3Downloader
eContext
...Per ottenere utenti casuali utilizzando l'API di cui hai bisogno
Retrofit
... Lui, a sua volta, ha bisogno di due dipendenze:GsonConvertFactory
eOkHttpClient
eccetera.Prenditi un momento per guardare il codice in
MainActivity
e confrontalo con il diagramma per una migliore comprensione.(punto di controllo)
...
Iniettare dipendenze con Dagger 2
È possibile trovare il codice completo in un ramo separato del progetto .La nota:
RandomUsersAPI
eRandomUsersApi
- Questo è lo stesso. Solo un errore di battitura nel diagramma.RandomUsersComponent
sarà diverso nel thread sopra e negli esempi sotto. Ti suggerisco di utilizzare il nuovo ramo per la piena conformità con gli esempi seguenti e di lasciare quanto sopra come riferimento.- Per favore, non dimenticare di aggiungere il progetto ai segnalibri su GitHub (Star) se ti ha davvero aiutato a imparare.
Passaggio 1. Installazione di Dagger 2
Basta aggiungere alcune righe abuild.gradle
file.dependencies {
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
}
Passaggio 2. Creazione del componente
Il componente sarà l'interfaccia per l'intero grafico delle dipendenze. La migliore pratica per l'utilizzo di Component è dichiarare solo le dipendenze di primo livello in esso e nascondere il resto delle dipendenze.Ciò significa che Component conterrà solo quelle dipendenze che sono contrassegnate in verde nel grafico delle dipendenze, ovvero
RandomUsersAPI
ePicasso
...
@Component
public interface RandomUserComponent {
RandomUsersApi getRandomUserService();
Picasso getPicasso();
}
Come il componente stesso capirà dove ottenere le dipendenze dependRandomUsersAPI
ePicasso
? Usiamo i moduli.Passaggio 3. Creazione di moduli
Ora dobbiamo spostare il codice daMainActivity
in vari moduli. Guardando il grafico delle dipendenze, puoi decidere quali moduli sono necessari.Primo -
RandomUsersModule
, fornirà le dipendenzeRandomUsersApi
,GsonConverterFactory
,Gson
eRetrofit
...@Module
public class RandomUsersModule {
@Provides
public RandomUsersApi randomUsersApi(Retrofit retrofit){
return retrofit.create(RandomUsersApi.class);
}
@Provides
public Retrofit retrofit(OkHttpClient okHttpClient,
GsonConverterFactory gsonConverterFactory, Gson gson){
return new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://randomuser.me/")
.addConverterFactory(gsonConverterFactory)
.build();
}
@Provides
public Gson gson(){
GsonBuilder gsonBuilder = new GsonBuilder();
return gsonBuilder.create();
}
@Provides
public GsonConverterFactory gsonConverterFactory(Gson gson){
return GsonConverterFactory.create(gson);
}
}
Secondo -PicassoModule
che fornirà dipendenzePicasso
eOkHttp3Downloader
...@Module
public class PicassoModule {
@Provides
public Picasso picasso(Context context, OkHttp3Downloader okHttp3Downloader){
return new Picasso.Builder(context).
downloader(okHttp3Downloader).
build();
}
@Provides
public OkHttp3Downloader okHttp3Downloader(OkHttpClient okHttpClient){
return new OkHttp3Downloader(okHttpClient);
}
}
Nel moduloRandomUsersModule
perRetrofit
necessarioOkHttpClient
... Che, a sua volta, ha bisogno di altre dipendenze. Perché non creare un modulo separato per questo?Creiamo
OkHttpClientModule
che forniràOkHttpClient
,Cache
,HttpLoggingInterceptor
eFile
...@Module
public class OkHttpClientModule {
@Provides
public OkHttpClient okHttpClient(Cache cache, HttpLoggingInterceptor httpLoggingInterceptor){
return new OkHttpClient()
.newBuilder()
.cache(cache)
.addInterceptor(httpLoggingInterceptor)
.build();
}
@Provides
public Cache cache(File cacheFile){
return new Cache(cacheFile, 10 * 1000 * 1000); //10 MB
}
@Provides
public File file(Context context){
File file = new File(context.getCacheDir(), "HttpCache");
file.mkdirs();
return file;
}
@Provides
public HttpLoggingInterceptor httpLoggingInterceptor(){
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Timber.d(message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
}
I moduli sono quasi pronti, maPicassoModule
eOkHttpClientModule
necessarioContext
e, forse, ci sarà utile in altri luoghi. Facciamo anche un modulo per questi scopi.@Module
public class ContextModule {
Context context;
public ContextModule(Context context){
this.context = context;
}
@Provides
public Context context(){ return context.getApplicationContext(); }
}
Passaggio 4. Collegare i moduli
Ora abbiamo tutti i moduli e i componenti, come nell'immagine qui sotto. Ma come trasmettereContext
ad altri moduli? Abbiamo bisogno di collegare moduli che dipendono l'uno dall'altro.
Per implementare la comunicazione tra i moduli, l'attributo è obbligatorio
includes
... Questo attributo include le dipendenze dei moduli di riferimento nel modulo corrente.Quali moduli devi collegare?
RandomUsersModule
deveOkHttpClientModule
OkHttpClientModule
deveContextModule
PicassoModule
deveOkHttpClientModule
eContextModule
... ComeOkHttpClientModule
già associato aContextModule
, allora puoi solo fareOkHttpClientModule
// в RandomUsersModule.java
@Module(includes = OkHttpClientModule.class)
public class RandomUsersModule { ... }
// в OkHttpClientModule.java
@Module(includes = ContextModule.class)
public class OkHttpClientModule { ... }
// в PicassoModule.java
@Module(includes = OkHttpClientModule.class)
public class PicassoModule { ... }
Quindi, abbiamo collegato tutti i moduli.
Passaggio 5. Collegamento di componenti e moduli
Al momento, tutti i moduli sono collegati e comunicano tra loro. Ora è il momento di dire a Component o addestrarlo ad accedere ai moduli che gli forniranno le dipendenze richieste.Come abbiamo collegato i moduli tra loro utilizzando l'attributo
includes
, in modo simile, possiamo collegare il componente e i moduli utilizzando l'attributomodules
...Considerando le esigenze del componente creato (metodi
getRandomUserService()
egetPicasso()
) includono collegamenti ai moduli nel componenteRandomUsersModule
ePicassoModule
usando l'attributomodules
... @Component(modules = {RandomUsersModule.class, PicassoModule.class})
public interface RandomUserComponent {
RandomUsersApi getRandomUserService();
Picasso getPicasso();
}

Passaggio 6. Costruisci il progetto
Se hai fatto tutto correttamente, Dagger 2 genererà una classe basata sul componente che abbiamo creato che fornisce le dipendenze necessarie.Ora in
MainActivity
puoi ottenere comodamente le dipendenzePicasso
eRandomUsersApi
attraversoRandomUserComponent
...public class MainActivity extends AppCompatActivity {
RandomUsersApi randomUsersApi;
Picasso picasso;
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
RandomUserComponent daggerRandomUserComponent = DaggerRandomUserComponent.builder()
.contextModule(new ContextModule(this))
.build();
picasso = daggerRandomUserComponent.getPicasso();
randomUsersApi = daggerRandomUserComponent.getRandomUserService();
populateUsers();
...
}
...
}
Passaggio 7. Congratulazioni con te stesso!
Ce l'hai fatta! Hai utilizzato Dagger 2 in un'app Android. Congratulazioni con te stesso e prenditi una pausa(punto di controllo)
...
GIF

Ma c'è un problema
Che cosa? Qual è il problema?Ogni volta che chiami
DaggerComponent.build()
vengono create nuove istanze di tutti gli oggetti o dipendenze configurate. In questo caso, perché Dagger 2 non sa che ho bisogno di una sola istanza?Picasso
? In altre parole, come possiamo dire a Dagger 2 di fornirci una dipendenza come singleton? annotazione@Scope
annotazione@Scope
dice a Dagger 2 di creare solo una singola istanza, anche seDaggerComponent.build()
chiamato più volte. Questo fa funzionare la dipendenza come un singleton. Per configurare l'ambito richiesto (Scope) è necessario creare la propria annotazione.@Scope
@Retention(RetentionPolicy.CLASS)
public @interface RandomUserApplicationScope {
}
@Retention
È un'annotazione per indicare il punto di deviazione dell'uso dell'annotazione. Parla di quando è possibile utilizzare l'annotazione. Ad esempio, con il contrassegno SOURCE, l'annotazione sarà disponibile solo nel codice sorgente e verrà scartata in fase di compilazione, con il contrassegno CLASS, l'annotazione sarà disponibile in fase di compilazione, ma non a runtime, con il contrassegno RUNTIME, l'annotazione sarà disponibile in fase di esecuzione.Utilizzo dell'ambito
Per utilizzare la regione creata, dobbiamo iniziare con il componente, contrassegnarlo con l'annotazione generata e quindi contrassegnare ogni metodo di cui abbiamo bisogno come singleton.@RandomUserApplicationScope
@Component(modules = {RandomUsersModule.class, PicassoModule.class})
public interface RandomUserComponent { ...}
@Module(includes = OkHttpClientModule.class)
public class PicassoModule {
...
@RandomUserApplicationScope
@Provides
public Picasso picasso(Context context, OkHttp3Downloader okHttp3Downloader){
return new Picasso.Builder(context).
downloader(okHttp3Downloader).
build();
}
...
}
@Module(includes = OkHttpClientModule.class)
public class RandomUsersModule {
...
@RandomUserApplicationScope
@Provides
public Retrofit retrofit(OkHttpClient okHttpClient,
GsonConverterFactory gsonConverterFactory, Gson gson){
return new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://randomuser.me/")
.addConverterFactory(gsonConverterFactory)
.build();
}
...
}
Ecco come creiamo una singola istanza.Un altro problema!
GIF
In genere, in ogni applicazione, utilizziamo due tipi di contesto:
ApplicationContext
e contestoActivity
... Come fornirli? FornireApplicationContext
può essere utilizzatoContextModule
... Creiamo un altro modulo.@Module
public class ActivityModule {
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
@RandomUserApplicationScope
@Provides
public Context context(){ return context; }
}
Ma la classe generata non risolve il problema. Forniamo ora due dipendenze con il tipoContext
e Dagger 2 non sarà in grado di capire quale usare, si verificherà un errore. annotazione@Named
Questa annotazione ci aiuterà a distinguere il contesto. Aggiungiamo questa annotazione ai metodi, senza dimenticare l'attributo.@Module
public class ActivityModule {
....
@Named("activity_context")
@RandomUserApplicationScope
@Provides
public Context context(){ return context; }
}
@Module
public class ContextModule {
....
@Named("application_context")
@RandomUserApplicationScope
@Provides
public Context context(){ return context.getApplicationContext(); }
}
Successivamente, diremo a Dagger 2 di utilizzare il contesto appropriato nei posti giusti.@Module(includes = ContextModule.class)
public class OkHttpClientModule {
....
@Provides
@RandomUserApplicationScope
public File file(@Named("application_context") Context context){
File file = new File(context.getCacheDir(), "HttpCache");
file.mkdirs();
return file;
}
....
}
@Module(includes = OkHttpClientModule.class)
public class PicassoModule {
...
@RandomUserApplicationScope
@Provides
public Picasso picasso(@Named("application_context")Context context, OkHttp3Downloader okHttp3Downloader){
return new Picasso.Builder(context).
downloader(okHttp3Downloader).
build();
...
}
Alternativa all'annotazione@Named
-@Qualifier
Per sostituire l'annotazione@Named
sul@Qualifier
è necessario creare un'annotazione separata e utilizzarla dove necessario.@Qualifier
public @interface ApplicationContext {}
Quindi annotiamo il metodo che fornisce la dipendenza appropriata.@Module
public class ContextModule {
....
@ApplicationContext
@RandomUserApplicationScope
@Provides
public Context context(){ return context.getApplicationContext(); }
}
Successivamente, segniamo i parametri di tutti i metodi in cui abbiamo bisognoApplicationContext
annotazione creata.@Module(includes = ContextModule.class)
public class OkHttpClientModule {
...
@Provides
@RandomUserApplicationScope
public File file(@ApplicationContext Context context){
File file = new File(context.getCacheDir(), "HttpCache");
file.mkdirs();
return file;
}
....
}
@Module(includes = OkHttpClientModule.class)
public class PicassoModule {
@RandomUserApplicationScope
@Provides
public Picasso picasso(@ApplicationContext Context context, OkHttp3Downloader okHttp3Downloader){
return new Picasso.Builder(context).
downloader(okHttp3Downloader).
build();
}
....
}
Dai un'occhiata al commit corrispondente per vedere come l'annotazione può essere sostituita.@Named
sul@Qualifier
...Sommario
Per ora, abbiamo preso un progetto semplice e vi abbiamo inserito delle dipendenze usando Dagger 2 e annotazioni.Abbiamo anche esplorato 3 nuove annotazioni. Il primo è
@Scope
per ottenere le dipendenze in una singola copia. Il secondo è@Named
, per separare i metodi che forniscono dipendenze dello stesso tipo. Terzo -@Qualifier
, in alternativa@Named
...Qual è il prossimo?
Per ora, abbiamo trattato solo le dipendenze a livello di applicazione. Nel prossimo articolo, esamineremo le dipendenze dei livelliActivity
, creiamo diversi componenti e impariamo a lavorarci. Il prossimo articolo uscirà tra una settimana.