喜糖

移动开发工程师 。涉及 android、ios、jni

导航

StrictMode对SharedPreferences的检查出来的IO操作

Posted on 2014-02-22 12:16  喜糖  阅读(6647)  评论(0编辑  收藏  举报

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