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.