Android SharedPreferences “存入“ 源码解析

        前言:本文是自己对SharedPreferences 存入数据过程源码的分析及总结,欢迎指正。

        一、SharedPreferences存入数据源码解析

        1.SharedPreferences 存入数据过程:

  • 获取 SharedPreferences 对象

  • 通过 Editor 获取编辑器对象

  • 以键值对的形式写入数据

  • 提交修改

//获取SharedPreferences对象
//第一种获取对象方法 -> 通过PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
//第二种获取对象方法 ->通过上下文 Context.getSharedPreferences("指定的文件名称",文件操作模式); 操作模式不做赘述,建议度娘
SharedPreferences mSharedPreferences = getApplicationContext().getSharedPreferences("er_liang",Context.MODE_PRIVATE);

        首先来看下这两种获取对象方式的源码

//第一种在PreferenceManager.java文件下
public static SharedPreferences getDefaultSharedPreferences(Context context) {
        return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
                getDefaultSharedPreferencesMode());
    }
​
//可以看到第一种通过PreferenceManager获取SharedPreferences实例
//这种方法只是将Context().getSharedPreferences("name",MODE)进行了一次封装,将其作为返回值
//通俗易懂的说 就是这种调用方式 是调用了一个”默认“的SharedPreferences对象,文件名称以及操作模式是默认的。
//默认的文件名为上下文的包名+"_preferences"
public static String getDefaultSharedPreferencesName(Context context) {
        return context.getPackageName() + "_preferences";
    }
//默认文件操作模式为MODE_PRIVATE
private static int getDefaultSharedPreferencesMode() {
        return Context.MODE_PRIVATE;
    }    
    
//第二种获取实例方式  Context.java -> 通过上下文Context.getSharedPreferences(参数略)  
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
//其在ContextImpl.java中实现 
public SharedPreferences getSharedPreferences(File file, int mode) {
        SharedPreferencesImpl sp;//sp -> SharedPreferences的实现类 (不要忘了SharedPreferences是个接口)
        synchronized (ContextImpl.class) {
            final ArrayMap cache = getSharedPreferencesCacheLocked();//根据对应的包名获取preferences的缓存
            sp = cache.get(file);//若file在缓存中有,那就取出来
            if (sp == null) { //如果缓存中没有,就new 一个sp对象
                checkMode(mode);
                if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
          //此部分判断是为了防止未解锁时就能使用存储
                    if (isCredentialProtectedStorage()
                            && !getSystemService(UserManager.class)
                                    .isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
                        throw new IllegalStateException("SharedPreferences in credential encrypted "
                                + "storage are not available until after user is unlocked");
                    }
                }
                sp = new SharedPreferencesImpl(file, mode);//new 的对象在这
                cache.put(file, sp);//将对应文件名和sp存入缓存中
                return sp;
            }
        }
    //MODE_MULTI_PROCESS多进程模式或者SDK<11时,就会重新从磁盘加载文件
    //不过多进程模式现在已经不用了 现在有另一个老大哥帮忙协调多进程访问 -> ContentProvider
​
        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);//生成.bak文件
        mMode = mode;//文件访问模式
        mLoaded = false;//标志位,用于判断数据从磁盘加载到内存是否完成
        mMap = null;//保存在内存中SharedPreferences对象的Map容器
        mThrowable = null;
        startLoadFromDisk();//从磁盘中加载文件到内存中
    }
  
  
    private void startLoadFromDisk() {
        synchronized (mLock) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                loadFromDisk();//开启了一个线程去从磁盘中加载文件到内存中
            }
        }.start();
    }
  
  //具体的存入逻辑
   private void loadFromDisk() {
        synchronized (mLock) {
            if (mLoaded) { //如果mLoaded为true代表存入完成->退出
                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;
        Throwable thrown = null;
        try {
            stat = Os.stat(mFile.getPath());
            if (mFile.canRead()) {
                BufferedInputStream str = null;
                try {
          //将SharePreferences文件以流的形式读出来
                    str = new BufferedInputStream(new FileInputStream(mFile), 16 * 1024);
                    map = (Map) 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;
            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;//将上方构建的map赋值给mMap
                        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锁住的数据已经加载完成,数据可以被使用
        //mLock 在SharedPreferencesImpl.java最上边定义了
        //private final Object mLock = new Object(); 
​
            }
        }
    }

        首次自己分析源码,有很多不足之处欢迎指正。

        PS:博客不怎么看评论,欢迎移步公众号 -> "二两仙气儿" 我自己的一些demo和总结 都会放在公众号上。

你可能感兴趣的:(android,java,android-studio)