android之Files,Saving State,Preferences(一)

 

保存简单的APP数据

1.Shared Preferences

是一个简单,轻量级的,以键值对的机制(name/value pair---NVP)存储一些基础数据(Boolean,string,float等),最常用在保存用户的APP参数。

2.保存APP~UI状态

当APP被移到后台,Activity和Fragment都包含用来帮助记录当前UI状态的生命周期事件回调函数。

3.Files

文件不是完美的选择,但是有时候读写文件是唯一的选择。安卓可以让你在外部或者内部媒介上创建文件,也提供支持在公有访问的文件夹下创建临时缓存文件和存储文件。

 

创建和保存Shared Preferences

去创建或者修改Shared Prefence,通过调用getSharedPreferences.

SharedPreferences mySharedPreferences = getSharedPreferences(MY_PREFS,Activity.MODE_PRIVATE);

如何去修改呢?

答:使用SharedPreferences.Editor类。

SharedPreferences.Editor editor = mySharedPreferences.edit();

根据指定的键,使用put方法去更新或者插入值。

// Store new primitive types in the shared preferences object.  
editor.putBoolean(“isTrue”, true); 
editor.putFloat(“lastFloat”, 1f); 
editor.putInt(“wholeNumber”, 2);  
editor.putLong(“aNumber”, 3l); 
editor.putString(“textEntryValue”, “Not Empty”);

 

完了后,需要提交一下,使用editor.apply()或者editor.commit() 前者是异步的,后者是同步的。

注意:apply方法是在API level 9(Android 2.3)引入的。使用此方法,可以安全得异步写入。因为是异步,所以通常这个方法更受欢迎。

如果你使用commit呢,是会阻塞调用的线程,写入成功会返回true,反之亦然。

 

获取Shared Preferences

直接看代码:用起来类似于hashMap,第2个参数是默认值,就是如果没有就返回默认值。

// Retrieve the saved values.   
boolean isTrue = mySharedPreferences.getBoolean(“isTrue”, false); 
float lastFloat = mySharedPreferences.getFloat(“lastFloat”, 0f); 
int wholeNumber = mySharedPreferences.getInt(“wholeNumber”, 1); 
long aNumber = mySharedPreferences.getLong(“aNumber”, 0); 
String stringPreference = mySharedPreferences.getString(“textEntryValue”, “”);

 

再看:

Map<String, ?> allPreferences = mySharedPreferences.getAll(); //获取全部
boolean containsLastFloat = mySharedPreferences.contains(“lastFloat”); //是否包含这个键

 

介绍Preference框架和Preference Activity

安卓提供一个基于XML驱动的框架,为你的APP创建系统风格的Preferences Screens。通过使用这个框架你可以创建Preferences Activities(此activity在原生APP或者第3方APP都保持风格一致)。

Preference框架包含4部分:

1.Preference Screen layout  Preference Screen的布局文件。

2.Preference ActivityPreference Fragment  分别继承与PreferenceActivity和PreferenceFragment, 被用来支持Preference Screen。在Android 3.0之前,Preference Activities直接支持Preference Screen,但是现在, PreferenceScreen<---PreferenceFragment<----Activity。

3.Preference Header Definition  3.0后引入

4.Shared Preference Change Listener  实现了onSharedPreferenceChangeListener的类,来监听某个Shared Preferences的状态改变。

 

用XML定义一个Preference Screen Layout

这种布局是做为xml资源,而不是layout资源,存放在res/xml下,程序中以R.xml.文件名.

首先需要一对<PreferenceScreen>标签,

<?xml version=”1.0” encoding=”utf-8”?> 
<PreferenceScreen 
   xmlns:android=”http://schemas.android.com/apk/res/android”> 
</PreferenceScreen>

 

PreferenceCategory 用来分类,以标题栏作为分隔符,像个容器:

<PreferenceCategory
  android:title=”My Preference Category”/>

 

Preference的控制视图至少包含:

1.android:key  这个与Shared Preference的key有关。

2.android:title 为这个Preference显示代表性的标题。

3.android:summary  以略微小的字体,略长的字数,概述当前的preference。

4.android:defaultValue 默认值,如果未设置。

 

下面举一个简单的example,包含PreferenceCategory和CheckBox Preference

<?xml version=”1.0” encoding=”utf-8”?> 
<PreferenceScreen 
  xmlns:android=”http://schemas.android.com/apk/res/android”> 
  <PreferenceCategory 
     android:title=”My Preference Category”> 
     <CheckBoxPreference 
       android:key=”PREF_CHECK_BOX” 
       android:title=”Check Box Preference” 
       android:summary=”Check Box Preference Description” 
       android:defaultValue=”true” 
     /> 
  </PreferenceCategory> 
</PreferenceScreen>

 

PreferenceCategory 更像一个分门别类的容器。

 

列举安卓提供的preference控制视图:

1.CheckBoxPreference

2.EditTextPreference

