1.SharedPreferences简介
Sharedpreferences是Android平台上一个轻量级的存储类,可以用于保存应用程序的各种配置信息,如应用设置里面的各种开关、是否打开音效、是否使用震动效果、小游戏的玩家积分等,其本质是以“键-值”对的方式保存数据到本地的 xml 文件中,其文件保存在 /data/data/<package name>/shared_prefs 目录下。
核心原理:以“键-值”对的方式保存数据到本地的 xml 文件中,具体实现是在 SharedPreferencesImpl 里面使用Map来管理,xml 文件的具体保存路径是在 /data/data/<package name>/shared_prefs 目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。
SharedPreferences本身是一 个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下:
Context.MODE_APPEND: 追加方式存储 Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。 Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。 Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写 Context.MODE_MULTI_PROCESS: 适用于多进程访问(目前已被废弃,google官方推荐使用ContentProvider来实现进程间共享访问)
Editor有如下主要重要方法:
SharedPreferences.Editor clear():清空SharedPreferences里所有数据 SharedPreferences.Editor putXxx(String key , xxx value): 向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据 SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项 boolean commit(): 当Editor编辑完成后,使用该方法提交修改
首次创建SharedPreferences对象(即SharedPreferences初始化时),会根据文件名将文件下内容一次性加载到mMap容器中,每当我们edit都会创建一个新的EditorImpl对象,当修改或者添加数据时会将数据添加到mModifiled容器中,然后commit或者apply操作比较mMap与mModifiled数据修正mMap中最后一次提交数据然后写入到文件中。
使用SharedPreferences的 get 方法获取数据时是直接从 mMap 中读取的,直接从 mMap 中读取数据可以提高读取的效率,但也间接表明 SharedPreferences 不适合存放 大的key和value,因为存放大的key和value在SharedPreferences中,数据会一直存储在内存中得不到释放占用较大的内存,容易引发系统 GC,严重时导致界面丢帧甚至ANR。
2.SharedPreferences提交数据的方法commit()、apply()的区别及使用场景
commit()、apply()的区别
- commit() 方法是Android API 1开始就存在的方法,而 apply() 方法是从Android API 9 开始增加的方法;
- commit() 和 apply() 虽然都是原子性操作,但是原子的操作范围不同,commit() 是原子提交到数据库,从提交数据到存在Disk中都是同步过程;而 apply() 方法是原子提交到内存,从内存到数据库的更新是异步操作;
- 提交相同的数据 commit() 方法的效率会比apply() 方法提交的速度慢,即 apply()方法提交数据的效率较高;
- apply() 没有返回值,而 commit() 有返回值表明提交修改是否成功。
使用场景
从 commit()和 apply() 两个方法的区别中可以得出两个方法的使用场景:在一个进程中,由于sharedPreference是单实例的,只要保证内存缓存正确就能保证运行时数据的正确性,一般不会出现并发冲突,所以如果对提交结果不关心的话,建议使用apply(),只有在关心提交结果的情况下使用 commit()。
3.Sharedpreferences跨进程访问问题
对于多进程的应用,若在某一个进程获取到的SP值不是最新的,很可能是创建SP的时候指定的模式有问题,应该指定为多进程的模式:Context.MODE_MULTI_PROCESS,设置之后可以实时读取Sharedpreferences中修改后的值。
通过Context.MODE_MULTI_PROCESS属性使用SharedPreferences虽然可以实现多进程访问SharedPreferences数据的问题,但是这种方式的多进程共享数据可能会出现数据不一致的问题。问题原因是因为进程间是不能内存共享的,每个进程操作的SharedPreferences都是一个单独的实例,SharedPreferences数据写入的时机也不确定,而且不能通过加锁解决多进程的数据同步,从而导致了多进程间通过SharedPreferences来共享数据是不安全的。
结论:Context.MODE_MULTI_PROCESS这个属性Google已经废弃,不建议使用了,对于多进程间的数据共享建议使用ContentProvider。若要用Sharedpreferences实现多进程数据共享,只能在确保不会同时操作SharedPreferences数据的前提下使用,但这个条件很难保证,所以建议最好不要使用。
4.访问其他应用中的Preference
如果要访问其他应用中的Preference,必须满足的条件是,要访问的应用的Preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
举例,假如有个<package name>为com.alexlee1987.demo下面的应用使用了下面语句创建了Preference,getSharedPreferences("demo", Context.MODE_WORLD_READABLE),现在要访问该Preferences:
首先,需要创建上面的Context,然后通过Context访问Preferences,访问preference时会在应用所在包下的shared_prefs目录找到preference:
Context context = createPackageContext("com.alexlee1987.demo", Context.CONTEXT_IGNORE_SECURITY); SharedPreferences sharedPreferences = context.getSharedPreferences("demo", Context.MODE_WORLD_READABLE); String name = sharedPreferences.getString("name", ""); int age = sharedPreferences.getInt("age", 0);
如果不通过创建Context访问其他应用的preference,可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package name>应替换成应用的包名。
5.使用建议
Sharedpreferences是Android平台上一个轻量级的存储类,可以方便快捷的在本地保存应用的一些信息,Sharedpreferences好用,但也不能滥用,使用过程中建议遵循以下规则:
- 不要存放大的key和value在SharedPreferences中,数据一直存储在内存中得不到释放,内存使用过高会频发引发GC,导致界面丢帧甚至ANR;
- 不相关的配置选项最好不要放在一起,单个文件越大读取速度则越慢;
- 读取频繁的key和不频繁的key尽量不要放在一起;
-
commit发生在UI线程中,apply发生在工作线程中,对于数据的提交最好是批量操作统一提交。虽然apply发生在工作线程(不会因为IO阻塞UI线程)但是如果添加任务较多,Activity页面退出时有可能会阻塞,严重时甚至会出现ANR,具体可以参照ActivityThread源码中handleStopActivity方法实现;
- 尽量不要存放json和html,这种可以直接文件缓存;
- 最好提前初始化SharedPreferences,避免SharedPreferences第一次创建时读取文件线程未结束而出现等待情况,可以考虑在Application初始化的时候初始化SharedPreferences。