我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android文件存储SharedPreferences源码解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android文件存储SharedPreferences源码解析

1.我们都知道SharedPreferences 是android可以用来存放key value的的文件。

        SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString("key","value");
        editor.commit();
    SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString("key","value");
        editor.apply();

SharedPreferences是一个接口。getSharedPreferences 拿到的是它的实现类SharedPreferencesImpl。

ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
public SharedPreferences getSharedPreferences(String name, int mode) {
	   if (sp == null) {
		 File prefsFile = getSharedPrefsFile(name);
		 sp = new SharedPreferencesImpl(prefsFile, mode);
		 packagePrefs.put(name, sp);
		 return sp;
     }
}

在构造函数中,会把存储的键值对保存到一个hashMap中

  SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        //读取文件中存储的key value,并存到全局变量mMap中
        startLoadFromDisk();
    }
  private void loadFromDiskLocked() {
    .......
         str = new BufferedInputStream(
                            new FileInputStream(mFile), 16*1024);
         map = XmlUtils.readMapXml(str);
        if (map != null) {
               mMap = map;
               mStatTimestamp = stat.st_mtime;
               mStatSize = stat.st_size;
       } else {
           mMap = new HashMap<String, Object>();
       }
    }

当我们getString等取值的时候,就是从这个mMap中取的。

    get方法就是从这个map中读取。
public String getString(String key, String defValue) {
        synchronized (this) {
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

2. sharedPrefrence存数据,有两种方式,commit和apply。

sp.edit()拿到的也是一个接口,Editor,实现类是EditorImpl。

 SharedPreferences.Editor editor = sp.edit();
 public Editor edit() {
     return new EditorImpl();
 }

当调用putString(String key, String value)时,先保存到了一个map中

  private final Map<String, Object> mModified = Maps.newHashMap();
 public Editor putString(String key, String value) {
        synchronized (this) {
            //将要修改的key value,存放到map中
            mModified.put(key, value);
            return this;
        }
}

那么commit和apply的区别是什么?

1).commit有返回值是一个boolean类型。

apply没有返回值,返回的是void。

2)commit是同步存储,所以必须拿到返回值,代码才能往下走,否则会阻塞在这。

apply是异步存储,直接丢在了一个线程中执行,我们不必等待他的返回结果。

直接看源码

  public boolean commit() {
	MemoryCommitResult mcr = commitToMemory();
	SharedPreferencesImpl.this.enqueueDiskWrite(
		mcr, null );
	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);
}

分析源码

commit和apply都调用了这行代码,

final MemoryCommitResult mcr = commitToMemory();

和private void enqueueDiskWrite(final MemoryCommitResult mcr,

final Runnable postWriteRunnable) ;

这俩的不同在于第二个参数 Runnable postWriteRunnable。commit传的是一个null,而apply传的是一个Runnable对象。这个参数很关键,后面会根据这个参数进行判断,选择是异步存储还是同步存储。

先看commitToMemory()是如何实现的。

这个方法是将要修改的键值对(存在mModified中),和文件中的的全量键值对(存在mMap中),

进行比对,把更新后的map赋值给 mcr.mapToWriteToDisk = mMap;

   private MemoryCommitResult commitToMemory() {
          MemoryCommitResult mcr = new MemoryCommitResult();
           //mMap存储了文件中所有的键值对。
           mcr.mapToWriteToDisk = mMap;
           对要新增或修改的键值对进行遍历。并添加到mMap中
            for (Map.Entry<String, Object> e : mModified.entrySet()) {
                       String k = e.getKey();
                       Object v = e.getValue();
                if (mMap.containsKey(k)) {
                   Object existingValue = mMap.get(k);
                   if (existingValue != null && existingValue.equals(v)) {
                       continue;
                   }
               }
               mMap.put(k, v);
            }
            mcr.changesMade = true;
            mModified.clear();
         return mcr;
   }

在看第二个方法enqueueDiskWrite(mrc,runnable)。

如果是commit方式存储,runnable==null。则调用writeToDiskRunnable.run();进行存储,这个方法是同步的。

如果是apply方式存储,runnable!=null。会直接放进一个线程池中执行。

QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);

