-1

I am developing an android application and there are several variables that i might need to change wihtout wanting to recompile and deploy the android application into the android smartphone.

In java i would do a propertyloader like the following i have done in java before:

public class PropertyLoader {

    private static Properties props = null;

    /**
     * Method initializes the PropertyLoader by reading all configuration settings
     * from the RememberMeServer.conf file.
     */
    public static void initializeProperties() {
        String propFile = getCatalinaDirectory() + "RememberMeServer.conf";

        System.out.println(propFile);

        File configFile = new File(propFile);
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(configFile);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }

        props = new Properties();

        try {
            props.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Returns a string value from the configuration file.
     *
     * @param  key  a string which represents the key of the requested value in the configuration file
     * @return      the value of the requested key from the property file as a string or null if the
     *              requested key could not be found.
     */
    public static String getStringValue(String key) {
        return props == null ? null : props.getProperty(key);
    }

    /**
     * Returns an int value from the configuration file.
     *
     * @param  key  a string which represents the key of the requested value in the configuration file
     * @return      the value of the requested key from the property file as an int or null if the
     *              requested key could not be found.
     */
    public static Integer getIntValue(String key) {
        return props == null ? null : Integer.valueOf(props.getProperty(key));
    }

    /**
     * Returns the directory of the project�s workspace as a string
     *
     * @return      Returns the directory of the project�s workspace as a string
     */
    public static String getWorkspaceDirectory() {
        URL url = PropertyLoader.class.getClassLoader().getResource(
                "hibernate.cfg.xml");
        return url.getFile().substring(0,
                url.getFile().lastIndexOf("hibernate.cfg.xml"));
    }

    /**
     * Returns the directory of the servlet container catalina directory as a string
     *
     * @return      Returns the directory of the servlet container catalina directory as a string
     */
    public static String getCatalinaDirectory() {
        String workspace = getWorkspaceDirectory();
        return workspace
                .substring(0, workspace.lastIndexOf("RememberMeServer"));
    }
}

Although in android there is something called SharedPreferences which i already use in my application. Although i never use the SharedPreferences to change variable information directly in the file but only from the application's code.

What is the best alternative in an android application?

Because what i want to achieve is, to me, better represented by a property loader which saves things that i do not want to hard code in my java code.

