我已经整理了一个简单的Android ListView应用程序来自学LoaderManagers和Loaders。该应用程序只提供一个Loader,当共享首选项发生更改时,通过OnSharedPreferenceChangeListener通知。
它有一个带有ListView和两个按钮的简单UI,一个用于添加首选项,另一个用于删除所有共享首选项。
我的活动是ListActivity的子类,并实现了LoaderManager.LoaderCallbacks>。
我的Loader只是在共享偏好设置发生变化时通知活动。
我的问题是,当单击“删除所有首选项”按钮时,不会通知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();
}
那么,关于为何删除偏好的任何想法都没有通知我的听众?
答案 0 :(得分:6)
sharedPreferences.edit().clear().commit()
不会通过设计通知听众。如果您希望收到删除共享偏好的通知,则需要使用remove(key)
代替clear()
。
查看Android source,您会发现clear()
和remove(key)
的实施略有不同。虽然remove(key)
将键添加到由编辑器创建的私有HashMap对象以跟踪更改,但clear()
只是设置一个标志,并且不对HashMap对象进行任何更改。 SharedPreferences私有方法NotifyListeners()不尊重此标志,它只查看HashMap对象。因此,侦听器不会报告调用clear()
。
[编辑]:
删除密钥时,您必须使用相同的editor
实例来提交密钥:
SharedPreferences settings = getSharedPreferences("someData", MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.remove(myKey1);
editor.remove(myKey2);
...
editor.commit();