SharedPreferences,apply跟commit的最大区别: apply的写入文件操作是在单线程的线程池来完成; 而commit是在当前线程阻塞运行的。
-- SharedPreference apply 引起的 ANR 问题:
SP 操作仅仅把 commit 替换为 apply 不是万能的,apply 调用次数过多容易引起 ANR。所有此类 ANR 都是经由 QueuedWork.waitToFinish() 触发的,如果在调用此函数之前,将其中保存的队列手动清空,那么是不是能解决问题呢,答案是肯定的。
Activity 的 onStop,以及 Service 的 onStop 和 onStartCommand 都是通过 ActivityThread 触发的,ActivityThread 中有一个 Handler 变量,我们通过 Hook 拿到此变量,给此 Handler 设置一个 callback,Handler 的 dispatchMessage 中会先处理 callback。
1.在 Callback 中调用队列的清理工作; 2.队列清理需要反射调用 QueuedWork。
SP 无论是 commit 还是 apply 都会产生 ANR,但从 Android 之初到目前 Android8.0,Google 一直没有修复此 bug.apply 机制本身的失败率就比较高,清理等待锁队列对持久化造成的影响不大。目前头条 app 已经全量开启清理等待锁策略.
-- SharedPreferences超级大卡顿- https://www.jianshu.com/p/40e42da910e2
采用objectbox替换SharedPreferences解决卡顿问题。
commit 方式会阻塞调用的线程
apply 放法不会阻塞调用的线程,但是如果写入任务比较耗时,会阻塞住主线程,因为主线程有调用的代码,需要等写入任务执行完了才会继续往下执行;
如果非要用这个我建议开启一个线程然后在线程中调用commit方式更新数据,至少这个方案不会卡住主线程。
> Android的缓存:DiskLruCache,LruCache
Android的缓存机制是基于Java的缓存机制。Java的缓存机制有四种,强引用、软引用、弱引用和虚引用。着重看看软引用(SoftReference)和弱引用(WeakReference)。
Android中可通过缓存来减少频繁的网络操作,减少流量、提升性能。三级缓存策略。
Note: In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache,
however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive
with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
Note: 在过去,一种比较流行的内存缓存实现方法是使用软引用(SoftReference)或弱引用(WeakReference)对Bitmap进行缓存,然而我们并不推荐这样的做法。从Android 2.3 (API Level 9)开始,垃圾回收机制变得更加频繁,这使得释放软(弱)引用的频率也随之增高,导致使用引用的效率降低很多。而且在Android 3.0 (API Level 11)之前,备份的Bitmap会存放在Native Memory中,它不是以可预知的方式被释放的,这样可能导致程序超出它的内存限制而崩溃。
所谓二级缓存实际上并不复杂,当Android端需要获得数据时比如获取网络中的图片,我们首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取;当获得来自网络的数据,就以key-value对的方式先缓存到内存(一级缓存),同时缓存到文件或sqlite中(二级缓存)。注意:内存缓存会造成堆内存泄露,所有一级缓存通常要严格控制缓存的大小,一般控制在系统内存的1/4。
互联网应用的缓存实践分享- http://www.roncoo.com/article/detail/129678?ref=myread
> SharePreferences组件的数据存储与缓存
SharePreferences源码分析(commit与apply的区别以及原理)- http://blog.youkuaiyun.com/double2hao/article/details/53871640
从源码来分析其实很简单,两者主要区别有两点:
1、commit()有返回值,apply()没有返回值。apply()失败了是不会报错的。
2、apply()写入文件的操作是异步的,会把Runnable放到线程池中执行,而commit()的写入文件的操作是在当前线程同步执行的。
SharedPreference的读写原理分析,apply是异步,commit是同步,在主线程中使用commit可能会影响性能,因为同步IO操作的耗时可能会比较长,两个方法都能保证value被正确的保存到磁盘上。
SharePreferences保存的数据,以XML形式保存在硬盘(SD卡)中。
-- 官方文档明确指出,SharedPreferences不支持多线程,进程也是不安全的.
在多进程中,如果要交换数据,不要使用SharedPreference,因为在不同版本表现不稳定,推荐使用ContentProvider替代。
SharedPreferences写操作有两个提交方式:
1)commit():线程安全,性能慢,一般来说在当线程完成写文件操作(有返回值)
2)apply();线程不安全,性能高,异步处理I/O操作,一定会把这个写文件放入一个SingleThreadExecutor线程池中(无返回值)
-- Android下多进程访问SharedPreferences遇到的坑:http://blog.youkuaiyun.com/it_phoenix/article/details/51281294
由于项目中使用sharedPreferences存储数据的server端存储了许多数据,而只针对需要共享的字段进行了mode设置,其余字段存储时所设置的mode都为private模式,导致client端无法访问,所以解决办法就是将需要共享数据的字段提出来统一存储到一个文件中,然后client端才能正常访问!
-- SharedPreferences作为Android存储数据方式之一,主要特点是:
1. 只支持Java基本数据类型,不支持自定义数据类型;
2. 应用内数据共享;
3. 使用简单.
因为sp.edit()每次都会返回一个新的Editor对象,Editor的实现类EditorImpl里面会有一个缓存的Map,最后commit的时候先将缓存里面的Map写入内存中的Map,然后将内存中的Map写进XML文件中。
getSharedPreferences源码分析:
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
......
final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
sSharedPrefs.put(packageName, packagePrefs);
}
......
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
}
......
return sp;
}
> SharePreferences跨应用读取数据
SharedPreferences跨应用读取数据- http://download.youkuaiyun.com/download/gcsdn2000/4161520
针对在多进程中使用SharedPreferences不安全的问题提供的解决方案- https://github.com/grandcentrix/tray
-- 保存
package edu.cczu.SimplePreference;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.EditText;
public class SimplePreferenceActivity extends Activity {
private EditText nameText;
private EditText ageText;
private EditText heightText;
public static final String PREFER_NAME = "SaveSet";
public static int MODE = Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
nameText = (EditText)findViewById(R.id.name);
ageText = (EditText)findViewById(R.id.age);
heightText = (EditText)findViewById(R.id.height);
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
loadSharedPreferences();
}
private void loadSharedPreferences() {
// TODO Auto-generated method stub
SharedPreferences share = getSharedPreferences(PREFER_NAME, MODE);
String name = share.getString("Name", "Tom");
int age = share.getInt("Age", 20);
float height = share.getFloat("Height", 1.81f);
nameText.setText(name);
ageText.setText(String.valueOf(age));
heightText.setText(String.valueOf(height));
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
saveSharedPreferences();
}
private void saveSharedPreferences() {
// TODO Auto-generated method stub
SharedPreferences share = getSharedPreferences(PREFER_NAME, MODE);
SharedPreferences.Editor editor = share.edit();
editor.putString("Name", nameText.getText().toString());
editor.putInt("Age", Integer.parseInt(ageText.getText().toString()));
editor.putFloat("Height", Float.parseFloat(heightText.getText().toString()));
editor.commit();
}
}
-- 读取其他应用数据
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
public class SharePreferenceActivity extends Activity {
public static final String PREFERENCE_PACKAGE = "edu.cczu.SimplePreference";
public static final String PREFERENCE_NAME = "SaveSet";
public static int MODE = Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE;
private TextView labelView;
private static String TAG = "LIFECYCLE";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Context c = null;
labelView = (TextView)findViewById(R.id.label);
try {
c = this.createPackageContext(PREFERENCE_PACKAGE, Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SharedPreferences sharedPreferences = c.getSharedPreferences(PREFERENCE_NAME, MODE);
String name = sharedPreferences.getString("Name","Tom");
int age = sharedPreferences.getInt("Age", 20);
float height = sharedPreferences.getFloat("Height",1.81f);
String msg = "";
msg += "姓名:" + name + "\n";
msg += "年龄:" + String.valueOf(age) + "\n";
msg += "身高:" + String.valueOf(height) + "\n";
labelView.setText(msg);
Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
Log.i(TAG, "(1) onCreate()");
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.i(TAG, "(2) onStart()");
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.i(TAG, "(8) onStop()");
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i(TAG, "(9) onDestroy()");
//System.exit(0);
}
}