Modelli di design per Android The Singleton Pattern

Qual è il modello Singleton?

Singleton Pattern è un pattern di progettazione software che garantisce che una classe abbia solo un'istanza e che tale classe fornisca un punto di accesso globale. Ogni volta che più classi o client richiedono quella classe, ottengono la stessa istanza della classe. Questa classe Singleton può essere responsabile dell'istanza di se stessa oppure è possibile delegare la creazione dell'oggetto a una classe factory.  

Usiamo l'esempio di un cellulare e il suo proprietario. Un telefono è in genere di proprietà di una singola persona, mentre una persona può possedere molti telefoni. Ogni volta che uno di questi telefoni squilla, lo stesso proprietario lo riprende. 

Vantaggi del modello Singleton

In una tipica app per Android, ci sono molti oggetti per i quali abbiamo bisogno di una sola istanza globale, sia che tu la stia utilizzando direttamente o semplicemente passandola ad un'altra classe. Gli esempi includono le cache, OkHttpClientHttpLoggingInterceptorRetrofitGSONSharedPreferences, la classe del repository, ecc. Se dovessimo istanziare più di uno di questi tipi di oggetti, avremmo incontrato problemi come comportamento errato delle app, uso eccessivo delle risorse e altri risultati confusi. 

Implementazione

È abbastanza facile implementare questo modello. Il seguente frammento di codice mostra come viene creato un Singleton.

public class Singleton private static Singleton INSTANCE = null; // altre variabili di istanza possono essere qui private Singleton () ; pubblico statico Singleton getInstance () if (INSTANCE == null) INSTANCE = new Singleton ();  return (INSTANCE);  // altri metodi di istanza possono seguire

Nel codice sopra, abbiamo una variabile statica ESEMPIO per tenere un'istanza della classe. Abbiamo anche reso privato il costruttore perché vogliamo applicare la non validità: la classe può solo creare un'istanza. Il metodo getInstance () garantisce che la classe sia istanziata, se non è stata, e che è restituita al chiamante. 

Esempio: creazione di una singola istanza di Retrofit

Retrofit è una libreria popolare per connettere un servizio web REST traducendo l'API in interfacce Java. Per saperne di più, guarda il mio tutorial qui su Envato Tuts+. 

In un'app Android, avrai bisogno di una singola istanza globale di un oggetto Retrofit in modo che altre parti di un'app come un UserProfileActivity o SettingsActivity può usarlo per eseguire una richiesta di rete senza la necessità di creare un'istanza ogni volta che ne abbiamo bisogno. La creazione di più istanze inquinerebbe la nostra app con oggetti di retrofit non utilizzati, occupando quindi memoria non necessaria su un dispositivo mobile già vincolato dalla memoria. 

importazione retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClient retrofit statico privato retrofit = null; public static Retrofit getClient (String baseUrl) if (retrofit == null) retrofit = new Retrofit.Builder () .baseUrl (baseUrl) .addConverterFactory (GsonConverterFactory.create ()) .build ();  retrofit di ritorno; 

Quindi ogni volta che il cliente A chiama RetrofitClient.getClient (), crea l'istanza se non è già stata creata, e quindi quando il client B chiama questo metodo, controlla se l'istanza di Retrofit esiste già. In tal caso, restituisce l'istanza al client B invece di crearne una nuova. 

Trattare con il multithreading

Nel sistema Android, è possibile eseguire lo spin off di più thread per eseguire attività diverse. Questi thread possono finire per eseguire contemporaneamente lo stesso blocco di codice. Nel caso del Singleton classe sopra, questo potrebbe portare alla creazione di più istanze di oggetti, che violano il contratto di un Singleton. Quindi il nostro metodo di snippet di codice Singleton getInstance () non è thread-safe Vedremo ora come renderlo sicuro.

Sincronizza il getInstance () Metodo 