这也就是为什么apply是异步存储。

  注意第二个参数,commit传的是null。apply传的是一个postWriteRunnable
   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();
                       }
                   }
               };
           //根据 postWriteRunnable 是不是null来区分是commit方式还是apply方式
           final boolean isFromSyncCommit = (postWriteRunnable == null);
           // Typical #commit() path with fewer allocations, doing a write on
           // the current thread.
           //如果是commit方式,上面的注释很也说明了commit是在当前线程执行的文件存储。
           if (isFromSyncCommit) {
               boolean wasEmpty = false;
               synchronized (SharedPreferencesImpl.this) {
                   wasEmpty = mDiskWritesInFlight == 1;
               }
               if (wasEmpty) {
                   //直接调用Runnable的run方法。在当前线程执行文件的存储。所以是同步方式
                   writeToDiskRunnable.run();
                   return;
               }
           }
            // 如果是applay方式,上面代码不会执行,也就不会return。
            //则会把存储文件的方法放到一个线程池中去执行
           QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
       }

然后在看看writeToFile(MemoryCommitResult mcr)。将修改后的键值对,保存入文件中。

先是对源文件做了一个备份,然后全量的写入文件。

如果写成功,会将备份文件删除。

如果写文件时出现异常,则会将备份文件恢复。

 private void writeToFile(MemoryCommitResult mcr) {
            //在写文件前,先将源文件进行一个备份
             if (!mBackupFile.exists()) {
                 if (!mFile.renameTo(mBackupFile)) {
                     mcr.setDiskWriteResult(false);
                     return;
                 }
             } else { //如果备份文件存在,则将源文件删掉
                 mFile.delete();
             }
          FileOutputStream str = createFileOutputStream(mFile);
            //将文件中所有的keyvalue,保护要修改的,全量存入新的文件中。
            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
            FileUtils.sync(str);
            str.close();
            ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
            // Writing was successful, delete the backup file if there is one.
            //删除备份的文件
            mBackupFile.delete();
            mcr.setDiskWriteResult(true);
 }

到此这篇关于Android文件存储SharedPreferences源码解析的文章就介绍到这了,更多相关Android SharedPreferences内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android文件存储SharedPreferences源码解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android(数据存储:文件存储、SharedPreferences)验证QQ密码

前情提要1 :静态页面-简易QQ登录页面(纯activity_main.xml页面) 【https://blog.csdn.net/weixin_44949135/article/details/104504728】 前情提要2 :动态页面
2022-06-06

android文件存储和SharedPreferences存储的方法

这篇文章主要讲解了“android文件存储和SharedPreferences存储的方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“android文件存储和SharedPreference
2023-06-30

Android编程之SharedPreferences文件存储操作实例分析

本文实例讲述了Android编程之SharedPreferences文件存储操作的方法。分享给大家供大家参考。具体分析如下: SharedPreferences类提供了一种简单的文件存储功能,像程序的配置文件可以通过它来实现。 源代码:pa
2022-06-06

Android使用SharedPreferences存储XML文件的实现方法

本文实例讲述了Android使用SharedPreferences存储XML文件的实现方法。分享给大家供大家参考,具体如下: SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比如窗口状态,
2022-06-06

详解Android文件存储

摘要 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理。根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存
2022-06-06

ConcurrentHashMap 存储结构源码解析

这篇文章主要为大家介绍了ConcurrentHashMap 存储结构源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

详解Android开发数据持久化之文件存储(附源码)

其实我们在社交网络上面所发出的任何信息, 都希望能够保留下来. 那么如何实现呢 数据持久化 数据持久化, 就是将内存中的瞬时数据保存在存储设备中, 保证即便关机之后, 数据仍然存在. 保存在内存中的数据是瞬时数据, 保存在存储设备中的数据就
2022-06-06

Android Jetpack 组件LiveData源码解析

这篇文章主要为大家介绍了Android Jetpack 组件LiveData源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-10

Android Jetpack组件Lifecycle源码解析

这篇文章主要为大家介绍了Android Jetpack组件Lifecycle源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-13

Android 文件数据存储实例详解

Android之文件数据存储 一、文件保存数据介绍 Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。文件可用来存放大量数据,如文本、图书、音频等
2022-06-06

android编程之XML文件解析方法详解(附源码)

本文实例讲述了android编程之XML文件解析方法。分享给大家供大家参考,具体如下: 在android开发中,经常用到去解析xml文件,常见的解析xml的方式有一下三种:SAX、Pull、Dom解析方式。最近做了一个android版的CS
2022-06-06

RocketMQ消息存储文件的加载与恢复机制源码分析

这篇文章主要介绍了RocketMQ源码分析之消息存储文件的加载与恢复机制详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-19

Android 文件存储及常见问题解决

Android文件存储 看下网上随处可以搜到的文件存储套路if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){cacheDir=mConte
2022-06-06

【Android】事件分发机制源码解析

文章目录1. 分发顺序2.源码分析2.1 Activity中的分发流程dispatchTouchEventonTouchEvent总结2.2 ViewGroup中的分发流程dispatchTouchEventonInterceptTouch
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录