SharedPreferences的使用和原理分析

简介

SharedPreferences一般用来存储一些简单的数据类型,比如int,String,Boolean

SharedPreferences的内部使用ArrayMap键值对的形式来临时存储数据,最终ArrayMap的数据会通过IO流写入到XML文件中

这个XML文件在手机中的位置是: /data/data/shared_prefs/

1,使用

//获取SharedPreferences 文件名称,模式
SharedPreferences sf = getSharedPreferences("demo",Context.MODE_PRIVATE);
SharedPreferences.Editor mEditor = sf.edit();
//存储
mEditor.putString("name","leidong");
mEditor.putBoolean("man",true);
mEditor.putInt("age",35);
//同步保存到xml文件, //mEditor.apply();另开线程将数据保存在xml
mEditor.commit();

//````````````````````````````
//获取数据
String name = sf.getString("name","");
boolean man = sf.getBoolean("man",true);
int age = sf.getInt("age",0);

Mode:

//私有模式。XML文件每次都覆盖写入
public static final int MODE_PRIVATE = 0x0000;
//文件开放读写权限。不安全,官方已经遗弃这个用法
public static final int MODE_WORLD_READABLE = 0x0001;
public static final int MODE_WORLD_WRITEABLE = 0x0002;

//添加模式。检查文件是否存在,存在就往文件追加内容,不存在就创建新的文件。
public static final int MODE_APPEND = 0x8000
//跨进程模式,官方已经遗弃这个用法
public static final int MODE_MULTI_PROCESS = 0x0004;

2,SharedPreferences 的获取

  //Activity中 name默认为类名
 public SharedPreferences getPreferences(@Context.PreferencesMode int mode) {
        return getSharedPreferences(getLocalClassName(), mode);
    }

  //ContextWrapper中
  public SharedPreferences getSharedPreferences(String name, int mode) {
        return mBase.getSharedPreferences(name, mode);
    }

Activity,Context,ContextWrapper,ContextImpl之间的关系

ContextImpl.java

 private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;

//存放xml文件 File
private ArrayMap<String, File> mSharedPrefsPaths;
 
public SharedPreferences getSharedPreferences(String name, int mode) {

        ......
        
        //xml文件
        File file;
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();
            }
            file = mSharedPrefsPaths.get(name);
            //如果文件不存在,创建文件bignqie保存在mSharedPrefsPaths中
            if (file == null) {
                file = getSharedPreferencesPath(name);
                mSharedPrefsPaths.put(name, file);
            }
        }
        return getSharedPreferences(file, mode);
}

public File getSharedPreferencesPath(String name) {
    return makeFilename(getPreferencesDir(), name + ".xml");
}

private File makeFilename(File base, String name) {
   if (name.indexOf(File.separatorChar) < 0) {
     return new File(base, name);
   }
     throw new IllegalArgumentException(
            "File " + name + " contains a path separator");
}
public SharedPreferences getSharedPreferences(File file, int mode) {
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
            sp = cache.get(file);
            if (sp == null) {

                ......

                sp = new SharedPreferencesImpl(file, mode);
                cache.put(file, sp);
                return sp;
            }
        }
       
               ......

        return sp;
    }

    private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
        if (sSharedPrefsCache == null) {
            sSharedPrefsCache = new ArrayMap<>();
        }

        final String packageName = getPackageName();
        ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
        if (packagePrefs == null) {
            packagePrefs = new ArrayMap<>();
            sSharedPrefsCache.put(packageName, packagePrefs);
        }

        return packagePrefs;
    }
SharedPreferencesImpl.java
//标记作用,xml是否已经被加载到内存中
private boolean mLoaded = false; 
//构造函数,初始化,创建备份文件mBackupFile ,读取磁盘中的xml文件
SharedPreferencesImpl(File file, int mode) {
    mFile = file;
    mBackupFile = makeBackupFile(file);//备份文件
    mMode = mode;
    mLoaded = false;
    mMap = null;
    mThrowable = null;
    startLoadFromDisk();
}
//起一个新线程,加载磁盘的xml文件内容
private void startLoadFromDisk() {
    synchronized (mLock) {
        mLoaded = false;
    }
    new Thread("SharedPreferencesImpl-load") {
        public void run() {
            loadFromDisk();
        }
    }.start();
}
//
private void loadFromDisk() {
    synchronized (mLock) {
        //如果xml已经被加载过,直接返回
        if (mLoaded) {
            return;
        }
        //如果备份存在,说明之前读写操作被中断,把mBackupFile当成mFile来用,直接使用备份文件中的内容。
        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<String, Object> map = null;
    StructStat stat = null;
    Throwable thrown = null;
    try {
        stat = Os.stat(mFile.getPath());
        if (mFile.canRead()) {
            BufferedInputStream str = null;
            try {
                str = new BufferedInputStream(
                        new FileInputStream(mFile), 16 * 1024);
                map = (Map<String, Object>) XmlUtils.readMapXml(str);//读取xml中的内容到内存中,赋值给map
            } catch (Exception e) {
                Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
            } finally {
                IoUtils.closeQuietly(str);
            }
        }
    } catch (ErrnoException e) {
        // An errno exception means the stat failed. Treat as empty/non-existing by
        // ignoring.
    } catch (Throwable t) {
        thrown = t;
    }

    synchronized (mLock) {
        mLoaded = true;//标记为已经加载过xml文件
        mThrowable = thrown;

        // It's important that we always signal waiters, even if we'll make
        // them fail with an exception. The try-finally is pretty wide, but
        // better safe than sorry.
        try {
            if (thrown == null) {
                if (map != null) {
                    mMap = map;
                    mStatTimestamp = stat.st_mtim;
                    mStatSize = stat.st_size;
                } else {
                    mMap = new HashMap<>();
                }
            }
            // In case of a thrown exception, we retain the old map. That allows
            // any open editors to commit and store updates.
        } catch (Throwable t) {
            mThrowable = t;
        } finally {
            mLock.notifyAll();//唤醒其他正在等待mLock锁的进程
        }
    }
}
//获取SharedPreferences中保存的键值对数据
public int getInt(String key, int defValue) {
    synchronized (mLock) {
        awaitLoadedLocked();//如果mLoaded为false,线程会一直处于等待的状态
        Integer v = (Integer)mMap.get(key);//获取读取到内存中的值(mMap键值对)
        return v != null ? v : defValue;
    }
}

SharedPreferences.Editor对数据的存储操作

private final Map<String, Object> mModified = new HashMap<>();

public Editor edit() {
   
    //如果该线程获取到了mLock对象锁,但是mLoaded为false,也就是加载xml过程没结束,那么线程会一直等待
    synchronized (mLock) {
        awaitLoadedLocked();
    }

    return new EditorImpl();
}
//如果mLoaded为false,线程会一直处于等待的状态
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 {
            mLock.wait();
        } catch (InterruptedException unused) {
        }
    }
    if (mThrowable != null) {
        throw new IllegalStateException(mThrowable);
    }
}