Uno dei modi per rendere sicuro il thread del codice Singleton è quello di creare il metodo getInstance () uno sincronizzato. Ciò consente solo a un thread di eseguire il metodo alla volta, forzando ogni altro thread in uno stato di attesa o bloccato. 

public class Singleton private static Singleton INSTANCE = null; // altre variabili di istanza possono essere qui private Singleton () ; pubblico statico sincronizzato Singleton getInstance () if (INSTANCE == null) INSTANCE = new Singleton ();  return (INSTANCE);  // altri metodi di istanza possono seguire

Questo approccio rende sicuro il nostro thread di codice, ma è un'operazione costosa. In altre parole, questo può rallentare le prestazioni. Quindi devi indagare e vedere se il costo delle prestazioni è utile nella tua applicazione.

Creare un'istanza

Un altro approccio per gestire più thread che accedono al singleton consiste nel creare immediatamente l'istanza Singleton quando la classe viene caricata o inizializzata (dal ClassLoader Android nella VM Dalvik). Questo rende il thread del codice sicuro. Quindi l'istanza dell'oggetto sarà già disponibile prima che qualsiasi thread acceda a ESEMPIO variabile. 

public class Singleton private static Singleton INSTANCE = new Singleton (); // altre variabili di istanza possono essere qui private Singleton () ; pubblico statico Singleton getInstance () return (INSTANCE);  // altri metodi di istanza possono seguire

Uno svantaggio di questo approccio è che si può finire per creare un oggetto che potrebbe non essere mai utilizzato, occupando quindi memoria non necessaria. Quindi questo approccio dovrebbe essere usato di solito solo se si è sicuri che il singleton sarà accessibile. 

Bonus: usando Dagger 2

Una libreria di dipendenze come Dagger può aiutarti a collegare le dipendenze degli oggetti e creare singleton usando il @Singleton annotazione. Ciò garantirà che l'oggetto sia inizializzato solo una volta durante il ciclo di vita dell'applicazione.

@Module public class NetworkModule @Provides @Singleton public Gson gson () GsonBuilder gsonBuilder = new GsonBuilder (); return gsonBuilder.create ();  @Provides @Singleton public HttpLoggingInterceptor loggingInterceptor () HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor (message -> Timber.i (message)); interceptor.setLevel (HttpLoggingInterceptor.Level.BODY); return interceptor;  @Provides @Singleton cache pubblica cache (File cachefile) return new Cache (cacheFile, 10 * 1000 * 1000); // 10 MB di cache @Provides @Singleton public File cacheFile (contesto contesto @ApplicationContext) return new File (context.getCacheDir (), "okhttp_cache");  @Provides @Singleton public OkHttpClient okHttpClient (HttpLoggingInterceptor loggingInterceptor, Cache cache) return new OkHttpClient.Builder () .addInterceptor (loggingInterceptor) .cache (cache) .build ();  @Provides Retrofit pubblico @Singleton Retrofit (OkHttpClient okHttpClient, Gson gson) return new Retrofit.Builder () .addCallAdapterFactory (RxJava2CallAdapterFactory.create ()) .addConverterFactory (GsonConverterFactory.create (gson)) .client (okHttpClient) .baseUrl ( "tu / base / url") .build (); 

Nel codice sopra, creiamo una singola istanza di GSONnascondiglio, FileOkHttpClient e infine Retrofit tipi da fornire dal grafico delle dipendenze generato da Dagger. 

Per ulteriori informazioni su Dagger 2, consulta il nostro tutorial qui su Envato Tuts+. 

Conclusione

In questo breve tutorial, hai imparato a conoscere il pattern Singleton su Android: quali sono i vantaggi di utilizzarlo, come implementarlo scrivendo il tuo e alcuni modi per gestire più thread. Ti ho anche mostrato come utilizzare una libreria di terze parti come Dagger 2. 

Nel frattempo, dai uno sguardo ad alcuni dei nostri altri corsi e tutorial sul linguaggio Java e sullo sviluppo di app per Android!