SharedPreference小结

SharedPreference小结

特点
  • 轻量级的数据存储方式,其内部是以xml的结构保存,以键值对的形式进行存储,文件位于data/data/包名/shared_prefs目录下
  • 只支持基本数据类型,不支持自定义数据类型

       <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map>
        <float name="isFloat" value="1.5" />
        <string name="isString">Android</string>
        <int name="isInt" value="1" />
        <long name="isLong" value="1000" />
        <boolean name="isBoolean" value="true" />
    
    </map>
    
1.初始化
  • Context是一个抽象类,其核心实现类是ContextImpl,其中getSharedPreference()有两个参数,第一个参数为文件名,可随意取,无需后缀;第二个参数为模式,分为四种:Context.MODE_PRIVATE(只能该应用读写),Context.MODE_WORLD_READABLE(可以被其他应用读取,但不可写入),Context.MODE_WORLD_WRITEABLE(可被其他应用读写),Context.MODE_APPEND(检查文件是否存在,存在直接追加内容,不存在则直接创建)

       @Override
        public SharedPreferences getSharedPreferences(String name, int mode) {
            SharedPreferencesImpl sp;
            synchronized (ContextImpl.class) {
                if (sSharedPrefs == null) {
                    // sSharedPrefs 是 ContextImpl 的静态成员变量,通过 Map 维护着当前包名下的 SP Map 集合
                    sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
                }
    
                final String packageName = getPackageName();
                ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
                if (packagePrefs == null) {
                    packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
                    sSharedPrefs.put(packageName, packagePrefs);
                }
    
                // 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) {
                    // name 参数为 null 时,文件名使用 null.xml
                    if (name == null) {
                        name = "null";
                    }
                }
    
                sp = packagePrefs.get(name);
                if (sp == null) {
                    // SP 集合是一个以 SP 的名字为 key , SP 为值的 Map
                    File prefsFile = getSharedPrefsFile(name);
                    // SP 的实现类是 SharedPreferencesImpl
                    sp = new SharedPreferencesImpl(prefsFile, mode);
                    packagePrefs.put(name, sp);
                    return sp;
                }
            }
            // Android 3.0 以下或者支持 MODE_MULTI_PROCESS 模式时,如果文件被改动,就重新从文件读取,实现多进程数据同步,但是实际使用中效果不佳,可能会有很多坑。
            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;
        }
    
    • 说明:

       *  从这里我们看出:sSharedPrefs为一个ArrayMap,其key为包名,value为一个Arraymap,而此arraymap的key为文件名,value为sharedpreference。
       *  首先会判断sSharedPrefs是否为空,为空则创建一个
       *  然后获取包名,通过包名来获取对应的arraymap,若为空则创建一个,反之返回对应的arraymap
       *  之后通过文件名获取对应的SharedPreferencesImpl若为空则创建一个,反之返回对应的SharedPreferencesImpl
       *  通过SharedPreferencesImpl创建对应的xml的文件
      
    • SharePreference是由SharedPreferenceImpl返回的,我们来看看SharedPreferenceImpl

      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() {
                          synchronized (SharedPreferencesImpl.this) {
                              loadFromDiskLocked();
                          }
                      }
                  }.start();
              }
      
           private void loadFromDiskLocked() {
                  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 {
                              str = new BufferedInputStream(
                                      new FileInputStream(mFile), 16*1024);
                              // 从 XML 里面读取数据返回一个 Map,内部使用了 XmlPullParser
                              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) {
                  }
                  mLoaded = true;
                  if (map != null) {
                      mMap = map;
                      mStatTimestamp = stat.st_mtime;
                      mStatSize = stat.st_size;
                  } else {
                      mMap = new HashMap<String, Object>();
                  }
                   // 唤醒等待的线程,到这文件读取完毕
                  notifyAll();
          }
      
    • 说明

      1.mBackupFile 定义了备份文件,该文件用于在读取数据时,当上次写入数据失败时,直接从该文件读取
      2.内部使用xmlPullParser读取xml文件数据
      
2.读数据
  • 首先会加入SharedPreferencesImpl 对象锁,然后同步等待从磁盘加载数据完成,之后才将数据返回,因为当存储的数据比较大,那么在读取数据的时候会阻塞线程,所以建议尽量保存较小的数据。

           @Nullable
        public String getString(String key, @Nullable String defValue) {
              synchronized (this) {
                  awaitLoadedLocked();
                  String v = (String)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) {
                  }
              }
        }
    
写数据
  • SharedPreferences只能用于读取数据,写入数据是通过Editor完成的
  • Editor是一个接口,其实现类为EditorImpl,是sharedpreference的内部类
  • 通过sharedpreference的edit方法返回一个EditorImpl对象

       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();
    }
    
  • 在put过程中mModified是一个map,他将要修改的数据先存储起来,之后调用commit或apply进行保存

      public Editor putString(String key, @Nullable String value) {
          synchronized (this) {
              mModified.put(key, value);
              return this;
          }
    }
    
  • 那最后的commit和apply是将数据保存到磁盘中,他们首先会执行commitToMemory,将数据提交到内存中,返回MemoryCommitResult,里面保存着本次提交的状态。然后commit会调用enqueueDiskWrite将数据保存到磁盘,并阻塞当前线程;apply通过runable把写入磁盘的操作传递给enqueueDiskWrite,这样将数据写入到了磁盘中。

        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);
    
                    // 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);
                }
    
清除指定的数据
        SharedPreferences userSettings = getSharedPreferences("setting", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = userSettings.edit();
        editor.remove("KEY");
        editor.apply();
清空数据
        SharedPreferences userSettings = getSharedPreferences("setting", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = userSettings.edit();
        editor.clear();
        editor.apply();
监听SharedPreference
  • 可以通过监听器监听SharedPreference的xml文件变化

        public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
           synchronized(this) {
               mListeners.put(listener, mContent);
           }
        }
    
        public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
           synchronized(this) {
               mListeners.remove(listener);
           }
        }
    
apply与commit的区别
  • commit()有返回值,表示是否执行成功;apply()没有返回值。失败后不会有任何提示
  • commit()写入文件是先将数据提交到内存中,然后在当前线程同步执行的,在同一时间有多个commit时,其他commit会等待前面的commit执行之后再操作;apply()写入文件是先将数据提交到内存中,然后把操作放入runable中异步执行,在同一时间有多个commit的时候,后面的会直接将前面的覆盖(推荐使用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值