3.ListPreference

4.MultiSelectListPreference  Android 3.0(API level 11) 引入,与check box list相等效果。

5.RingtonePreference 当你想构建一个为设置Notification的视图,会很有用。

具体不解释,试试才是真谛。

 

你还可以自定义自个儿的preference控制视图,通过继承Preference类。更多细节,参考

http://developer.android.com/reference/android/preference/Preference.html

 

使用Intents向Preference Screens里导入系统Preferences

除了刚才那些,preference层级视图中你还可以加入其它APP的Preference Screen.

先看片段:

<?xml version=”1.0” encoding=”utf-8”?> 
<PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android” 
         android:title=”Intent preference” 
         android:summary=”System preference imported using an intent”>

      <intent android:action=”android.settings.DISPLAY_SETTINGS “/> 
</PreferenceScreen>

 

系统视此intent为请求,会调用startActivity()。

android.provider.Settings 类包含一些android.settings.*常量,这些常量可以被用来调用系统设置界面。

 

3.0前与现在的不同

以往是:先定义个xml Preference Screen布局文件,然后再创建个类继承PreferenceActivity,在onCreate中:

@Override 
public void onCreate(Bundle savedInstanceState) { 
   super.onCreate(savedInstanceState); 
   addPreferencesFromResource(R.xml.preference_layout);  
} 

 

注意这是一种xml资源。

activity都是要注册的。

 

3.0后步骤是:先创建个继承于PreferenceFragment的类,在其onCreate中:

@Override 
public void onCreate(Bundle savedInstanceState) { 
    // TODO Auto-generated method stub 
    super.onCreate(savedInstanceState); 
    this.addPreferencesFromResource(R.xml.preference_layout); 
    
}

 

然后写个xml preference-headers文件:

<?xml version="1.0" encoding="utf-8"?> 
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > 
    <header 
        android:fragment="com.example.test.PreFragment" 
        android:icon="@drawable/ic_launcher" 
        android:title="My Preferences" 
        android:summary="Description of these preferences"/> 
    
</preference-headers>

 

最后创建个继承于PreferenceActivity的类,重写方法onBuildHeaders:

@Override 
public void onBuildHeaders(List<Header> target) {

    super.onBuildHeaders(target); 
    this.loadHeadersFromResource(R.xml.pref_header, target); 
    
}

 

preference-header也是属于xml资源。

总结:

3.0前,关系是  PreferenceScreen xml---PreferenceActivity

3.0后,关系是  PreferenceScreen xml-PreferenceFragmentpreference-header—PreferenceActivity

 

header还可以引入Intents:

<header android:icon=”@drawable/ic_settings_display” 
            android:title=”Intent” 
            android:summary=”Launches an Intent.”> 
            <intent android:action=”android.settings.DISPLAY_SETTINGS “/> 
</header>

 

这个header就没有指定PreferenceFragment了.

 

向后兼容

为你的程序预先创建2种版本:

Class c = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? 
   MyPreferenceActivity.class : MyFragmentPreferenceActivity.class;

Intent i = new Intent(this, c); 
startActivityForResult(i, SHOW_PREFERENCES);

 

Build.VERSION.SDK_INT 意思是你当前项目编译的版本,如果小于Build.VERSION_CODES.HONEYCOMB 3.0,那么我就选择MyPreferenceActivity.class,否则MyFragmentPreferenceActivity。

上述是一种兼容解决方案。

 

说了这么多,如何使用由Preference Screens所设置的Shared Preferences呢?

答:

Context context = getApplicationContext(); 
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 
// TODO Retrieve values using get<type>

使用PreferenceManager.getDefaultSharedPreferences获得的SharedPreferences,是可以被APP所有组件共享的。

 

介绍Shared Preference Change Listeners

既然是个监听者,那就说说什么时候会触发。

当Shared Preference值被增加,移除或者修改的时候触发。

通常这会非常有用,给Activities和Services使用Shared Preferences框架去设置APP preferences,如果合适的话,以此来更新UI和APP的行为。

 

什么是App Preferences?

答:就是前面提到的PreferenceManager.getDefaultSharedPreferences方式获得的,具有全局意义的,可以被app所有组件共享的。

下面看个实现的例子:

public class MyActivity extends Activity implements 
  OnSharedPreferenceChangeListener {

  @Override 
  public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState);

     // Register this OnSharedPreferenceChangeListener 
     SharedPreferences prefs = 
       PreferenceManager.getDefaultSharedPreferences(this); 
     prefs.registerOnSharedPreferenceChangeListener(this); 
  }

  public void onSharedPreferenceChanged(SharedPreferences prefs, 
                                              String key) { 
     // TODO Check the shared preference and key parameters 
     // and change UI or behavior as appropriate. 
  } 
}

 

总结:有3种SharedPreference

1.以PreferenceManager.getDefaultSharedPreferences,这种保存的可以被App所有组件获取

