Android sp相关问题
SP推荐实践
1.在工作线程中写入sp时,直接调用commit就可以,不必调用apply,这种情况下,commit的开销更小
2.在主线程中写入sp时,不要调用commit,要调用apply
3.sp对应的文件尽量不要太大,按照模块名称去读写对应的sp文件,而不是一个整个应用都读写一个sp文件
4.sp的适合读写轻量的、小的配置信息,不适合保存大数据量的信息,比如长串的json字符串。
5.当有连续的调用PutXxx方法操作时(特别是循环中),当确认不需要立即读取时,最后一次调用commit或apply即可。
Android轻量级存储方案对比
源码解析
apply
方法,首先创建了一个 awaitCommit 的 Runnable,然后加入到 QueuedWork 中,awaitCommit 中包含了一个等待锁,需要在其它地方释放。我们在上面看到的 QueuedWork.waitToFinish() 其实就是等待这个队列中的 awaitCommit 全部释放。
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); }复制代码
然后通过 SharedPreferencesImpl.this.enqueueDiskWrite
创建了一个任务来执行真正的 SP 持久化。
commit
则会同步的执行 writeToFile,apply 则会将 writeToFile 加入到一个任务队列中异步的执行
private void enqueueDiskWrite(final MemoryCommitResult mcr, final Runnable postWriteRunnable) { final boolean isFromSyncCommit = (postWriteRunnable == null); final Runnable writeToDiskRunnable = new Runnable() { @Override public void run() { synchronized (mWritingToDiskLock) { writeToFile(mcr, isFromSyncCommit); } synchronized (mLock) { mDiskWritesInFlight--; } if (postWriteRunnable != null) { postWriteRunnable.run(); } } }; // Typical #commit() path with fewer allocations, doing a write on // the current thread. if (isFromSyncCommit) { boolean wasEmpty = false; synchronized (mLock) { wasEmpty = mDiskWritesInFlight == 1; } if (wasEmpty) { writeToDiskRunnable.run(); return; } } QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit); }复制代码
writeToFile
执行完成会释放等待锁,之后会回调传递进来的第二个参数 Runnable 的 run 方法,并将 QueuedWork 中的这个等待任务移除。
总结来看,SP
调用 apply
方法,会创建一个等待锁放到 QueuedWork 中,并将真正数据持久化封装成一个任务放到异步队列中执行
,任务执行结束会释放锁。Activity onStop 以及 Service 处理 onStop,onStartCommand 时,执行 QueuedWork.waitToFinish() 等待所有的等待锁释放。
Android8.0对Sp的优化主要是有两个方面:
1、改变原来被动等待线程调度执行写入的方式,改为主动去调用,涉及主要方法是SharedPreferencesImpl.waitToFinish 2、 增加版本号控制的逻辑,原来是所有的提交都会执行写入磁盘一遍,现在是只执行最后、最新的提交写入磁盘,涉及的主要方法是:SharedPreferencesImpl.writeToFile复制代码
针对8.0以下优化:
mp.weixin.qq.com/s/IFgXvPdiE…
public static void tryHackActivityThreadH() { try { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentAtyThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object activityThread = currentAtyThreadMethod.invoke(null); Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler handler = (Handler) mHField.get(activityThread); Field mCallbackField = Handler.class.getDeclaredField("mCallback"); mCallbackField.setAccessible(true); mCallbackField.set(handler,new SpCompatCallback()); Log.d(TAG,"hook success"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (Throwable e){ e.printStackTrace(); } }复制代码
//自定义callbak:SpCompatCallback,在这个方法中做清理等待锁列表的操作: public class SpCompatCallback implements Handler.Callback { public SpCompatCallback(){ } //handleServiceArgs private static final int SERVICE_ARGS = 115; //handleStopService private static final int STOP_SERVICE = 116; //handleSleeping private static final int SLEEPING = 137; //handleStopActivity private static final int STOP_ACTIVITY_SHOW = 103; //handleStopActivity private static final int STOP_ACTIVITY_HIDE = 104; //handlePauseActivity private static final int PAUSE_ACTIVITY = 101; //handlePauseActivity private static final int PAUSE_ACTIVITY_FINISHING = 102; @Override public boolean handleMessage(Message msg) { switch (msg.what){ case SERVICE_ARGS: SpHelper.beforeSpBlock("SERVICE_ARGS"); break; case STOP_SERVICE: SpHelper.beforeSpBlock("STOP_SERVICE"); break; case SLEEPING: SpHelper.beforeSpBlock("SLEEPING"); break; case STOP_ACTIVITY_SHOW: SpHelper.beforeSpBlock("STOP_ACTIVITY_SHOW"); break; case STOP_ACTIVITY_HIDE: SpHelper.beforeSpBlock("STOP_ACTIVITY_HIDE"); break; case PAUSE_ACTIVITY: SpHelper.beforeSpBlock("PAUSE_ACTIVITY"); break; case PAUSE_ACTIVITY_FINISHING: SpHelper.beforeSpBlock("PAUSE_ACTIVITY_FINISHING"); break; default: break; } return false; } }复制代码
//清理等待列表的操作 public class SpHelper { private static final String TAG = "SpHelper"; private static boolean init = false; private static String CLASS_QUEUED_WORK = "android.app.QueuedWork"; private static String FIELD_PENDING_FINISHERS = "sPendingWorkFinishers"; private static ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers = null; public static void beforeSpBlock(String tag){ if(!init){ getPendingWorkFinishers(); init = true; } Log.d(TAG,"beforeSpBlock "+tag); if(sPendingWorkFinishers != null){ sPendingWorkFinishers.clear(); } } private static void getPendingWorkFinishers() { Log.d(TAG,"getPendingWorkFinishers"); try { Class clazz = Class.forName(CLASS_QUEUED_WORK); Field field = clazz.getDeclaredField(FIELD_PENDING_FINISHERS); field.setAccessible(true); sPendingWorkFinishers = (ConcurrentLinkedQueue<Runnable>) field.get(null); Log.d(TAG,"getPendingWorkFinishers success"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (Throwable e){ e.printStackTrace(); } } }
作者:瀚海来客
链接:https://juejin.cn/post/7020249411255025694