0

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 ?

MBH
  • 16,271
  • 19
  • 99
  • 149
  • 1
    Why don't you just create a xml and store it whatever you want? Why border to use `SharedPreferences `? – Joshua Jun 21 '16 at 08:27
  • you cannot set a custom path for sharedPreferences, it´s impossible. Do it like Joshua said. With an xml file located on your custom path, you can do the same... – Opiatefuchs Jun 21 '16 at 08:29
  • @Joshua if it is possible, why should I rewrite it again ! – MBH Jun 21 '16 at 08:34
  • @MBH You can just write an adapter with all the methods you used and map it to the actual xml class. – Joshua Jun 21 '16 at 08:36
  • @Joshua i need to save Key-Value sets to the file thats why i asked for sharedpreferences – MBH Jun 21 '16 at 08:56
  • 1
    @MBH For example, if your want to use `getString(String key, String defValue)` from `SharedPreferences`. You can write a class called `XMLPreferences` and have a method `getString(String key, String defValue)`. This method is actually look up from an XML file and return if it exists. In the XML file, you can save like `YOUR_VALUE` or use the key as the tag name like `YOUR_VALUE` – Joshua Jun 21 '16 at 09:02
  • yeah exactly thats what i was looking for @Joshua – MBH Jun 21 '16 at 09:06
  • 1
    @MBH I will write a long answer for you =) – Joshua Jun 21 '16 at 09:10
  • you are great :) @Joshua thank you in advance – MBH Jun 21 '16 at 09:12

4 Answers4

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

Joshua
  • 5,901
  • 2
  • 32
  • 52
  • Thank you alot, i was already trying to learn parsing xml but you were way faster than me – MBH Jun 21 '16 at 09:48
  • 1
    You should learn about SAX. This example is using DOM. DOM is easy to use and understand but it uses large memory. – Joshua Jun 21 '16 at 09:50
  • please check my answer for json, thank you for your answer it inspired me for the JSON shared preferences – MBH Jun 22 '16 at 08:13
1

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

MBH
  • 16,271
  • 19
  • 99
  • 149
0

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.

Alex
  • 617
  • 4
  • 15
  • saving shared preferences to sdcard, is not breaking the sandbox rule, cuz usually we can write and read files to sdcard with gaining the required permission – MBH Jun 21 '16 at 08:50
  • @MBH, this is not safe, because every different app can change your application settings (that may cause some destructive changes). But if you want it, you can see the answer here: http://stackoverflow.com/a/6030399/3546306 – Alex Jun 21 '16 at 09:01
  • 1
    @Alexander You can place the file in external storage with mode private. – Joshua Jun 21 '16 at 09:06
0

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.

Gogu CelMare
  • 699
  • 6
  • 15