OnSharedPreferenceChangeListener не уведомляется после удаления записей из SharedPreferences

75
4

Я собрал простое приложение Android ListView, чтобы научить меня LoaderManagers и Loaders. Приложение просто предоставляет загрузчик, который уведомляется при изменении общих настроек через OnSharedPreferenceChangeListener.

Он имеет простой интерфейс с ListView и двумя кнопками, один для добавления предпочтений и один для удаления всех общих настроек.

Моя активность является подклассом ListActivity и реализует LoaderManager.LoaderCallbacks>.

Мой загрузчик просто уведомляет об активности при изменении общих настроек.

Моя проблема заключается в том, что при нажатии кнопки удаления всех предпочтений OnSharedPreferenceChangeListener не уведомляется, а добавление предпочтения уведомляет об этом. Несмотря на то, что слушатель не уведомлен, предпочтения удаляются, так как последующий щелчок по кнопке добавления предпочтений обновляет список, чтобы показать одну запись списка.

Здесь код (бит опущен для краткости):

MainActivity.java

public class MainActivity extends ListActivity implements LoaderManager.LoaderCallbacks<List<String>>
{

private ListView listView;
private static final int LOADER_ID = 1;
private LoaderManager.LoaderCallbacks<List<String>> mCallbacks;
private ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setUpLoaderManager();

ArrayList<String> values = new ArrayList<String>();
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, values);

listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(adapter);
}

private void setUpLoaderManager()
{
mCallbacks = this;
LoaderManager lm = getLoaderManager();
lm.initLoader(LOADER_ID, null, mCallbacks);
}

@Override
public void onLoadFinished(Loader<List<String>> loader, List<String> data)
{
switch (loader.getId())
{
case LOADER_ID:
adapter.clear();
adapter.addAll(data);
break;
}
}

@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args)
{
return new SampleLoader(this);
}

public void addSharedPreference(View v)
{
getSharedPreferences("someData", Context.MODE_PRIVATE).edit().putString(new Date().toString(), "hi").commit();
}

public void deleteSharedPreference(View v)
{
SharedPreferences sharedPreferences = getSharedPreferences("someData", Context.MODE_PRIVATE);
sharedPreferences.edit().clear().commit();
}

}

SampleLoader.java

public class SampleLoader extends AsyncTaskLoader<List<String>>

{
private List<String> mData;

public SampleLoader(Context ctx)
{
super(ctx);
}

@Override
public List<String> loadInBackground()
{
List<String> data = new ArrayList<String>();

SharedPreferences prefs = getContext().getSharedPreferences("someData", Context.MODE_PRIVATE);
Map<String, String> currentPref = (Map<String, String>) prefs.getAll();

if (currentPref.size() > 0)
{
for (Map.Entry<String, String> entry : currentPref.entrySet())
{
data.add(entry.getKey());
}
}
return data;
}

@Override
public void deliverResult(List<String> data)
{
if (isReset())
{
releaseResources(data);
return;
}

List<String> oldData = mData;
mData = data;

if (isStarted())
{
super.deliverResult(data);
}

if (oldData != null && oldData != data)
{
releaseResources(oldData);
}
}

@Override
protected void onStartLoading()
{
if (mData != null)
{
deliverResult(mData);
}

if (mObserver == null)
{
mObserver = new SharedPrefsObserver(this, getContext());
}

if (takeContentChanged() || mData == null)
{
forceLoad();
}
}

@Override
protected void onStopLoading()
{
cancelLoad();
}

@Override
protected void onReset()
{
onStopLoading();

if (mData != null)
{
releaseResources(mData);
mData = null;
}

if (mObserver != null)
{
mObserver = null;
}
}

@Override
public void onCanceled(List<String> data)
{
super.onCanceled(data);

releaseResources(data);
}

private void releaseResources(List<String> data)
{
}

private SharedPrefsObserver mObserver;
}

SharedPrefsObserver.java

public class SharedPrefsObserver
{
public static OnSharedPreferenceChangeListener listener;

public SharedPrefsObserver(final SampleLoader sampleLoader, Context context)
{
final SharedPreferences prefs = context.getSharedPreferences("someData", Context.MODE_PRIVATE);

listener = new OnSharedPreferenceChangeListener()
{

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
sampleLoader.onContentChanged();
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
}
}

activity_main.xml

<LinearLayout
android:id="@+id/button_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:weightSum="2">

<Button
android:id="@+id/button_delete"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="deleteSharedPreference"
android:text="Delete all preferences" >
</Button>

<Button
android:id="@+id/button_add"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="addSharedPreference"
android:text="Add new preference" >
</Button>
</LinearLayout>

<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_above="@id/button_layout"
android:layout_alignParentLeft="true">
</ListView>

Это работа, которую мне пришлось использовать, чтобы заставить Loader обновлять данные после удаления предпочтений:

    public void deleteSharedPreference(View v)
{
SharedPreferences sharedPreferences = getSharedPreferences("someData", Context.MODE_PRIVATE);
sharedPreferences.edit().clear().commit();
//dirty hack - sharedPreferences observer is not notified when preferences are removed so had to bypass it and call the loader manually
getLoaderManager().getLoader(LOADER_ID).forceLoad();
}

Итак, любые идеи относительно того, почему удаление предпочтения не уведомляет моего слушателя?

спросил(а) 2021-01-19T12:44:38+03:00 2 месяца, 4 недели назад
1
Решение
106

sharedPreferences.edit().clear().commit() не будет уведомлять слушателя по дизайну. Вам нужно использовать remove(key) вместо clear() если вы хотите получать уведомление об удалении общего предпочтения.

Если посмотреть на источник Android, вы увидите, что реализация clear() и remove(key) немного отличается. Хотя remove(key) добавляет ключ к частному объекту HashMap, который создается редактором для отслеживания изменений, clear() просто устанавливает флаг и не вносит никаких изменений в объект HashMap. Закрытый метод SharedPreferences NotifyListeners() не соблюдает этот флаг, он смотрит только на объект HashMap. Поэтому вызов clear() не будет отправлен слушателем.

[Edit]: При удалении ключей вы должны использовать один и тот же экземпляр editor для его фиксации:

SharedPreferences settings = getSharedPreferences("someData", MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.remove(myKey1);
editor.remove(myKey2);
...
editor.commit();

ответил(а) 2021-01-19T12:44:38+03:00 2 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема