问题
I want to keep my SharedPreferences xml in a different place where default path is the application package.
Can we set a custom path for android SharedPreferences and use it while it is in that path ?
回答1:
You can create a adapter to get preferences.
First, create an interface IPreferences
(Not necessary but make your life easier if you want to change it back or change to another method):
public interface IPreferences {
boolean contains(String key);
int getInt(String key, int defValue);
String getString(String key, String defValue);
void putInt(String key, int value);
void putString(String key, String value);
}
(Some methods are left out, most of them are redundant)
Then, you implement with a xml storage class XMLPreferences
.
public class XMLPreferences implements IPreferences {
private final String path;
public XMLPreferences(String path) {
this.path = path;
}
@Override
public boolean contains(String key) {
return getContentByKey(key) == null;
}
@Override
public int getInt(String key, int defValue) {
if( getContentByKey(key) == null)
return defValue;
return Integer.valueOf(key);
}
@Override
public String getString(String key, String defValue) {
if( getContentByKey(key) == null)
return defValue;
return defValue;
}
@Override
public void putInt(String key, int value) {
putContentByKey(key, value);
}
@Override
public void putString(String key, String value) {
putContentByKey(key, value);
}
private String getContentByKey(String key) {
try {
FileInputStream fileInputStream = new FileInputStream(path);
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse(fileInputStream);
Element root = dom.getDocumentElement();
NodeList nodes = root.getElementsByTagName(key);
if (nodes.getLength() > 0)
return nodes.item(0).getTextContent();
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
return null;
}
private void putContentByKey(String key, String content) {
try {
FileInputStream fileInputStream = new FileInputStream(path);
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse(fileInputStream);
Element root = dom.getDocumentElement();
NodeList nodes = root.getElementsByTagName(key);
if (nodes.getLength() > 0)
nodes.item(0).setTextContent(content);
else {
Element newElement = dom.createElement(key);
newElement.setTextContent(content);
root.appendChild(newElement);
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(new File(path));
Source input = new DOMSource(dom);
transformer.transform(input, output);
} catch (ParserConfigurationException | SAXException | IOException | TransformerException e) {
e.printStackTrace();
}
}
}
Finally, you can call the following:
IPreferences pref = new XMLPreferences(YOUR_PATH);
Later, if you want to change the implementation, you can implement a new class with IPreferences. Then, you can just change the initialization without changing other parts.
P.S. This uses DOM Praser which may facing performance issue if you have a large XML file. You may want use other XML Praser instead
回答2:
Big thanks to @Joshua This answer is inspired by his code, i added something to the same interface and i implemented it with Json,
public interface IPreferences {
boolean contains(String key);
int getInt(String key, int defValue);
String getString(String key, String defValue);
boolean getBoolean(String key, boolean defValue); // added
void putInt(String key, int value);
void putBoolean(String key, boolean value); // added
void putString(String key, String value);
// easiness of use
void put(String key, String value);
void put(String key, int value);
void put(String key, boolean value);
}
and this is the implementation with Json :
public class MBJsonSharedPrefs implements IPreferences {
String filePath;
JSONObject mJSONObject;
public MBJsonSharedPrefs(String filePath){
this.filePath = filePath;
try {
if(!MBFileUtils.FileExists(filePath)){
// this is important for the first time
MBFileUtils.CreateFile(filePath, "{}"); // put empty json object
}
String json = MBFileUtils.ReadFile(filePath);
mJSONObject = new JSONObject(json);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean contains(String key) {
if(mJSONObject== null) return false;
return mJSONObject.has(key);
}
@Override
public int getInt(String key, int defValue) {
return tryParseInt(getContentByKey(key), defValue);
}
private int tryParseInt(String strVal, int defValue){
if(strVal == null) return defValue;
try{return Integer.parseInt(strVal);}
catch (Exception e){return defValue;}
}
@Override
public String getString(String key, String defValue) {
String value = getContentByKey(key);
return value==null?defValue:value;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
String value = getContentByKey(key);
return value==null?defValue:value.equals("t");
}
@Override
public void putInt(String key, int value) {
putContentByKey(key, value+"");
}
@Override
public void put(String key, int value) {
putInt(key, value);
}
@Override
public void putString(String key, String value) {
putContentByKey(key, value);
}
@Override
public void put(String key, String value) {
putString(key, value);
}
@Override
public void putBoolean(String key, boolean value) {
putContentByKey(key, value?"t":"f");
}
@Override
public void put(String key, boolean value) {
putBoolean(key, value);
}
public void commit() {
if(mJSONObject == null) return;
try {
MBFileUtils.WriteToFile(mJSONObject.toString(), filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
public void apply() {
MBThreadUtils.DoOnBackground(new Runnable() {
@Override
public void run() {
commit();
}
});
}
private String getContentByKey(String key) {
if(mJSONObject == null) return null;
if(!contains(key)) return null;
try {
return (String)mJSONObject.get(key);
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
private void putContentByKey(String key, String content) {
if(mJSONObject == null) return;
try {
mJSONObject.put(key, content);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
and this is how i use it:
// getting string
MBJsonSharedPrefs pref = new MBJsonSharedPrefs(pathToFile/settings.json).getString(KET_ID,"Default");
pref.apply(); // important to write to file
// putting string
MBJsonSharedPrefs pref = new MBJsonSharedPrefs(pathToFile/settings.json).putString(KET_ID,"111222333");
pref.apply();
// OR another way for putting string into the file
MBJsonSharedPrefs pref = new MBJsonSharedPrefs(pathToFile/settings.json).put(KET_ID,"111222333");
pref.apply();
This worked for me, Again thanks to @Joshua for his answer
回答3:
No, you can't, because in Android every application has his own sandbox. The only thing you can do - set name for SharedPreferences file (for the case, if you want to have different setting files):
SharedPreferences mSettings = context.getSharedPreferences(prefName, Context.MODE_PRIVATE);
Where prefName is name of the settings file.
回答4:
Simple thing I am doing. On startup (before anything) I copy my private preferences file from my own location to the location of the app app.folders/shared_prefs.
On shutdown of the app I copy the shared preferences back to my private folder...
I do this so that I don't loose some important settings between apk uninstall/install when I need to completely wipe up the old apk installation.
来源:https://stackoverflow.com/questions/37938853/sharedpreferences-to-a-custom-path-android