在使用StrictMode時,發現會爆出
StrictMode policy violation;~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
這個提示顯示, 在UI線程中有IO操作,請這是盡量避免。
但是我們真的在UI線程中使用了嗎?其實我們也是按照普通大眾的方法調用的。 直接在UI線程中調用getSharedPreference方法,該方法可能就會產生IO操作。
跟着查一下源碼吧.
在2.3版本中有這樣的代碼:
@Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; File f = getSharedPrefsFile(name); synchronized (sSharedPrefs) { sp = sSharedPrefs.get(f); if (sp != null && !sp.hasFileChanged()) { //Log.i(TAG, "Returning existing prefs " + name + ": " + sp); return sp; } } FileInputStream str = null; File backup = makeBackupFile(f); if (backup.exists()) { f.delete(); backup.renameTo(f); } // Debugging if (f.exists() && !f.canRead()) { Log.w(TAG, "Attempt to read preferences file " + f + " without permission"); } Map map = null; if (f.exists() && f.canRead()) { try { str = new FileInputStream(f); map = XmlUtils.readMapXml(str); str.close(); } catch (org.xmlpull.v1.XmlPullParserException e) { Log.w(TAG, "getSharedPreferences", e); } catch (FileNotFoundException e) { Log.w(TAG, "getSharedPreferences", e); } catch (IOException e) { Log.w(TAG, "getSharedPreferences", e); } } synchronized (sSharedPrefs) { if (sp != null) { //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map); sp.replace(map); } else { sp = sSharedPrefs.get(f); if (sp == null) { sp = new SharedPreferencesImpl(f, mode, map); sSharedPrefs.put(f, sp); } } return sp; } }
看發生了xml的讀寫操作, 怪不得會有IO操作。
但是再4.0版本此處代碼發生了變化。
@Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; synchronized (ContextImpl.class) { if (sSharedPrefs == null) { sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>(); } final String packageName = getPackageName(); ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName); if (packagePrefs == null) { packagePrefs = new ArrayMap<String, SharedPreferencesImpl>(); sSharedPrefs.put(packageName, packagePrefs); } // At least one application in the world actually passes in a null // name. This happened to work because when we generated the file name // we would stringify it to "null.xml". Nice. if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; } } sp = packagePrefs.get(name); if (sp == null) { File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); packagePrefs.put(name, sp); return sp; } } if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. sp.startReloadIfChangedUnexpectedly(); } return sp; }
看代碼,這里沒看到XML的IO操作。 那這個IO跑哪里去了呢? 跟蹤一下SharedPreferencesImpl的構造方法,
SharedPreferencesImpl(File file, int mode) { mFile = file; mBackupFile = makeBackupFile(file); mMode = mode; mLoaded = false; mMap = null; startLoadFromDisk(); } private void startLoadFromDisk() { synchronized (this) { mLoaded = false; } new Thread("SharedPreferencesImpl-load") { public void run() { synchronized (SharedPreferencesImpl.this) { loadFromDiskLocked(); } } }.start(); }
你看, XML的讀取確實啟動新線程了,那為什么還要爆出IO操作呢? 重新跟蹤堆棧,發現原來不是這段代碼發生的IO操作,是在startReloadIfChangedUnexpectedly中發生的, 代碼中有些, 處於多進程模式,或者targe版本較低版本都會走該方法。那該方法中就會有IO操作。
后面, 我又更改了targetAPI的版本,不會走到startReloadIfChangedUnexpectedly中去。 果然StrictMode不會再查出東西來了。