2.Activtiy.getSharedPreferences 这个方法,可以指定一个名字,只要名字正确,也是可以全局被获取。

3.Activity.getPreferences()这个方法所保存的是针对于当前的activity,不是全局。

 

使用Shared Preferences保存Activity状态

例子:

// Create or retrieve the activity preference object. 
SharedPreferences activityPreferences = 
getPreferences(Activity.MODE_PRIVATE);

// Retrieve an editor to modify the shared preferences. 
SharedPreferences.Editor editor = activityPreferences.edit();

// Retrieve the View 
TextView myTextView = (TextView)findViewById(R.id.myTextView);

// Store new primitive types in the shared preferences object. 
editor.putString(“currentTextValue”, myTextView.getText().toString());

// Commit changes. 
editor.apply();

 

使用Activity生命周期函数来保存和恢复Activity实例状态

Activity提供onSaveInstanceState回调函数去保存与UI状态相关的数据,主要情形呢:activity在后台因为资源紧张被杀死,待资源恢复后,或者是硬件配置改变后导致的重启,需要恢复之前的UI状态。

private static final String TEXTVIEW_STATE_KEY = “TEXTVIEW_STATE_KEY”;

@Override 
public void onSaveInstanceState(Bundle saveInstanceState) { 
   // Retrieve the View 
   TextView myTextView = (TextView)findViewById(R.id.myTextView);

   // Save its state 
   saveInstanceState.putString(TEXTVIEW_STATE_KEY, 
     myTextView.getText().toString());

   super.onSaveInstanceState(saveInstanceState); 
}


在super.onSaveInstanceState之前,保存你想要保存的UI状态数据。

当Activity重启的时候,saveInstanceState变量会传给onCreateonRestoreInstanceState.

注意:正常的结束是不会产生saveInstanceState变量的。所谓正常,就是在前台用户选择了退出。(如按返回键)导致了finish,触发onDestory。

恢复的小DEMO:

@Override 
public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main);

     TextView myTextView = (TextView)findViewById(R.id.myTextView);

     String text = “”; 
     if (savedInstanceState != null && 
          savedInstanceState.containsKey(TEXTVIEW_STATE_KEY)) 
       text = savedInstanceState.getString(TEXTVIEW_STATE_KEY);

     myTextView.setText(text); 
}

 

使用生命周期函数来保存和恢复 Fragment实例状态

大多数APP的UI,都会内嵌在Fragments中。相应的,Fragments也包含了一个onSaveInstanceState回调函数,很像activity。

它所产生的Bundle对象,会传递给onCreate,onCreateView,onActivityCreated。

如果一个Activity因为硬件配置改变,实例被杀死然后重启,比如屏幕翻转等,你可以让你的fragment实例不做任何改变,通过在Fragment的onCreate中调用setRetainInstance即可,这样的话相关的activity重启,也不管它的事儿了,fragment的实例不会被杀死,就不会被重新创建。

注意:onCreate和onDestory那时候不会被触发了,即便如此,onAttach,onCreateView,onActivityCreated,onStart,onResume和它们的一致的事件函数还是会被调用。

(在onCreate方法中保存初始化,这样就最大化的保存状态了,效率比起以前没有fragment还是有好处的)

public class MyFragment extends Fragment {

            private static String USER_SELECTION = “USER_SELECTION”; 
            private int userSelection = 0; 
            private TextView tv;

            @Override 
            public void onCreate(Bundle savedInstanceState) { 
              super.onCreate(savedInstanceState); 
              setRetainInstance(true); 
              if (savedInstanceState != null) 
                 userSelection = savedInstanceState.getInt(USER_SELECTION); 
            }

            @Override 
            public View onCreateView(LayoutInflater inflater, 
                                          ViewGroup container, 
                                          Bundle savedInstanceState) { 
              View v = inflater.inflate(R.layout.mainfragment, container, false);

              tv = (TextView)v.findViewById(R.id.text); 
              setSelection(userSelection);

              Button b1 = (Button)v.findViewById(R.id.button1); 
              Button b2 = (Button)v.findViewById(R.id.button2); 
              Button b3 = (Button)v.findViewById(R.id.button3);

              b1.setOnClickListener(new OnClickListener() { 
                 public void onClick(View arg0) { 
                   setSelection(1); 
                 }

           });

           b2.setOnClickListener(new OnClickListener() { 
              public void onClick(View arg0) { 
                setSelection(2); 
              } 
           });

           b3.setOnClickListener(new OnClickListener() { 
              public void onClick(View arg0) { 
                setSelection(3); 
              } 
           });

           return v; 
         }

         private void setSelection(int selection) { 
           userSelection = selection; 
           tv.setText(“Selected: “ + selection); 
         }

         @Override 
         public void onSaveInstanceState(Bundle outState) { 
           outState.putInt(USER_SELECTION, userSelection); 
           super.onSaveInstanceState(outState); 
         }  
}

 

你可能感兴趣的:(android,存储)