67

你已经用 SharedPrefrence 的 apply() 替换 commit() 了吗?

 6 years ago
source link: https://juejin.im/post/5a13cb3851882558513252de
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

你已经用 SharedPrefrence 的 apply() 替换 commit() 了吗?

2017年11月21日 08:43 ·  阅读 7778

这是 面试系列 的第六期。本期我们将来探讨一个有趣的东西 —— SharePrefrence 的两种提交方式 apply()commit()

往期内容传递:
Android 面试(一):说说 Android 的四种启动模式
Android 面试(二):如何理解 Activity 的生命周期
Android 面试(三):用广播 BroadcastReceiver 更新 UI 界面真的好吗?
Android 面试(四):Android Service 你真的能应答自如了吗?
Android 面试(五):探索 Android 的 Handler

其实非常有趣,我们经常在开发中使用 SharePrefrence 保存一些轻量级数据,比如判断是否是首次启动,首次启动进入引导页,否则直接到主页面,或者是其它的一些应用场景。

而我们也耳熟能详这样的写法。

  • 根据 Context 获取 SharedPreferences 对象
  • 利用 edit() 方法获取 Editor 对象。
  • 通过 Editor 对象存储 key-value 键值对数据。
  • 通过 commit() 方法提交数据。
public class SplashActivity extends AppCompatActivity {
    public static final String SP_KEY = "com.zxedu.myapplication.SplashActivity_SP_KEY";
    public static final String IS_FIRST_IN = "com.zxedu.myapplication.SplashActivity_IS_FIRST_IN";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 一些业务代码
        ......    

        SharedPreferences preferences = getSharedPreferences("name",MODE_PRIVATE);
        if (preferences.getBoolean(IS_FIRST_IN,true)){
            // 跳转引导页面
            startActivity(new Intent(this,GuideActivity.class));
            finish();
        }else{
            // 跳转主页面
            startActivity(new Intent(this,MainActivity.class));
        }

    }
}


public class GuideActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        getSharedPreferences(SplashActivity.SP_KEY,MODE_PRIVATE).edit().putBoolean(SplashActivity.IS_FIRST_IN,false).apply();
    }
}复制代码

从代码中可以看到,一阵混乱操作,没啥特别的地方,但早期开发的人员应该知道,之前我们都是比较青睐 commit() 进行提交的。而现在 Android Studio 在我们使用 commit() 直接提交的时候会直接报黄色警告。

nanchen

nanchen

commit() 和 apply() 到底有什么异同?

先说说相同点:

  • 二者都是提交 Prefrence 修改数据;
  • 二者都是原子过程。

不同点直接上源码吧,先看看 commit() 方法的定义:

/**
         * Commit your preferences changes back from this Editor to the
         * {@link SharedPreferences} object it is editing.  This atomically
         * performs the requested modifications, replacing whatever is currently
         * in the SharedPreferences.
         *
         * <p>Note that when two editors are modifying preferences at the same
         * time, the last one to call commit wins.
         *
         * <p>If you don't care about the return value and you're
         * using this from your application's main thread, consider
         * using {@link #apply} instead.
         *
         * @return Returns true if the new values were successfully written
         * to persistent storage.
         */
        boolean commit();复制代码

综合一下 commit() 方法的注释也就是两点:

  • 会返回执行结果。
  • 如果不考虑结果并且是在主线程执行可以考虑 apply()

再看看 apply() 方法的定义:

/**
         * Commit your preferences changes back from this Editor to the
         * {@link SharedPreferences} object it is editing.  This atomically
         * performs the requested modifications, replacing whatever is currently
         * in the SharedPreferences.
         *
         * <p>Note that when two editors are modifying preferences at the same
         * time, the last one to call apply wins.
         *
         * <p>Unlike {@link #commit}, which writes its preferences out
         * to persistent storage synchronously, {@link #apply}
         * commits its changes to the in-memory
         * {@link SharedPreferences} immediately but starts an
         * asynchronous commit to disk and you won't be notified of
         * any failures.  If another editor on this
         * {@link SharedPreferences} does a regular {@link #commit}
         * while a {@link #apply} is still outstanding, the
         * {@link #commit} will block until all async commits are
         * completed as well as the commit itself.
         *
         * <p>As {@link SharedPreferences} instances are singletons within
         * a process, it's safe to replace any instance of {@link #commit} with
         * {@link #apply} if you were already ignoring the return value.
         *
         * <p>You don't need to worry about Android component
         * lifecycles and their interaction with <code>apply()</code>
         * writing to disk.  The framework makes sure in-flight disk
         * writes from <code>apply()</code> complete before switching
         * states.
         *
         * <p class='note'>The SharedPreferences.Editor interface
         * isn't expected to be implemented directly.  However, if you
         * previously did implement it and are now getting errors
         * about missing <code>apply()</code>, you can simply call
         * {@link #commit} from <code>apply()</code>.
         */
        void apply();复制代码

略微有点长,大概意思就是 apply()commit() 不一样的地方是,它使用的是异步而不是同步,它会立即将更改提交到内存,然后异步提交到硬盘,并且如果失败将没有任何提示。

总结一下不同点:

  • commit() 是直接同步地提交到硬件磁盘,因此,多个并发的采用 commit() 做提交的时候,它们会等待正在处理的 commit() 保存到磁盘后再进行操作,从而降低了效率。而 apply() 只是原子的提交到内容,后面再调用 apply() 的函数进行异步操作。
  • 翻源码可以发现 apply() 返回值为 void,而 commit() 返回一个 boolean 值代表是否提交成功。
  • apply() 方法不会有任何失败的提示。

那到底使用 commit() 还是 apply()?

大多数情况下,我们都是在同一个进程中,这时候的 SharedPrefrence 都是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,我们非常建议用 apply() ,当然需要确保操作成功且有后续操作的话,还是需要用 commit() 的。

做不完的开源,写不完的矫情。欢迎扫描下方二维码或者公众号搜索「nanchen」关注我的微信公众号,目前多运营 Android ,尽自己所能为你提升。如果你喜欢,为我点赞分享吧~
nanchen


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK