studio preferencias preferencia pantalla nombre example editar crear configuracion compartidas cambiar archivos archivo android dependencies sharedpreferences mvp android-context

preferencias - preferencefragment android



¿Cómo usar las preferencias compartidas en MVP sin Dagger y no hacer que el presentador dependa del contexto? (4)

Así es como lo hago. Tengo un singleton "SharedPreferencesManager" clase que manejará todas las operaciones de lectura y escritura a las preferencias compartidas como abajo

public final class SharedPreferencesManager { private static final String MY_APP_PREFERENCES = "ca7eed88-2409-4de7-b529-52598af76734"; private static final String PREF_USER_LEARNED_DRAWER = "963dfbb5-5f25-4fa9-9a9e-6766bfebfda8"; ... // other shared preference keys private SharedPreferences sharedPrefs; private static SharedPreferencesManager instance; private SharedPreferencesManager(Context context){ //using application context just to make sure we don''t leak any activities sharedPrefs = context.getApplicationContext().getSharedPreferences(MY_APP_PREFERENCES, Context.MODE_PRIVATE); } public static synchronized SharedPreferencesManager getInstance(Context context){ if(instance == null) instance = new SharedPreferencesManager(context); return instance; } public boolean isNavigationDrawerLearned(){ return sharedPrefs.getBoolean(PREF_USER_LEARNED_DRAWER, false); } public void setNavigationDrawerLearned(boolean value){ SharedPreferences.Editor editor = sharedPrefs.edit(); editor.putBoolean(PREF_USER_LEARNED_DRAWER, value); editor.apply(); } ... // other shared preference accessors }

Luego, cuando sea necesario acceder a una preferencia compartida, paso el objeto SharedPreferencesManager en el constructor del presentador correspondiente. Por ejemplo :

if(null == presenter){ presenter = new Presenter(SharedPreferencesManager.getInstance(getApplicationContext())); }

¡Espero que esto ayude!

Estoy tratando de implementar MVP sin Dagger (para propósitos de aprendizaje). Pero resolví el problema: uso el patrón de Repository para obtener datos sin procesar de la caché (Preferencias Compartidas) o de la red:

Shared Prefs| |<->Repository<->Model<->Presenter<->View Network|

Pero para poner mis manos en las preferencias compartidas tengo que poner un lugar en línea como

presenter = new Presenter(getApplicationContext());

Uso el par onRetainCustomNonConfigurationInstance / getLastCustomNonConfigurationInstance para mantener el presentador "retenido".

public class MyActivity extends AppCompatActivity implements MvpView { @Override protected void onCreate(Bundle savedInstanceState) { //... presenter = (MvpPresenter) getLastCustomNonConfigurationInstance(); if(null == presenter){ presenter = new Presenter(getApplicationContext()); } presenter.attachView(this); } @Override public Object onRetainCustomNonConfigurationInstance() { return presenter; } //... }

Entonces, ¿cómo usar las Preferencias Compartidas en MVP sin Dagger y no hacer que el Presentador dependa del Contexto?


Otro enfoque también se puede encontrar en las bibliotecas de arquitectura de Android:

Como las Preferencias Compartidas dependen de un contexto, solo deberían conocerlas. Para tener las cosas en un solo lugar, elijo un Singleton para gestionar esto. Consta de dos clases: el Administrador (es decir, SharePreferenceManager o ServiceManager o lo que sea), y un inicializador que inyecta el Contexto.

class ServiceManager { private static final ServiceManager instance = new ServiceManager(); // Avoid mem leak when referencing context within singletons private WeakReference<Context> context private ServiceManager() {} public static ServiceManager getInstance() { return instance; } static void attach(Context context) { instance.context = new WeakReference(context); } ... your code... }

El inicializador es básicamente un Provider vacío ( https://developer.android.com/guide/topics/providers/content-providers.html ), que se registra en el AndroidManifest.xml y se carga cuando se inicia la aplicación:

public class ServiceManagerInitializer extends ContentProvider { @Override public boolean onCreate() { ServiceManager.init(getContext()); return false; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } }

Todas las funciones son implementaciones predeterminadas, excepto onCreate, que inyecta el contexto requerido en nuestro administrador.

El último paso para hacer que esto funcione es registrar al proveedor en el manifiesto:

<provider android:authorities="com.example.service-trojan" android:name=".interactor.impl.ServiceManagerInitializer" android:exported="false" />

De esta manera, su administrador de servicios se desacopla de cualquier inicialización de contexto externo. Ahora se puede reemplazar completamente con otra implementación que sea independiente del contexto.


Puede usar el contexto de la Application en la capa del Repository sin pasar por Presenter como se explica aquí . Primero subclase su clase de aplicación y guarde su instancia en una variable estática.

public class MyApplication extends Application { private static context = null; public void onCreate(...) { context = this; ... } public static Context getContext() { return context; } }

Luego menciona el nombre de la clase de tu aplicación en el AndroidManifest ,

<application android:name=".MyApplication" ... > </application>

Ahora puede usar el contexto de la aplicación dentro del Repositorio (ya sea para las referencias compartidas, la base de datos SQLite, el acceso a la red), usando MyApplication.context .


Su presentador no debe ser dependiente del Context en primer lugar. Si su presentador necesita SharedPreferences , debe pasarlos al constructor .
Si su presentador necesita un Repository , nuevamente, póngalo en el constructor . Recomiendo encarecidamente ver las conversaciones de código limpio de Google, ya que hacen un buen trabajo explicando por qué debes usar una API adecuada.

Esta es una gestión de dependencia adecuada, que le ayudará a escribir código limpio, mantenible y comprobable. Y si usa daga, alguna otra herramienta de DI o suministra los objetos usted mismo es irrelevante.

public class MyActivity extends AppCompatActivity implements MvpView { @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences preferences = // get your preferences ApiClient apiClient = // get your network handling object Repository repository = new Repository(apiClient, preferences); presenter = new Presenter(repository); } }

Esta creación de objetos se puede simplificar utilizando un patrón de fábrica o algún marco DI como dagger, pero como se puede ver arriba, ni el Repository ni su presentador dependen de un Context . Si desea proporcionar sus SharedPreferences reales, solo su creación dependerá del contexto.

Su repositorio depende de algunos clientes de API y de las SharedPreferences , su presentador depende del Repository . Ambas clases se pueden probar fácilmente simplemente proporcionándoles objetos simulados.

Sin ningún código estático. Sin efectos secundarios.