  • Are you wanting to save variable states or save files to the device? Android has build in solutions for both. – Max McKinney Jul 08 '14 at 16:23
  • I want to store static information that is vital for the functioning of the application but it wont add or remove variables programatically. Which are those? – JoaoFilipeClementeMartins Jul 08 '14 at 16:25
  • SharedPreferences will do what you need. I would just read up more on the documentation. If that doesn't fit your needs perhaps take a look at the Parse SDK and see if that'll work for you. – Max McKinney Jul 08 '14 at 16:27
  • I use and understand SharedPreferences. But i want to change the variables from the file. so i can change stuff like ips and ports, which the app will use to perform communication to server – JoaoFilipeClementeMartins Jul 08 '14 at 16:28
  • even sharedprefs are stored in file, so you can actually replace the file everytime just like property file – nandeesh Jul 22 '14 at 06:39
  • What I really don't understand is why you are not using the normal resources? You can specify anything in there, integers, dimension, strings, values, do you currently hardcode ip and port? They should always be in the resources. – Xaver Kapeller Jul 22 '14 at 09:29
  • Yes, i am currently hardcoding them but i am only developing it. – JoaoFilipeClementeMartins Jul 22 '14 at 19:01

2 Answers2

1

You can use xml file to store your configuration and access the same way as key being the tag and value being the tag value.

For example-

Path = yourapp/res/xml/RememberMeServer.xml

RememberMeServer.xml contents -

<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
    <key>day</key>
    <integer>1</integer>
    <key>hour</key>
    <integer>12</integer>
    <key>minute</key>
    <integer>10</integer>
    <key>second</key>
    <integer>14</integer>
    <key>background</key>
    <string>Graphic_6_7_Red</string>
    <key>Online</key>
    <true/>   
</dict>
</plist>

Then to access and use the key-value -

Class Code -

String day, hour, minute, second, background;
boolean status;

int resId = getResources().getIdentifier("xml/" + RememberMeServer, 
            "string", getActivity().getPackageName());
XmlPullParser xpp0 = getResources().getXml(resId);
XMLData xml = new XMLData(xpp0);
day = (xml.getValue("day"));
hour= (xml.getValue("hour"));
second= (xml.getValue("second"));
minute= (xml.getValue("minute"));
background= (xml.getValue("Graphic_6_7_Red"));
status= (xml.checkFieldPresence("Online"));

Class XMLData.java - (This class contains the logic of accessing value by key)

public String getValue(String key) {
        int start = this.xmldoc.indexOf("<key>"+ key + "</key>");

        if(start == -1)
            return "";


        String xmldoc2 = this.xmldoc.substring(start);

        start = xmldoc2.indexOf("</key>");
        xmldoc2 = xmldoc2.substring(start);

        xmldoc2 = xmldoc2.substring(6);

        start = xmldoc2.indexOf(">");
        xmldoc2 = xmldoc2.substring(start + 1);

        int end = xmldoc2.indexOf("</");

        xmldoc2 = xmldoc2.substring(0, end);
        return xmldoc2;

    }

public boolean checkFieldPresence(String key)
{
        int start = this.xmldoc.indexOf(key + "</key>");

        if(start == -1)
            return false;
        else
            return true;
}

NOTE:

You can change any value for any key in your file RememberMeServer.xml. This provides the flexibility that you don't have to worry about getting the saved value of the key. Whatever will be the value, the methods return those values by their keys.

SharedPreferences is also a good thing but as you told that you have lots of variables that change a lot so the best solution is to put them all in a xml file and access them when needed. You can change any value according to the requirement and still be content specific. The whole logic is centered within a single xml file and you can look and change just a single file to modify any value.

sjain
  • 23,126
  • 28
  • 107
  • 185
  • It's interesting, your answer basically boils down to the same point I am trying to make in my answer. Just using the app resources is the best option, although personally I don't like the XML approach. Using an `XmlPullParser` just seems messy... Just `getResources().getString(...)` or something like that is much cleaner and less error-prone in my opinion. – Xaver Kapeller Jul 22 '14 at 13:30
  • yes but if you want to maintain a clear hierarchy with elements containing key-value and those elements further having sub-elements with each having key-value. The readability vs the bit easy without `XmlPullParser`. A layout based way to handle everything in a single file. Nevertheless, worthy for the OP to consider what's being suitable according to his/her project. – sjain Jul 22 '14 at 14:00
  • Yes it really depends on the project, both approaches have merit. – Xaver Kapeller Jul 22 '14 at 14:34
  • OK, i like this answer it really seems something similar to the property loader concept i display in my question. going to try it. – JoaoFilipeClementeMartins Jul 22 '14 at 19:04
  • where do you initialize the xmldoc used in int start = this.xmldoc.indexOf(""+ key + ""); – JoaoFilipeClementeMartins Aug 10 '14 at 16:08
0

Option 1: App Resources

What I really don't understand is why you are not using the normal app resources? From what you describe that would be exactly what you are looking for. You can specify all kinds of values in there and things like ip and port and other important values should always be in the resources anyway. If you for example need an integer somewhere you can just define an integer in res/values/ints.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <integer name="some_integer">27</integer>
    <integer name="another_integer">42</integer>   

</resources>

Or a string can be defined in res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="some_string">Some Text</integer>
    <string name="another_string">qwerty</integer>

</resources>

And all values you define in those resources can be loaded later on at any time:

Resources resources = getResources();
int someInteger = resources.getInteger(R.integer.some_integer);
String someString = resources.getString(R.string.some_string);

You can find more information about the app resources in the official documentation.


Option 2: SharedPreferences

Another option are of course the SharedPreferences. Internally the SharedPreferences are saved to a File. But I guess that this is not what you are looking for since it would still require you to hardcode all initial values.


Option 3: Something fancy

If you want I can also slap something fancy together like this:

@Resource(id = R.string.some_string)
private String someString;

@Resource(id = R.integer.some_integer)
private int someInteger;

As you can see this uses reflection and annotations to load resource values. Could be very convenient and might be exactly what you are looking for. You load all the annotated values from one Object by calling this:

ResourceLoader.load(context, object);

The source code of this ResourceLoader is nothing fancy, but currently it only supports loading string and integer resources, but it should be no problem to expand this:

public class ResourceLoader {

    // This is the definition of the @Resource annotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Resource {
        public int id();
    }

    public static void load(Context context, Object target) {
        final Class<?> cls = target.getClass();

        // This gets all declared fields, meaning all inherited fields are ignored
        final Field[] fields = cls.getDeclaredFields();

        for(Field field : fields) {
            field.setAccessible(true);

            final Class<?> type = field.getType();

            // We check if the Annotation is present
            if(field.isAnnotationPresent(Resource.class)) {
                final Resource annotation = field.getAnnotation(Resource.class);
                final int id = annotation.id();

                // And if it is present use the id and type of the field to load
                // the correct resource
                final Object value = loadValue(context, id, type);

                try {
                    // Finally we set the new value to the field
                    field.set(target, value);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("Could not set resource value to field " + field, e);
                }
            }
        }
    }

    private static Object loadValue(Context context, int id, Class<?> type) {
        final Resources resources = context.getResources();

        if(int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)) {
            return resources.getInteger(id);
        }

        if(String.class.isAssignableFrom(type)) {
            return resources.getString(id);
        }

        throw new IllegalStateException("Type \"" + type + "\" is not supported!");
    }
}
Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86