说在前面:SharedPreferences是Android中几种重要的存储数据的方式,Android开发不会没有人历来没有使用过,可是却很好人会关注它是怎么实现的,确实SharedPreferences实现起来比较简单,本质是基于文件存储,格式是XML形势的,本篇文章并无太深的技术,但绝对是不可缺乏的知识。java
SharedPreferences能够经过Context获取到,它是一个接口,实如今frameworksbasecorejavaandroidappSharedPreferencesImpl.javaandroid
源码也仅仅只有630行,相对于庞大的Android源码,小不少。可是你真的了解它吗?缓存
一、不一样的Activity里面Context.getSharedPreferences()获取的SharedPreferences相同吗?多线程
二、支持并发吗?并发
三、SharedPreferences.putXXX()直接保存了吗?app
......ide
针对上面的问题,咱们一点点来分析。函数
咱们得到SharedPreferences是经过Context.getSharedPreferences(),源码以下:post
@Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; synchronized (ContextImpl.class) { // 静态的 全局惟一,缓存获得的SharedPreferences,key是app包名 if (sSharedPrefs == null) { sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>(); } // 经过每个app的包名缓存不一样的SharedPreferences,key是文件名 // 这里就明白前面提到的第一个问题了,一个文件对应一个SharedPreferences final String packageName = getPackageName(); ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName); if (packagePrefs == null) { packagePrefs = new ArrayMap<String, SharedPreferencesImpl>(); sSharedPrefs.put(packageName, packagePrefs); } // 略... // 若是以建立过,直接从缓存中获取返回 sp = packagePrefs.get(name); if (sp == null) { // 若是没有建立一个 文件名为data/data/packege/shared_prefs/xxx.xml File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); // 建立实例对象 packagePrefs.put(name, sp); // 放到缓存中 return sp; } } // 略... return sp; }
SharedPreferences的建立很简单,咱们先看看构造方法:ui
// 仅有一个 SharedPreferencesImpl(File file, int mode) { mFile = file; mBackupFile = makeBackupFile(file); // 建立备份 mMode = mode; mLoaded = false; mMap = null; // 存放键值对的map startLoadFromDisk(); // 从本地load出来 }
从构造函数咱们不难看出,SharedPreferences实际是以Map(HashMap)存储数据,建立的时候就从本地文件中读取数据到内存中,因此,不能存储太多的数据 ,不然内存会爆掉的哦。
当实例化的时候就会触发,一个读取本地已存储的数据,
private void loadFromDiskLocked() { // 读取过就不在读取 if (mLoaded) { return; } //... 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); // 格式为xml,解析xml获得map 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) { } // 加载事后更改状态,若是没有建立一个HashMap mLoaded = true; if (map != null) { mMap = map; mStatTimestamp = stat.st_mtime; mStatSize = stat.st_size; } else { mMap = new HashMap<String, Object>(); } notifyAll(); }
获得数据一般是getXXX(),实际很简单,就是存map中取数据。
// 已getString()为例,其余同样 @Nullable public String getString(String key, @Nullable String defValue) { // 同步哦 synchronized (this) { // 确保,已从文件中读取过数据 awaitLoadedLocked(); // 从map中的获得数据 String v = (String)mMap.get(key); return v != null ? v : defValue; } }
你们都知道保存数据是经过Editor来存储的,事实上也确实是这样的。可是putXXX()仅仅是保存到了map中,正真的是要在commit()或apply()以后。
public Editor putString(String key, @Nullable String value) { // 同步哦 synchronized (this) { mModified.put(key, value); return this; } }
一、
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 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(); } } }; // ... QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); }
三、
// Note: must hold mWritingToDiskLock private void writeToFile(MemoryCommitResult mcr) { // ... 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); try { // 更新文件的状态 final StructStat stat = Os.stat(mFile.getPath()); synchronized (this) { mStatTimestamp = stat.st_mtime; mStatSize = stat.st_size; } } catch (ErrnoException e) { // Do nothing } // 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); }
一、SharedPreferences有缓存
二、数据是经过map来快速访问,因此不用担忧文件IO耗时
三、支持多线程并发
四、本质是基于文件xml的形式