阅读 250

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


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