一.SharedPreferences是线程安全的吗?它的commit()方法和apply()方法有什么区别?它有什么缺点?有没有类似更好的方案?
1.SharePreferences是线程安全的 里面的方法有大量的synchronized来保障。
2.SharePreferences不是进程安全的 即使你用了MODE_MULTI_PROCESS 。
3.第一次getSharePreference会读取磁盘文件,异步读取,写入到内存中,后续的getSharePreference都是从内存中拿了。
4.第一次读取完毕之前,所有的get/set请求都会被卡住,等待读取完毕后再执行,所以第一次读取会有ANR风险。
5.所有的get都是从内存中读取。
6.提交都是写入到内存和磁盘中 。apply跟commit的区别在于apply 是内存同步,然后磁盘异步写入任务放到一个单线程队列中 等待调用。方法无返回,即void,commit 内存同步 只不过要等待磁盘写入结束才返回,直接返回写入成功状态 true or false
7.从 Android N 开始, 不再支持 MODE_WORLD_READABLE & MODE_WORLD_WRITEABLE. 一旦指定, 会抛异常 。也不建议使用MODE_MULTI_PROCESS 。
8.每次commit/apply都会把全部数据一次性写入到磁盘,即没有增量写入的概念 。 所以单个文件千万不要太大 否则会严重影响性能。
9.不能保证类型安全,调用 getXXX() 方法的时候,可能会出现 ClassCastException 异常,因为使用相同的 key 进行操作的时候,putXXX() 方法可以使用不同类型的数据覆盖掉相同的 key。
SharePreferences | DataStore | MMKV | |
是否线程安全 | 是 | 是 | 是 |
是否线程阻塞 | 是 | 否(通过协程+Flow异步写入) | 否 |
是否支持跨进程 | 否 | 否 | 是(基于mmap+进程锁) |
读取方式 | 完整的 I/O 操作,需要经过用户态、内核态、文件系统 | 通过 mmap 写入虚拟内存,0 拷贝和操作内存一样快 | |
数据格式 | xml(键值对) | xml或者Protobuf | Protobuf |
写入方式 | 全量写入 | 增量写入 | 增量写入 |
是否类型安全 | 否 | 是 | 否 |
是否能监听数据变化 | 否 | 是 | 否 |
参考:再见 SharedPreferences,拥抱 Jetpack DataStore
[实践—卡顿优化] 替换 SharePreferences 为 MMKV_大圣代的博客-CSDN博客
再见 SharedPreferences ,Jetpack DataStore 第二种实现方式
官方也无力回天?SharedPreferences的设计与实现
详解DataStore,SharedPreferences终结者
二.Serializable和Parcelable的区别?
- 都是用于对象序列化的。
- Serializable是Java提供的,Parcelable是android专用的。
- Serializable序列化会调用反射的一些逻辑,产生大量的临时变量,因此效率较低,而Parcelable是通过native方法(Parcel.cpp)来将对象通过JNI写到内存中,一般情况下效率较高。
- Parcelable实现过程较为复杂,并实现序列化、反序列化、内容描述的过程,不过AS有快捷方式,Serializable实现过程较为简单,类继承Serializable接口,添加常量serialVersionUID,此类就可以进行序列化和反序列化操作了。
- Parcelable适用于内存序列化,Serializable适用于存储设备和网络传输序列化。
三.LruCache底层原理?
- 初始化的时候, 会限制缓存占据内存空间的总容量 maxSize;
- 底层维护的是 LinkedHashMap, 使用 LruCache 最好要重写 sizeOf 方法, 用于计算每个被缓存的对象, 在内存中存储时, 占用多少空间;
- 在 put 操作时, 首先计算新的缓存对象, 占多少空间, 再根据 key, 移除老的对象,
- 占用内存大小 = 之前占用的内存大小 + 新对象的大小 - 老对象的大小;
- put 操作最后总会根据 maxSize, 在拿到 LinkedHashMap.EntrySet 中链表的头节点, 循环判断, 只要当前缓存对象占据内存超出 maxSize, 就移除一个头节点, 一直到符合要求;
- lruCache 和 LinkedHashMap 的关系:
- LinkedHashMap 中维护一个双向链表, 并维护 head 和 tail 指针, lruCache 使用了 LinkedHashMap accessOrder 为 true 的属性, 只要访问了某个 key,
- 包括 get 和 put, 就把当前这个 entry 放在链表的位节点, 所以链表的头节点, 是最老访问的节点, 尾节点是最新访问的节点,
- 所以, lruCache 就很巧妙的利用了这个特点, 完成了 Least Recently Used 的需求;
四.android 5.0、android 6.0、android 7.0、android 8.0、android 9.0、android 10、android 11、android12 新特性?
android 5.0
1.默认开启art虚拟机
2.全新的Material Design 风格
3.添加了悬挂式通知栏
android 6.0
1.运行时权限
2.Doze电量管理
3.删除了HttpClient类库,只保留了HttpURLConnection,也可以自己导入HttpClient类库
android 7.0
1.多窗口模式
2.分享私有文件内容的推荐方法是使用FileProvider,使用content://协议,直接使用file://协议会报错
3.支持Java 8
4.后台省电
5.在启动activity的SystemServer进程过程中添加了ActivityStarter类,他是加载Activity的控制类,会收集所有的逻辑来将Intent和Flag转化成Activity。
6.art中添加了JIT即时编译器
android 8.0
1.添加了android中的一个InMemoryDexClassLoader,用于加载内存中的dex文件。
2.修改了IActivityManager从代理模式编程AIDL实现RPC。
3.系统绝大多数隐式广播不能静态注册,自定义隐式广播不能静态注册,个别系统广播可以静态注册,具体查看https://developer.android.google.cn/guide/components/broadcast-exceptions.html
4.引入通知渠道
5.只有前台service不容易被杀死,一旦进入后台,service随时会被回收
android 9.0
1.使用前台service必须声明权限(是普通权限)
2.默认只允许https类型的网络请求
android 10
1.引入深色主题特性
2.支持5g手机
3.通知栏智能回复
android 11
1.新增ControlsProviderService API,可用于提供连接外部设备的控件,这些控件显示于 Android 电源菜单中的设备控制器下。
2.无线调试,同一局域网可以直接进行无线调试手机
3.adb增量安装,命令adb install --incremental
4.聊天气泡
android 12
1.增加了Material You,可帮助您构建更具个性化、更精美的应用。
2.音频焦点,如果某个应用请求获得音频焦点,而另一个应用具有焦点并正在播放,则系统会淡出正在播放的应用。
3.微件改进,改进了现有的 Widgets API,以改善平台和启动器中的用户和开发者体验。
4.新增应用启动动画
参考:Android 5.0 行为变更 | Android 开发者 | Android Developers
5.