SharedPreferences 全面解析

SharedPreference是以key-value键值对的形式存储数据,它只能存储简单的基本数据类型:boolean, float, int, long, String. 它的文件是在data/data/package-name/shared-prefs/目录下。

获取SharedPreferences对象

可以使用以下两种方法之一:

  • getSharedPreferences(String name, int mode)

  • getPreferences(int mode)

其实getPreferences()内部也是调用了getSharedPreferences(),看下源码:

public SharedPreferences getPreferences(int mode) {
    return getSharedPreferences(getLocalClassName(), mode);
}
/**
 * Returns class name for this activity with the package prefix removed.
 * This is the default name used to read and write settings.
 *
 * @return The local class name.
 */
@NonNull
public String getLocalClassName() {
    final String pkg = getPackageName();
    final String cls = mComponent.getClassName();
    int packageLen = pkg.length();
    if (!cls.startsWith(pkg) || cls.length() <= packageLen
            || cls.charAt(packageLen) != '.') {
        return cls;
    }
    return cls.substring(packageLen+1);
}

getPreferences()是Activity的方法,不需要传入name参数,它返回的是以getLocalClassName()返回值命名的SharedPreferences对象,而getLocalClassName()方法返回的是去掉package-name的当前Activity类名。所以在不同的Activity中调用getPreferences()返回的是不同的SharedPreferences对象。

例如:

package com.lc.learndemo.sharedpreference;

...
import com.lc.learndemo.R;

public class SharedPrefsActivity extends Activity implements View.OnClickListener{

    private SharedPreferences sPrefs;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shared_prefs);

        sPrefs = getPreferences(MODE_PRIVATE);//此时并没有创建文件
        sPrefs.edit().putString("name","小明").commit();//第一次写数据的时候才会创建文件
    }
}

启动这个Activity后,会在data/data/package-name/shared-prefs/目录下创建一个名为sharedpreference.SharedPrefsActivity的xml文件。

SharedPreferences的读写

SharedPreferences本身只支持读数据:

SharedPreferences sPrefs = getSharedPreferences("config_sprefs", MODE_PRIVATE);
String name = sPrefs.getString("name","该值不存在")

而存储或修改数据则需要通过SharedPreferences.Editor对象。例如:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shared_prefs);

    SharedPreferences sPrefs = getSharedPreferences("config_sprefs", MODE_PRIVATE);
    SharedPreferences.Editor editor = sPrefs.edit(); //获取Editor对象
    editor.putString("name","小明"); //存储数据
    editor.commit(); //提交修改
}

SharedPreferences的标志位

文件创建标志位:

  • Context.MODE_PRIVATE 这是默认标志位,表示创建的文件只能被当前应用访问。
  • Context.MODE_WORLD_READABLE 允许其它应用程序读该文件。
  • Context.MODE_WORLD_WRITEABLE 允许其它应用程序写该文件。

文件加载标志位:

  • Context.MODE_MULTI_PROCESS 这是文件加载标志位,表示即使你的SharedPreferences文件已经加载到进程,系统还是会去检查文件是否更改。这个是在API level 11加入的,解决的问题是:当加载SharedPreference文件的时候,系统会在内存中留有一份缓存,所以当SharedPreference文件的内容发生改变时,其他应用程序读取的还是缓存的数据,不能读到最新的数据,当设置这个标志位问题就解决了。这个标志主要用在跨进程访问中。

跨进程访问SharedPreferences

同一个应用中的跨进程访问

首先创建应用App1,创建AActivity,在A中创建SharedPreferences

public class A extends Activity implements View.OnClickListener {

    private SharedPreferences sPrefs;
    private SharedPreferences.Editor editor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shared_prefs);

        sPrefs = getSharedPreferences("config_sprefs",MODE_WORLD_READABLE);
        editor = sPrefs.edit();
        editor.putString("name","小明");
        editor.commit();
        init();
    }

    private void init () {
        Button editButton = (Button) findViewById(R.id.btn_edit);
        editButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_edit:
                 editor.putString("name","张三").commit();
        }
    }
}

在应用App1中再创建BActivity,在AndroidManifest.xml中给B Activity 指定在另一个独立的进程中运行:

<activity android:name=".B"
          android:process=":Remote" />

在B中跨进程访问App1所在的进程的SharedPreference文件:

public class B extends Activity implements View.OnClickListener{

    private TextView tvAcrossProcess;
    private Button btnAcrossProcess;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_androidprocess);
        initViews();
    }

    private void initViews () {
        tvAcrossProcess = (TextView) findViewById(R.id.tv_across_process);
        btnAcrossProcess = (Button) findViewById(R.id.btn_across_process);
        btnAcrossProcess.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_across_process:
                /*在同一个应用中得进程可以直接获取SharedPreferences对象,加载模式设置为MODE_MULTI_PROCESS才可以对到修改后的数据*/
                SharedPreferences sp = getSharedPreferences("config_sprefs", MODE_MULTI_PROCESS);
                tvAcrossProcess.setText(sp.getString("name","该值不存在"));
        }
    }
}

B中有一个Button和一个TextView,点击Button跨进程访问SharedPreferences,获取的内容显示到TextView中。

不同应用程序之间的跨进程访问

我们再创建一个App2,在App2中创建CActivity,点击Button访问App1中得SharedPreferences,结果显示在TextView中:

public class C extends Activity implements View.OnClickListener{

    private TextView tvAcrossProcess;
    private Button btnAcrossProcess;
    private Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews () {
        tvAcrossProcess = (TextView) findViewById(R.id.tv_across_process);
        btnAcrossProcess = (Button) findViewById(R.id.btn_across_process);
        btnAcrossProcess.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_across_process:
                try {
                    context = createPackageContext("com.lc.learndemo",CONTEXT_IGNORE_SECURITY);
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }

                SharedPreferences sp = context.getSharedPreferences("config_sprefs", MODE_MULTI_PROCESS);
                String content = sp.getString("name","该值不存在");
                tvAcrossProcess.setText(content);
        }
    }
}

createPackageContext(String packageName, int flags)创建其它程序的Context,第一个参数是应用的包名,第二个参数是标志位,它有两个值,CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。CONTEXT_IGNORE_SECURITY的意思 是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。

但是谷歌官方文档不建议使用SharedPreferences来进行跨进程通信,会有安全性问题,可以用ContentProvider, BroadcastReceiver, 或Service来代替.

SharedPreferences.OnSharedPreferenceChangeListener

这个接口用来监听SharedPreferences内容变化的,其内部有两个方法:

//注册监听
registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener)
//解除注册
unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener)

注意:当调用getXxx()方法获取SharedPreference对象是,这个文件并没有创建,只有当第一次调用putXxx()方法想文件中写值得时候才会在data/data/package-name/shared-prefs/目录下创建该文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容