【问题标题】:make a property loader for an android application为android应用程序制作一个属性加载器
【发布时间】:2014-08-29 11:38:12
【问题描述】:

我正在开发一个 android 应用程序,我可能需要更改几个变量,但不想重新编译并将 android 应用程序部署到 android 智能手机中。

在 java 中,我会像之前在 java 中所做的那样做一个属性加载器:

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"));
    }
}

虽然在 android 中有一个叫做 SharedPreferences 的东西,我已经在我的应用程序中使用了它。虽然我从不使用 SharedPreferences 直接在文件中更改变量信息,但只能从应用程序的代码中更改。

Android 应用程序中的最佳替代方案是什么?

因为我想要实现的是,对我来说,更好地由属性加载器表示,它可以保存我不想在我的 java 代码中硬编码的东西。

【问题讨论】:

  • 您是要保存变量状态还是将文件保存到设备? Android 为这两者提供了内置解决方案。
  • 我想存储对应用程序运行至关重要的静态信息,但它不会以编程方式添加或删除变量。那些是什么?
  • SharedPreferences 将满足您的需求。我只想阅读更多关于文档的内容。如果这不符合您的需求,不妨看看 Parse SDK,看看它是否适合您。
  • 我使用并理解 SharedPreferences。但我想更改文件中的变量。所以我可以更改 ips 和端口之类的东西,应用程序将使用它们来执行与服务器的通信
  • 甚至共享首选项都存储在文件中,因此您实际上可以像属性文件一样每次替换文件

标签: java android sharedpreferences properties-file


【解决方案1】:

您可以使用 xml 文件来存储您的配置和访问方式,就像 key 是标签和 value 是标签值一样。

例如-

路径 = yourapp/res/xml/RememberMeServer.xml

RememberMeServer.xml 内容-

<?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>

然后去访问和使用key-value -

类代码 -

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"));

类 XMLData.java - (该类包含按键访问值的逻辑)

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;
}

注意:

您可以更改文件RememberMeServer.xml 中任何键的任何值。 这提供了灵活性,您不必担心获取密钥的保存值。无论值是什么,方法都会通过它们的键返回这些值。

SharedPreferences 也是一件好事,但正如您所说,您有很多变量会发生很大变化,因此最好的解决方案是将它们全部放在一个 xml 文件中,并在需要时访问它们。您可以根据要求更改任何值,并且仍然是特定于内容的。整个逻辑集中在一个 xml 文件中,您只需查看和更改一个文件即可修改任何值。

【讨论】:

  • 有趣的是,您的回答基本上归结为我试图在回答中提出的同一点。仅使用应用程序资源是最好的选择,尽管我个人不喜欢 XML 方法。在我看来,使用XmlPullParser 似乎很乱......只是getResources().getString(...) 或类似的东西更干净,更不容易出错。
  • 是的,但是如果你想保持一个清晰的层次结构,元素包含键值,并且这些元素进一步具有子元素,每个子元素都有键值。没有XmlPullParser 的可读性与有点容易。一种基于布局的方式来处理单个文件中的所有内容。尽管如此,值得 OP 根据他/她的项目考虑什么是合适的。
  • 是的,这确实取决于项目,两种方法都有优点。
  • 好的,我喜欢这个答案,它看起来确实类似于我在问题中显示的属性加载器概念。去试试看。
  • 在哪里初始化 int start = this.xmldoc.indexOf(""+ key + ""); 中使用的 xmldoc;
【解决方案2】:

选项 1:应用资源

我真的不明白为什么你不使用正常的应用程序资源?根据您的描述,这正是您正在寻找的。您可以在其中指定各种值,并且 ip 和 port 以及其他重要值应该始终在资源中。例如,如果您在某处需要一个整数,您可以在 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>

或者可以在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>

您在这些资源中定义的所有值都可以在以后随时加载:

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

您可以在the official documentation找到更多关于应用资源的信息。


选项 2:共享首选项

另一个选项当然是SharedPreferences。在内部,SharedPreferences 被保存到 File。但我想这不是您要寻找的,因为它仍然需要您对所有初始值进行硬编码。


选项 3:花哨的东西

如果你愿意,我也可以像这样拍一些花哨的东西:

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

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

如您所见,它使用反射和注释来加载资源值。可能非常方便,可能正是您正在寻找的。您可以通过调用以下代码从 Object 加载所有带注释的值:

ResourceLoader.load(context, object);

这个ResourceLoader的源码没什么花哨,不过目前只支持加载字符串和整数资源,不过扩展这个应该没问题:

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!");
    }
}

【讨论】:

  • 好的,谢谢你的回答如果我使用这些资源我可以从文件系统访问它们吗?还是我必须将它们读到应用程序中更改它们?
  • 对不起,我不明白你的问题。
  • 如果我想在不访问应用程序的情况下更改这些属性,我应该怎么做?是否有存储它们的文件,我可以轻松更新它们?
  • 资源中的所有内容都不是静态的。但是,如果您想知道,同样的事情也适用于其他答案。要更新您需要更新应用的资源。
  • 但是更新你的应用程序很容易,所以我想如果你不需要动态更改资源也没关系。
猜你喜欢
  • 2012-01-22
  • 2019-10-15
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
相关资源
最近更新 更多