源码分析:SharedPreferences实现

来源:互联网 发布:ios10软件不受信任 编辑:程序博客网 时间:2024/05/23 14:04

首先看Context.java可以看到:

    public abstract SharedPreferences getSharedPreferences(String name,            int mode);

由于Context.java只是一个接口。实际的Context为Application/Activity这些实现类。这些类都继承于 ContextWarpper,ContextWrapper可以看到:

    @Override    public SharedPreferences getSharedPreferences(String name, int mode) {        return mBase.getSharedPreferences(name, mode);    }

mBase为Context,其实现为ContextImpl,查看ContextImpl.java可以看到:

@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;    }

SharedPreferences的真正实现类为:SharedPreferencesImpl,本文主要分析SharedPreferencesImpl.java的实现。

   SharedPreferencesImpl(File file, int mode) {        mFile = file;        mBackupFile = makeBackupFile(file);        mMode = mode;        mLoaded = false;        mMap = null;        startLoadFromDisk();    }

构造函数调用时,会调用startLoadFromDisk(),这个函数比较有意思:

    private void startLoadFromDisk() {        synchronized (this) {            mLoaded = false;        }        new Thread("SharedPreferencesImpl-load") {            public void run() {                synchronized (SharedPreferencesImpl.this) {                    loadFromDiskLocked();                }            }        }.start();    }  private void loadFromDiskLocked() {        if (mLoaded) {            return;        }        if (mBackupFile.exists()) {            mFile.delete();            mBackupFile.renameTo(mFile);        }        // Debugging        if (mFile.exists() && !mFile.canRead()) {            Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");        }        Map map = null;        StructStat stat = null;        try {            stat = Os.stat(mFile.getPath());            if (mFile.canRead()) {                BufferedInputStream str = null;                try {                    str = new BufferedInputStream(                            new FileInputStream(mFile), 16*1024);                    map = XmlUtils.readMapXml(str);                } catch (XmlPullParserException e) {                    Log.w(TAG, "getSharedPreferences", e);                } catch (FileNotFoundException e) {                    Log.w(TAG, "getSharedPreferences", e);                } catch (IOException e) {                    Log.w(TAG, "getSharedPreferences", e);                } finally {                    IoUtils.closeQuietly(str);                }            }        } catch (ErrnoException e) {        }        mLoaded = true;        if (map != null) {            mMap = map;            mStatTimestamp = stat.st_mtime;            mStatSize = stat.st_size;        } else {            mMap = new HashMap<String, Object>();        }        notifyAll();    }

startLoadFromDisk启动一个线程,线程调用loadFromDiskLocked(),这个方法首先检测.bak文件是否存在,如果存在就更名为非.bak结尾的文件。然后检测这个文件是否可读:如果可读,读取XML文件的键值对,并保存到Map中。读取完毕以后,调用notifyAll,看到这个,我们马上就会想到wait。看看wait在哪里?

    private void awaitLoadedLocked() {        if (!mLoaded) {            // Raise an explicit StrictMode onReadFromDisk for this            // thread, since the real read will be in a different            // thread and otherwise ignored by StrictMode.            BlockGuard.getThreadPolicy().onReadFromDisk();        }        while (!mLoaded) {            try {                wait();            } catch (InterruptedException unused) {            }        }    }

可以看到只要XML文件的数据没有加载完毕,就阻塞当前线程调用wait()。而这个awaitLoadedLocked()会在所有读取数据的地方都需要调用。
所以这意味着:SharedPreferencesImpl初始化的时候,会异步加载XML文件中保存的数据,然后读取数据的时候,会检查是否加载完毕,如果没有加载完毕,就挂起当前线程等待子线程加载完毕。想一想,如果保存的数据过多,XML文件很大,这个加载过程就会变长。如果在UI线程调用SharedPreferences,那么UI线程可能会阻塞一段时间。所以SharedPreferences如果保存的数据过多,对UI线程是有影响的。

原创粉丝点击