//存储的键值对数据只是保存到了内部类Editor的mModified集合中
public Editor putInt(String key, int value) {
    synchronized (mEditorLock) {
        mModified.put(key, value);
        return this;
    }
}

public boolean commit() {
    long startTime = 0;

    if (DEBUG) {
        startTime = System.currentTimeMillis();
    }
    //把内部类Editor的mModified集合中的数据保存到SharedPreferencesImpl类的mMap集合中。
    MemoryCommitResult mcr = commitToMemory();
    //把mMap集合中的数据写入到xml文件中
    SharedPreferencesImpl.this.enqueueDiskWrite(
        mcr, null /* sync write on this thread okay */);
    try {
        mcr.writtenToDiskLatch.await();
    } catch (InterruptedException e) {
        return false;
    } finally {
        if (DEBUG) {
            Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                    + " committed after " + (System.currentTimeMillis() - startTime)
                    + " ms");
        }
    }
    notifyListeners(mcr);//通知注册监听者
    return mcr.writeToDiskResult;
}

public void apply() {
    final long startTime = System.currentTimeMillis();
    //把内部类Editor的mModified集合中的数据保存到SharedPreferencesImpl类的mMap集合中。
    final MemoryCommitResult mcr = commitToMemory();
    final Runnable awaitCommit = new Runnable() {
            @Override
            public void run() {
                try {
                    mcr.writtenToDiskLatch.await();
                } catch (InterruptedException ignored) {
                }

                if (DEBUG && mcr.wasWritten) {
                    Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                            + " applied after " + (System.currentTimeMillis() - startTime)
                            + " ms");
                }
            }
        };

    QueuedWork.addFinisher(awaitCommit);

    Runnable postWriteRunnable = new Runnable() {
            @Override
            public void run() {
                awaitCommit.run();
                QueuedWork.removeFinisher(awaitCommit);
            }
        };
    //把mMap集合中的数据写入到xml文件中
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);

    // Okay to notify the listeners before it's hit disk
    // because the listeners should always get the same
    // SharedPreferences instance back, which has the
    // changes reflected in memory.
    notifyListeners(mcr);//通知注册监听者
}
//把mMap集合中的数据写入到xml文件中。
private void enqueueDiskWrite(final MemoryCommitResult mcr,
                              final Runnable postWriteRunnable) {
    //如果是apply()形式,则postWriteRunnable != null,isFromSyncCommit 为true。
    final boolean isFromSyncCommit = (postWriteRunnable == null);

    final Runnable writeToDiskRunnable = new Runnable() {
            @Override
            public void run() {
                synchronized (mWritingToDiskLock) {
                    writeToFile(mcr, isFromSyncCommit);//写入磁盘xml的操作被封装在新起的线程中
                }
                synchronized (mLock) {
                    mDiskWritesInFlight--;
                }
                if (postWriteRunnable != null) {
                    postWriteRunnable.run();
                }
            }
        };

    // Typical #commit() path with fewer allocations, doing a write on
    // the current thread.
    //如果是commit()的形式,且当前没有写磁盘任务(mDiskWritesInFlight == 1),则直接调用
    //writeToDiskRunnable.run()执行writeToFile()写入操作,不起新线程。
    if (isFromSyncCommit) {
        boolean wasEmpty = false;
        synchronized (mLock) {
            wasEmpty = mDiskWritesInFlight == 1;
        }
        if (wasEmpty) {
            writeToDiskRunnable.run();
            return;
        }
    }
    //如果是apply()的形式,所有的线程都加入到QueuedWork中,以队列的形式保存,逐个线程启动
    QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}

猜你喜欢

转载自blog.csdn.net/qq_32671919/article/details/84585909
今日推荐