在使用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不会再查出东西来了。