近在做一個項目,想到要使用SharedPreferences類去存儲一些簡單的數據,但是我們知道,使用SharedPreferences保存數據,其實質是采用了xml文件存放數據,路徑為:/data/data/<package name>/shared_prefs. 但是有些時候,我們不想將xml文件存儲到SharedPreferences累指定的目錄下,比如說,我想將xml文件存儲到sdcard下面,這個時候,我們該怎么辦呢?
我們知道,SharedPreferences是封裝好的類,我們只能使用類提供的方法,但是我們也知道,使用java的反射機制,類在我們面前就是透明的,我們可以任意使用類中的方法,變量,構造器等等。所以,這里,我想到使用java的反射機制去解決這個問題。但是在使用java的反射機制之前,有些概念我們還是要了解的。
Context類路徑:/frameworks/base/core/java/android/content/Context.java
說明:抽象類,提供了一組通用的API。
源代碼(部分)如下:
public abstract class Context {
...
public abstract Object getSystemService(String name); //獲得系統級服務
public abstract void startActivity(Intent intent); //通過一個Intent啟動Activity
public abstract ComponentName startService(Intent service); //啟動Service
//根據文件名得到SharedPreferences對象
public abstract SharedPreferences getSharedPreferences(String name,int mode);
...
}
ContextIml.java類 路徑 :/frameworks/base/core/java/android/app/ContextImpl.java
說明:該Context類的實現類為ContextIml,該類實現了Context類的功能。請注意,該函數的大部分功能都是直接調用。
其屬性mPackageInfo去完成。
源代碼(部分)如下:
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context{
//所有Application程序公用一個mPackageInfo對象
/*package*/ ActivityThread.PackageInfo mPackageInfo;
@Override
public Object getSystemService(String name){
...
else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
}
else if (INPUT_METHOD_SERVICE.equals(name)) {
return InputMethodManager.getInstance(this);
}
}
@Override
public void startActivity(Intent intent) {
...
//開始啟動一個Activity
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
}
}
ContextWrapper類 路徑 :\frameworks\base\core\java\android\content\ContextWrapper.java
說明: 正如其名稱一樣,該類只是對Context類的一種包裝,該類的構造函數包含了一個真正的Context引用,即ContextIml對象。源代碼(部分)如下:
public class ContextWrapper extends Context {
Context mBase; //該屬性指向一個ContextIml實例,一般在創建Application、Service、Activity時賦值
//創建Application、Service、Activity,會調用該方法給mBase屬性賦值
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent); //調用mBase實例方法
}
}
Context相關類的繼承關系
SharedPreferences原則上只能保存在當前應用程序私有的Shared_prefs目錄中,但利用Java的反射機制可以改變系統內定的文件存儲路徑。
例如:將config.xml文件保存在SD卡的根目錄中。
private void savePreToSDcard() {
try {
Field field; // 獲取ContextWrapper對象中的mBase變量。該變量保存了ContextImpl對象
field = ContextWrapper.class.getDeclaredField("mBase");
field.setAccessible(true);
// 獲取mBase變量
Object obj = field.get(this);
// 獲取ContextImpl.mPreferencesDir變量,該變量保存了數據文件的保存路徑
field = obj.getClass().getDeclaredField("mPreferencesDir");
field.setAccessible(true);
// 創建自定義路徑
File file = new File("/sdcard");
// 修改mPreferencesDir變量的值
field.set(obj, file);
SharedPreferences mySharedPreferences = getSharedPreferences( "config", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putString("name", "20130310");
editor.commit();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}