SharedPreferences 源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zgkxzx/article/details/84231218

使用用例

    //获得SharedPreferences的实例 sp_name是文件名
    SharedPreferences sp = getSharedPreferences("sp_name", Context.MODE_PRIVATE);
    //获得Editor 实例
    SharedPreferences.Editor editor = sp.edit();
    //以key-value形式保存数据
    editor.putString("key", "data");
    //apply()是异步写入数据
    editor.apply();
    //commit()是同步写入数据
    //editor.commit();

源码分析

** getSharedPreferences **
本来是Context的方法,但是Context是抽象类,具体要看他的实现类 ContextImpl

//存储SP名字与路径文件关系的表结构
private ArrayMap<String, File> mSharedPrefsPaths;

public SharedPreferences getSharedPreferences(String name, int mode) {
        // 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";
            }
        }

        File file;
        synchronized (ContextImpl.class) {
            //如果表为空 ,创建表
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();
            }
            //通过名字 得到文件
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
                file = getSharedPreferencesPath(name);
                //存入表中
                mSharedPrefsPaths.put(name, file);
            }
        }
        //返回SharedPreferences对象
        return getSharedPreferences(file, mode);
}
//返回SharedPreferences对象    
public SharedPreferences getSharedPreferences(File file, int mode) {
            checkMode(mode);
            SharedPreferencesImpl sp;
            synchronized (ContextImpl.class) {
                final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
                sp = cache.get(file);
                if (sp == null) {
                    //返回SharedPreferences的实现类SharedPreferencesImpl
                    sp = new SharedPreferencesImpl(file, mode);
                    cache.put(file, 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(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() {
                loadFromDisk();
            }
        }.start();
}
//从磁盘中加载数据
private void loadFromDisk() {
        synchronized (SharedPreferencesImpl.this) {
            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 {
                  //文件转化成BufferedInputStream
                    str = new BufferedInputStream(
                            new FileInputStream(mFile), 16*1024);
                    //从流里面读取数据转化成Map
                    map = XmlUtils.readMapXml(str);
                } catch (XmlPullParserException | IOException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } finally {
                    IoUtils.closeQuietly(str);
                }
            }
        } catch (ErrnoException e) {
            /* ignore */
        }

        synchronized (SharedPreferencesImpl.this) {
            mLoaded = true;
            if (map != null) {
                mMap = map;
                mStatTimestamp = stat.st_mtime;
                mStatSize = stat.st_size;
            } else {
                mMap = new HashMap<>();
            }
            notifyAll();
        }
}
//通过key在map里面获取Value
public String getString(String key, String defValue) {
        synchronized (this) {
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

看看怎么写数据的

public Editor edit() {
        synchronized (this) {
            awaitLoadedLocked();
        }
        //返回Editor的实现类
        return new EditorImpl();
}
  private final Map<String, Object> mModified = Maps.newHashMap();
  private boolean mClear = false;

  public Editor putString(String key, @Nullable String value) {
      synchronized (this) {
          //放到表里面
          mModified.put(key, value);
          return this;
      }
  }

然后Commit()

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

看看异步提交方法

public void apply() {
      //先写入内存中
      final MemoryCommitResult mcr = commitToMemory();
      final Runnable awaitCommit = new Runnable() {
                   public void run() {
                       try {
                           mcr.writtenToDiskLatch.await();
                       } catch (InterruptedException ignored) {
                       }
                   }
      };

      QueuedWork.add(awaitCommit);

      Runnable postWriteRunnable = new Runnable() {
                   public void run() {
                       awaitCommit.run();
                       QueuedWork.remove(awaitCommit);
                   }
      };
      //异步写入磁盘
      SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
      notifyListeners(mcr);
}

扩展

** readMapXml **

public static final HashMap<String, ?> readMapXml(InputStream in)
    throws XmlPullParserException, java.io.IOException
    {
        //创建一个XML解析器
        XmlPullParser   parser = Xml.newPullParser();
        parser.setInput(in, null);
        return (HashMap<String, ?>) readValueXml(parser, new String[1]);
    }
//读取XML里面数据
public static final Object readValueXml(XmlPullParser parser, String[] name)
        throws XmlPullParserException, java.io.IOException
        {
            int eventType = parser.getEventType();
            do {
                if (eventType == parser.START_TAG) {
                    return readThisValueXml(parser, name, null);
                } else if (eventType == parser.END_TAG) {
                    throw new XmlPullParserException(
                        "Unexpected end tag at: " + parser.getName());
                } else if (eventType == parser.TEXT) {
                    throw new XmlPullParserException(
                        "Unexpected text: " + parser.getText());
                }
                eventType = parser.next();
            } while (eventType != parser.END_DOCUMENT);

            throw new XmlPullParserException(
                "Unexpected end of document");
}

结论:

  1. SharedPreferences读取xml文件时,会以DOM方式解析(把整个xml文件直接加载到内存中解析),在调用getXXX()方法时取到的是内存中的数据,方法执行时会有个锁来阻塞,目的是等待文件加载完毕,没加载完成之前会wait()。
  2. SharedPreferences写文件时,如果调用的commit(),会将数据同步写入内存中,内存数据更新,再同步写入磁盘中;如果调用的apply(),会将数据同步写入内存中,内存数据更新,然后异步写人磁盘,也就是说可能写磁盘操作还没有完成就直接返回了。在主线程中建议使用apply(),因为同步写磁盘,当文件较大时,commit()会等到写磁盘完成再返回,可能会有ANR问题。
  3. SP第一次初始化到读取到数据存在一定延迟,因为需要到文件中读取数据,因此可能会对UI线程流畅度造成一定影响

猜你喜欢

转载自blog.csdn.net/zgkxzx/article/details/84231218
今日推荐