Android SharedPreferences解析

引:在使用SharedPreferences过程中,觉得Android这种轻量级的存储思想用起来挺方便的.本着知其然知其所以然的想法.尝试阅读SharedPreferences源码

入口函数

public SharedPreferences getPreferences(int mode) {        return getSharedPreferences(getLocalClassName(), mode);    }
public SharedPreferences getSharedPreferences(String name, int mode) {    SharedPreferencesImpl sp;    synchronized (sSharedPrefs) {        sp = sSharedPrefs.get(name);        if (sp == null) {            File prefsFile = getSharedPrefsFile(name);            sp = new SharedPreferencesImpl(prefsFile, mode);            sSharedPrefs.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;    }

很简单的两个入口函数.

public SharedPreferences getPreferences(int mode)

定义在Activity.class类

 public SharedPreferences getSharedPreferences(String name, int mode) 

定义在ContextImpl.class类

两者之间的关系,getPreferences通过调用getSharedPreferences来实现.

public SharedPreferences getPreferences(int mode) {        return getSharedPreferences(getLocalClassName(), mode);    }
public String getLocalClassName() {        final String pkg = getPackageName();        final String cls = mComponent.getClassName();        int packageLen = pkg.length();        if (!cls.startsWith(pkg) || cls.length() <= packageLen                || cls.charAt(packageLen) != '.') {            return cls;        }        return cls.substring(packageLen+1);    }

getPreferences(int mode)函数通过调用getLocalClassName()传递的是类名进去.然而他们两区别就是这里了.

getSharedPreferences函数

getSharedPreferences函数返回的是一个SharedPreferencesImpl实例.使用过SharedPreferences的应该都知道,我们操作都是通过SharedPreferences实例进行操作的.

SharedPreferencesImpl继承SharedPreferences

ContextImpl.class有个sSharedPrefs静态变量.sSharedPrefs保存的是键值对.每当调用getSharedPreferences函数首先会用name查询sSharedPrefs里面有没保存对于的SharedPreferencesImpl实例.如果有那么直接返回name对于的SharedPreferencesImpl实例.如果没找到那么创建一个SharedPreferencesImpl实例并且已键值对的形式插入到sSharedPrefs.以便以后在通过name获取SharedPreferencesImpl实例.

  • 也就说调用getSharedPreferences函数传递进来的name是一个索引.通过索引返回SharedPreferencesImpl实例.
private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =            new HashMap<String, SharedPreferencesImpl>();
public SharedPreferences getSharedPreferences(String name, int mode) {        SharedPreferencesImpl sp;        synchronized (sSharedPrefs) {            //通过name查找SharedPreferencesImpl实例            sp = sSharedPrefs.get(name);            if (sp == null) {                //为空,第一次读取.没有根据name产生SharedPreferencesImpl实例                //getSharedPrefsFile()构造Xml文件File                File prefsFile = getSharedPrefsFile(name);                //创建SharedPreferencesImpl实例,默认在”/data/data/包名/shared_prefs/”目录里面创建XML文件                sp = new SharedPreferencesImpl(prefsFile, mode);                //保存SharedPreferencesImpl实例到sSharedPrefs,name作为索引                sSharedPrefs.put(name, sp);                return sp;            }        }        //这部分忽略,Context.MODE_MULTI_PROCESS getSharedPreferences是用于跨线程        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;

接下来这三个很好理解,getSharedPrefsFile就是返回Xml文件的File.所以说SharedPreferences的实现是基于Xml文件.从这里可以看出.

  • 例如:
  • 会生成xml文件
  • /data/data/com.dsliang.SharedPreferencesDemo/shared_prefs/ds.xml
 public File getSharedPrefsFile(String name) {        return makeFilename(getPreferencesDir(), name + ".xml");    }
private File getPreferencesDir() {        synchronized (mSync) {            if (mPreferencesDir == null) {                mPreferencesDir = new File(getDataDirFile(), "shared_prefs");            }            return mPreferencesDir;        }    }
    private File getDataDirFile() {        if (mPackageInfo != null) {            return mPackageInfo.getDataDirFile();        }        throw new RuntimeException("Not supported in system context");    }

SharedPreferencesImpl类

理解了SharedPreferencesImpl类基本对SharedPreferences就明白一大半了.

构造函数

SharedPreferencesImpl(File file, int mode) {        //Xml文件的File        mFile = file;        //Xml文件的副本(备份)        mBackupFile = makeBackupFile(file);        //SharedPreferences模式        mMode = mode;        //是否加载文件内容到内存(加载到sSharedPrefs)        mLoaded = false;        //SharedPreferences键值对哈希表,最终获取数据就是在这表里面获取        mMap = null;        //异步读取文件并且保存数据到sSharedPrefs        startLoadFromDisk();    }
    private void startLoadFromDisk() {        synchronized (this) {            //标识文件未读取到内存            //在getString/getBoolean一系列的读取函数中会调用awaitLoadedLocked()函数会判断mLoaded.false - awaitLoadedLocked()函数阻塞等待mLoaded为true.            //还没读取完Xml文件哪里来读取呢?关键就在这变量.没赋值为true之前所有读取操作都是阻塞等待.            mLoaded = false;        }        //子线程执行读取Xml文件操作        new Thread("SharedPreferencesImpl-load") {            public void run() {                synchronized (SharedPreferencesImpl.this) {                    loadFromDiskLocked();                }            }        }.start();    }    private void loadFromDiskLocked() {        if (mLoaded) {            return;        }        //如果存在Xml备份文件,删除Xml文件.用备份文件替换.        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;        FileStatus stat = new FileStatus();        if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {            try {                BufferedInputStream str = new BufferedInputStream(                        //通过XmlPull解析Xml文件                        new FileInputStream(mFile), 16*1024);                //返回的数据是Map.有兴趣可以进去看看.里面很多递归调用,看到头都晕.知道是解析Xml文件并且返回Map就可以了.                map = XmlUtils.readMapXml(str);                str.close();            } catch (XmlPullParserException e) {                Log.w(TAG, "getSharedPreferences", e);            } catch (FileNotFoundException e) {                Log.w(TAG, "getSharedPreferences", e);            } catch (IOException e) {                Log.w(TAG, "getSharedPreferences", e);            }        }        //到这里文件已经读取出来并且保持到内存当中.        mLoaded = true;        if (map != null) {            //mMap,SharedPreferencesImpl内部用来存放Map的实例            mMap = map;            //文件读取时间            mStatTimestamp = stat.mtime;            //文件大小            mStatSize = stat.size;        } else {            //文件不存在创建一个空的Map            mMap = new HashMap<String, Object>();        }        //通知等待线程        notifyAll();    }

SharedPreferencesImpl类解析,在构造函数会创建子线程读取Xml文件解析成Map保存到SharedPreferencesImpl类内部的mMap.在读取解析Xml文件这过程当中对该SharedPreferencesImpl实例进行操作都会阻塞.(awaitLoadedLocked()函数导致)
可以知道SharedPreferences是会将Xml文件的全部数据都读取到内存.

读取数据

在SharedPreferencesImpl.class内部,mMap哈希表用来存放SharedPreferences键值对.

 private Map<String, Object> mMap; 

getInt/getLong/getString等.
这一系列函数本质上没多少区别,就是通过key在mMap查询,查不到责返回默认值.

public String getString(String key, String defValue) {        synchronized (this) {            awaitLoadedLocked();            String v = (String)mMap.get(key);            return v != null ? v : defValue;        }public int getInt(String key, int defValue) {        synchronized (this) {            awaitLoadedLocked();            Integer v = (Integer)mMap.get(key);            return v != null ? v : defValue;        }    }    public long getLong(String key, long defValue) {        synchronized (this) {            awaitLoadedLocked();            Long v = (Long)mMap.get(key);            return v != null ? v : defValue;        }    }
 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) {            }        }    }

读取数据函数主要是要关注awaitLoadedLocked()函数.如果文件还没加载完成会导致操作阻塞.

修改数据-Editor类

调用SharedPreferencesImpl类的edit()方法返回EditorImpl类.EditorImpl类封装对数据操作的函数.putString/putBoolean等一系列方法.这里需要注意几个问题.

  • putString/putBoolean等一系列方法操作EditorImpl类内部的一个Map并不是操作SharedPreferencesImpl类的Map.所以要生效是需要调用EditorImpl类的commit()方法.
  • EditorImpl类的clear(0方法是清空EditorImpl类内部的Map实例.
    以下操作不会导致SharedPreferences清楚数据.
edit().clear().commit();
  • 正确姿势如下:
edit().remove("Save")..remove("PassWord")commit();
    public Editor edit() {        // TODO: remove the need to call awaitLoadedLocked() when        // requesting an editor. will require some work on the        // Editor, but then we should be able to do:        //        // context.getSharedPreferences(..).edit().putString(..).apply()        //        // ... all without blocking.        synchronized (this) {            awaitLoadedLocked();        }        return new EditorImpl();    }

最后就是commit()方法源代码.

    //记录Commit()方法结果的类    private static class MemoryCommitResult {        //数据是否需要更新        public boolean changesMade;  // any keys different?        //Map实例        public List<String> keysModified;  // may be null        //回调函数,有可能为null        public Set<OnSharedPreferenceChangeListener> listeners;  // may be null        public Map<?, ?> mapToWriteToDisk;        public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);        //结果        public volatile boolean writeToDiskResult = false;        public void setDiskWriteResult(boolean result) {            writeToDiskResult = result;            writtenToDiskLatch.countDown();        }    }// Returns true if any changes were made        private MemoryCommitResult commitToMemory() {            MemoryCommitResult mcr = new MemoryCommitResult();            synchronized (SharedPreferencesImpl.this) {                // We optimistically don't make a deep copy until                // a memory commit comes in when we're already                // writing to disk.                //mDiskWritesInFlight等处理的提交个数                if (mDiskWritesInFlight > 0) {                    // We can't modify our mMap as a currently                    // in-flight write owns it. Clone it before                    // modifying it.                    // noinspection unchecked                    mMap = new HashMap<String, Object>(mMap);                }                //SharedPreferences的Map保存到mcr.mapToWriteToDisk                mcr.mapToWriteToDisk = mMap;                mDiskWritesInFlight++;                //回调函数                boolean hasListeners = mListeners.size() > 0;                if (hasListeners) {                    mcr.keysModified = new ArrayList<String>();                    mcr.listeners =                            new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());                }                synchronized (this) {                    //是否有清空,调用clear()以后设置mClear=true.就是当调用clear()以后在put数据进去也不会生效.                    if (mClear) {                        if (!mMap.isEmpty()) {                            mcr.changesMade = true;                            mMap.clear();                        }                        mClear = false;                    }                    //处理edit的Map和SharedPreferences的Map                    for (Map.Entry<String, Object> e : mModified.entrySet()) {                        String k = e.getKey();                        Object v = e.getValue();                        //调用romve以后,Value会设置成this.用来标识此key需要删除.很犀利的技巧.如果不设置没法知道哪一个需要删除.                        if (v == this) {  // magic value for a removal mutation                            if (!mMap.containsKey(k)) {                                continue;                            }                            //从SharedPreferences的Map移除                            mMap.remove(k);                        } else {                            boolean isSame = false;                            if (mMap.containsKey(k)) {                                Object existingValue = mMap.get(k);                                if (existingValue != null && existingValue.equals(v)) {                                    continue;                                }                            }                            //键值对插入SharedPreferences的Map                            mMap.put(k, v);                        }                        //设置修改位,标志已经是脏的.需要更新                        mcr.changesMade = true;                        if (hasListeners) {                            mcr.keysModified.add(k);                        }                    }                    //edit的Map已经处理完.可以清空.此时SharedPreferences的Map数据已经是最新的                    mModified.clear();                }            }            return mcr;        }    private void enqueueDiskWrite(final MemoryCommitResult mcr,                                  final Runnable postWriteRunnable) {        final Runnable writeToDiskRunnable = new Runnable() {                public void run() {                    synchronized (mWritingToDiskLock) {                        writeToFile(mcr);                    }                    synchronized (SharedPreferencesImpl.this) {                        mDiskWritesInFlight--;                    }                    if (postWriteRunnable != null) {                        postWriteRunnable.run();                    }                }            };        /.通过commit()方法调用,此处isFromSyncCommit为true        final boolean isFromSyncCommit = (postWriteRunnable == null);        // Typical #commit() path with fewer allocations, doing a write on        // the current thread.        if (isFromSyncCommit) {            boolean wasEmpty = false;            synchronized (SharedPreferencesImpl.this) {                wasEmpty = mDiskWritesInFlight == 1;            }            if (wasEmpty) {                //回写                writeToDiskRunnable.run();                return;            }        }        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);    }    private void writeToFile(MemoryCommitResult mcr) {        // Rename the current file so it may be used as a backup during the next read        if (mFile.exists()) {            if (!mcr.changesMade) {                // If the file already exists, but no changes were                // made to the underlying map, it's wasteful to                // re-write the file. Return as if we wrote it                // out.                mcr.setDiskWriteResult(true);                return;            }            if (!mBackupFile.exists()) {                if (!mFile.renameTo(mBackupFile)) {                    Log.e(TAG, "Couldn't rename file " + mFile                          + " to backup file " + mBackupFile);                    mcr.setDiskWriteResult(false);                    return;                }            } else {                //如果备份Xml文件存在,把mFile删除就可以了.                mFile.delete();            }        }        // Attempt to write the file, delete the backup and return true as atomically as        // possible. If any exception occurs, delete the new file; next time we will restore        // from the backup.        try {            FileOutputStream str = createFileOutputStream(mFile);            if (str == null) {                mcr.setDiskWriteResult(false);                return;            }            //回写到文件            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);            //刷新            FileUtils.sync(str);            str.close();               //设置权限ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);            FileStatus stat = new FileStatus();            if (FileUtils.getFileStatus(mFile.getPath(), stat)) {                synchronized (this) {                    mStatTimestamp = stat.mtime;                    mStatSize = stat.size;                }            }            // Writing was successful, delete the backup file if there is one.            //成功后,把备份文件删除            mBackupFile.delete();            mcr.setDiskWriteResult(true);            return;        } catch (XmlPullParserException e) {            Log.w(TAG, "writeToFile: Got exception:", e);        } catch (IOException e) {            Log.w(TAG, "writeToFile: Got exception:", e);        }        // Clean up an unsuccessfully written file        if (mFile.exists()) {            if (!mFile.delete()) {                Log.e(TAG, "Couldn't clean up partially-written file " + mFile);            }        }        mcr.setDiskWriteResult(false);    }}public boolean commit() {            //提交修改            MemoryCommitResult mcr = commitToMemory();            //这里就是回写函数            SharedPreferencesImpl.this.enqueueDiskWrite(                mcr, null /* sync write on this thread okay */);            try {                mcr.writtenToDiskLatch.await();            } catch (InterruptedException e) {                return false;            }            notifyListeners(mcr);            return mcr.writeToDiskResult;        }        private void notifyListeners(final MemoryCommitResult mcr) {            if (mcr.listeners == null || mcr.keysModified == null ||                mcr.keysModified.size() == 0) {                return;            }            if (Looper.myLooper() == Looper.getMainLooper()) {                for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {                    final String key = mcr.keysModified.get(i);                    for (OnSharedPreferenceChangeListener listener : mcr.listeners) {                        if (listener != null) {                            listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);                        }                    }                }            } else {                // Run this function on the main thread.                ActivityThread.sMainThreadHandler.post(new Runnable() {                        public void run() {                            notifyListeners(mcr);                        }                    });            }        }    }

SharedPreferences代码总结

SharedPreferences本质是通过Xml文件实现.使用的时候会把Xml文件解析成Map保存到内存中.要知道它会将Xml文件全部数据都解析到内存当中.免不了会占用内存.其次对SharedPreferences修改都会导致整个Xml文件再次回写.

更多相关文章

  1. 箭头函数的基础使用
  2. NPM 和webpack 的基础使用
  3. Python技巧匿名函数、回调函数和高阶函数
  4. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  5. 浅析android下如何通过jni监控wifi网络连接、dhcpcd执行和power
  6. android布局文件中的include
  7. android 播放视频
  8. Android上传文件到Web服务器,PHP接收文件(二)
  9. Android(安卓)Studio中使用NDK

随机推荐

  1. Mysql 使用 select into outfile
  2. 检索每n行的最高值
  3. 如何在C中删除多个闪存地址?
  4. MySQL数据库引擎ISAM MyISAM HEAP InnoDB
  5. mysql 数据库中表不同 但是列名相同,怎么
  6. SQL从结果数据库中选择subCode及其得分,并
  7. 什么时候最推荐使用mysql_real_escape_st
  8. Atitit 数据库 标准库 &#160;sdk 函数库
  9. mysql如何导出HTML?
  10. wampserver无法启动之mysql疑难杂症