OnSharedPreferenceChangeListener詳解及出現(xiàn)不觸發(fā)解決辦法
之前使用OnSharedPreferenceChangeListener,遇到了點(diǎn)小問題,就是有些時(shí)候OnSharedPreferenceChangeListener沒有被觸發(fā)。最近花了點(diǎn)時(shí)間研究了一下,小做整理。本文將會(huì)介紹監(jiān)聽器不被觸發(fā)的原因,解決方法,以及其中隱含的一些技術(shù)細(xì)節(jié)。
問題再現(xiàn)
OnSharedPreferenceChangeListener是Android中SharedPreference文件發(fā)生變化的監(jiān)聽器。通常我們想要進(jìn)行監(jiān)聽,會(huì)實(shí)現(xiàn)如下的代碼。
protected void onCreate(Bundle savedInstanceState) { PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key); } }); }
這種寫法看上去沒有什么問題,而且很多時(shí)候開始幾次onSharedPreferenceChanged方法也可以被調(diào)用。但是過一段時(shí)間(簡(jiǎn)單demo不容易出現(xiàn),但是使用DDMS中的gc會(huì)立刻導(dǎo)致接下來的問題),你會(huì)發(fā)現(xiàn)前面的方法突然不再被調(diào)用,進(jìn)而影響到程序的處理。
原因剖析
簡(jiǎn)而言之,就是你注冊(cè)的監(jiān)聽器被移除掉了。
首先我們先了解一下registerOnSharedPreferenceChangeListener注冊(cè)的實(shí)現(xiàn)。
private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>(); //some code goes here public void More ...registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { synchronized(this) { mListeners.put(listener, mContent); } }
從上面的代碼可以得知,一個(gè)OnSharedPreferenceChangeListener對(duì)象實(shí)際上是放到了一個(gè)WeakHashMap的容器中,執(zhí)行完示例中的onCreate方法,這個(gè)監(jiān)聽器對(duì)象很快就會(huì)成為垃圾回收的目標(biāo),由于放在WeakHashMap中作為key不會(huì)阻止垃圾回收,所以當(dāng)監(jiān)聽器對(duì)象被回收之后,這個(gè)監(jiān)聽器也會(huì)從mListeners中移除。所以就造成了onSharedPreferenceChanged不會(huì)被調(diào)用。
關(guān)于WeakHashMap相關(guān),請(qǐng)閱讀譯文:理解Java中的弱引用進(jìn)而更多了解。
如何解決
改為對(duì)象成員變量(推薦)
將監(jiān)聽器作為Activity的一個(gè)成員變量,在Activity的onResume進(jìn)行注冊(cè),在onPause時(shí)進(jìn)行注銷。推薦在這兩個(gè)Activity生命周期中進(jìn)行處理,尤其是當(dāng)SharedPreference值發(fā)生變化后,對(duì)Activity展示的UI進(jìn)行處理操作的情況。這種方法是最推薦的解決方案。
private OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "instance variable key=" + key); } }; @Override protected void onResume() { PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(mListener); super.onResume(); } @Override protected void onPause() { PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).unregisterOnSharedPreferenceChangeListener(mListener); super.onPause(); }
改為靜態(tài)變量(不推薦)
如下,將一個(gè)指向匿名的內(nèi)部類對(duì)象的變量sListener使用static修飾,這個(gè)內(nèi)部類對(duì)象則不會(huì)持有外部類的引用。
但是這種做法并不推薦,因?yàn)橐粋€(gè)靜態(tài)變量和與外部實(shí)例不相關(guān),我們很難和外部實(shí)例進(jìn)行一些操作。
private static OnSharedPreferenceChangeListener sListener = new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "static variable key=" + key); } };
為什么這樣設(shè)計(jì)
可能會(huì)有人認(rèn)為這是系統(tǒng)設(shè)計(jì)的貓膩或者bug,其實(shí)不然,這正是Android設(shè)計(jì)人員的高明之處。
正如我們示例的代碼一樣,將一個(gè)(隱式的)局部變量添加到監(jiān)聽器容器中,如果該容器只是一個(gè)普通的HashMap,這樣會(huì)導(dǎo)致內(nèi)存泄露,因?yàn)樵撊萜鬟€有局部變量指向的對(duì)象,該對(duì)象又隱式持有外部Activity的對(duì)象,導(dǎo)致Activity無法被銷毀。關(guān)于非靜態(tài)內(nèi)部類持有隱式持有外部類引用,請(qǐng)參考細(xì)話Java:”失效”的private修飾符
除此之外,因?yàn)榫植孔兞繜o法在其所在方法外部訪問,這樣就導(dǎo)致了我們只可以使用方法中使用局部變量就行注冊(cè),在合適的時(shí)機(jī)卻無法使用局部變量進(jìn)行注銷。
以上就是對(duì) Android OnSharedPreferenceChangeListener的介紹,以及出現(xiàn)問題解決辦法,謝謝大家對(duì)本站的支持!
- 如何從UA分辨出Android設(shè)備類型
- Android高仿微信對(duì)話列表滑動(dòng)刪除效果
- Android 中Handler引起的內(nèi)存泄露
- Android 代碼JIT友好度檢測(cè)工具
- Android點(diǎn)擊事件派發(fā)機(jī)制源碼分析
- android換膚功能 如何動(dòng)態(tài)獲取控件中背景圖片的資源id?
- Android仿微信列表滑動(dòng)刪除之可滑動(dòng)控件(一)
- Android ListView滑動(dòng)刪除操作(SwipeListView)
- Android實(shí)現(xiàn)仿微信tab高亮icon粘著手的滑動(dòng)效果
相關(guān)文章
Android Studio和Gradle使用不同位置JDK的問題解決
這篇文章主要介紹了Android Studio和Gradle使用不同位置JDK的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03android暫?;蛲V蛊渌魳凡シ牌鞯牟シ艑?shí)現(xiàn)代碼
來自android自帶的music源碼,下面是廣播接收的代碼,通過發(fā)送廣播來控制音樂的播放,停止等2013-11-11Android自定義垂直拖動(dòng)seekbar進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義垂直拖動(dòng)seekbar進(jìn)度條,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Suspend函數(shù)與回調(diào)的互相轉(zhuǎn)換示例詳解
這篇文章主要為大家介紹了Suspend函數(shù)與回調(diào)的互相轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01使用Kotlin實(shí)現(xiàn)文字漸變TextView的代碼
這篇文章主要介紹了使用Kotlin實(shí)現(xiàn)文字漸變TextView的代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Android實(shí)現(xiàn)閱讀進(jìn)度記憶功能
這篇文章主要介紹了Android實(shí)現(xiàn)閱讀進(jìn)度記憶功能,Android控件WebView實(shí)現(xiàn)保存閱讀進(jìn)度,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Android APK優(yōu)化工具Zipalign詳解
本文主要介紹Android APK優(yōu)化工具Zipalign,這里整理了相關(guān)資料,并詳細(xì)介紹如何使用Zipalign工具及使用技巧,有需要的小伙伴可以參考下2016-09-09