Store static data in Android - custom resource?
Asked Answered
A

4

22

I'm new to Android development, and I've been playing around with it a bit. I was trying to create a program that has a small database-like collection of never-changing data. In C#, my currently best language, I'd use a List of a custom class and serialize that to an xml file, then read that into my application at runtime. I found the /xml resource folder in Android, but I'm not sure how I would go about doing what I'm envisioning. What would be the best way to go about doing this?

The data will never need to change. Example:

Blob   | A  | B
----------------
Blob 1 | 23 | 42
Blob 2 | 34 | 21

I know that's laid out like a table, but using a database doesn't really make sense to me because the data will never change, and I would need a way to store it to initially populate the database anyway.

So basically I'm looking for a way to store somewhat-complex static data in my application. Any ideas?

EDIT: I also saw the /raw folder. So I could store things in /res/raw or /res/xml. But I'm not sure what would be the best way to store/parse the data...

Appraisal answered 7/7, 2010 at 15:24 Comment(0)
J
15

The best way is to use the Android Resource Heirarchy.

In the res/values/ directory, you can store any number of key-value pairs for several basic data types. In your app, you would refer to them using an autogenerated resource id (name based on your resource's key). See the link above for more documentation and details.

Android also supports raw datafiles. You could store your data in the file directory under res/raw/yourfile.dat

You you create your data in whatever text based format you want and then read it on activity startup using the resource access apis.

Janina answered 7/7, 2010 at 16:9 Comment(5)
would that be better or worse than storing it in an xml form and reading it in? and if I did use an xml form, how would I read it in?Appraisal
@Appraisal Better in that you could store in any format you want, worse in you don't have built in APIs for parsing the data. You can also use the resources hierarchy which would be better. I'll edit my post with the details.Janina
What I'm worried about with the res/values/ is that I don't plan on just storing data in one-to-one pairs (as in the example above). So are there built in apis for parsing xml files other than those in the hierarchy specified in the link you posted? EDIT: Would TypedArray work for a custom class (like the "Blob" example above)? Would that be the way to go?Appraisal
@Appraisal Yes there is. "developer.android.com/reference/android/content/res/…"Janina
Ah, I had seen that before but hadn't looked too deeply into it. Thanks! So that is a good way to go then. I didn't want to break any conventions...Appraisal
C
17

I think this is the BEST solution and i am already using this one to store Static-data in my every project.

For that... You can do one thing, make one xml file namely "temp.xml" ..and store the data in temp.xml as follows:

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

    <subelement> Blob 1  
     <subsubelement> 23 </subsubelement>
     <subsubelement> 42 </subsubelement> 
    </subelement>

    <subelement>Blob 2      
    <subsubelement> 34 </subsubelement>
    <subsubelement> 21 </subsubelement>
    </subelement>


    </rootelement1>

and then use XML PullParser technique to parse data. You can have coding samples of PullParsing technique on Example , refer this example for better idea.

Enjoy!!

Crypto answered 8/7, 2010 at 5:29 Comment(4)
Yep, that's the technique I ended up going with, as discussed on the comments of the accepted answer. I actually found the instructions here to be the best for XmlPullParser: ibm.com/developerworks/opensource/library/x-android/index.htmlAppraisal
is't possible to update this xml like field value to change when person modifies it.For ex <subsubelement>Blob 1<subsubelement> to <subsubelement> sumit Sharma<subsubelement>Virga
@SumitSharma // once the file is in the Asset or Resource folder, you cannot change it. because it is baked as a compiled package. you need to copy it to sdcard or somewhere and to modify it.Motorcycle
Where should one store this static XML file? I'm looking for a way to store static data in android and thinking of doing this way. Also, is this way still the preferred way of doing? (since this was answered few years back).Generalize
J
15

The best way is to use the Android Resource Heirarchy.

In the res/values/ directory, you can store any number of key-value pairs for several basic data types. In your app, you would refer to them using an autogenerated resource id (name based on your resource's key). See the link above for more documentation and details.

Android also supports raw datafiles. You could store your data in the file directory under res/raw/yourfile.dat

You you create your data in whatever text based format you want and then read it on activity startup using the resource access apis.

Janina answered 7/7, 2010 at 16:9 Comment(5)
would that be better or worse than storing it in an xml form and reading it in? and if I did use an xml form, how would I read it in?Appraisal
@Appraisal Better in that you could store in any format you want, worse in you don't have built in APIs for parsing the data. You can also use the resources hierarchy which would be better. I'll edit my post with the details.Janina
What I'm worried about with the res/values/ is that I don't plan on just storing data in one-to-one pairs (as in the example above). So are there built in apis for parsing xml files other than those in the hierarchy specified in the link you posted? EDIT: Would TypedArray work for a custom class (like the "Blob" example above)? Would that be the way to go?Appraisal
@Appraisal Yes there is. "developer.android.com/reference/android/content/res/…"Janina
Ah, I had seen that before but hadn't looked too deeply into it. Thanks! So that is a good way to go then. I didn't want to break any conventions...Appraisal
C
4

I have used Simple for xml parsing in the past. I think it has the least amount of code if you know what to expect in xml, which in your case you do.

http://simple.sourceforge.net/

Cirilla answered 17/9, 2012 at 20:5 Comment(0)
Q
4

According to the doc, /xml is the way to go.

Providing Resources

xml/ Arbitrary XML files that can be read at run-time by calling

Resources.getXML().

Various XML configuration files must be saved here, such as a searchable configuration.

Documentation for getXML()

I also made a working example:

  • the XML structure:

    <?xml version="1.0" encoding="utf-8"?>
    <quizquestions>
        <quizquestion>
            <header_image_src>ic_help_black_24dp</header_image_src>
            <question>What is the Capital of U.S.A.?</question>
            <input_type>Radio</input_type>
            <answer correct="false">New York City</answer>
            <answer correct="true">Washington D.C.</answer>
            <answer correct="false">Chicago</answer>
            <answer correct="false">Philadelphia</answer>
        </quizquestion>
    
        <quizquestion>
            <header_image_src>ic_help_black_24dp</header_image_src>
            <question>What is the family name of the famous dutch painter Vincent Willem van .... ?</question>
            <input_type>EditText</input_type>              
            <answer correct="true">Gogh</answer>
        </quizquestion>
    </quizquestions>
    
  • the Java class to hold parsed data:

    public class QuizQuestion {
        private int headerImageResId;
        private String question;
        private String inputType;
        private ArrayList<String> answers;
        private ArrayList<Boolean> answerIsCorrect;
        private ArrayList<Integer> correctAnswerIndexes;
    
        /**
         * constructor for QuizQuestion object
         */
        QuizQuestion() {
            headerImageResId = 0;
            question = null;
            inputType = null;
            answers = new ArrayList<>();
            answerIsCorrect = new ArrayList<>();
            correctAnswerIndexes = new ArrayList<>();
        }
    
    
        public void setHeaderImageResId(int headerImageResId) {
            this.headerImageResId = headerImageResId;
        }
    
        public int getHeaderImageResId() {
            return headerImageResId;
        }
    
    
        void setQuestion(String question) {
            this.question = question;
        }
    
        public String getQuestion() {
            return question;
        }
    
    
        void setInputType(String inputType) {
            this.inputType = inputType;
        }
    
        public String getInputType() {
            return inputType;
        }
    
        void addAnswer(String answer, boolean isCorrect)
        {
            if (isCorrect)
                correctAnswerIndexes.add(answers.size());
            answers.add(answer);
            answerIsCorrect.add(isCorrect);
        }
    
    
        public ArrayList<String> getAnswers() {
            return answers;
        }
    
        public String getAnswer(int index)
        {
            // check index to avoid out of bounds exception
            if (index < answers.size()) {
                return answers.get(index);
            }
            else
            {
                return null;
            }
        }
    
        public int size()
        {
            return answers.size();
        }
    
    }
    
  • the parser itself:

    /**
     * Created by bivanbi on 2017.02.23..
     *
     * class to parse xml resource containing quiz data into ArrayList of QuizQuestion objects
     *
     */
    
    public class QuizXmlParser {
    
        public static String lastErrorMessage = "";
    
        /**
         * static method to parse XML data into ArrayList of QuizQuestion objects
         * @param activity is the calling activity
         * @param xmlResourceId is the resource id of XML resource to be parsed
         * @return null if parse error is occurred or ArrayList of objects if successful
         * @throws XmlPullParserException
         * @throws IOException
         */
        public static ArrayList<QuizQuestion> parse(Activity activity, int xmlResourceId)
                throws XmlPullParserException, IOException
        {
            String logTag = QuizXmlParser.class.getSimpleName();
            Resources res = activity.getResources();
            XmlResourceParser quizDataXmlParser = res.getXml(R.xml.quiz_data);
    
            ArrayList<String> xmlTagStack = new ArrayList<>();
            ArrayList<QuizQuestion> quizQuestions = new ArrayList<>();
    
            QuizQuestion currentQuestion = null;
    
            boolean isCurrentAnswerCorrect = false;
    
            quizDataXmlParser.next();
            int eventType = quizDataXmlParser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT)
            {
                //  begin document
                if(eventType == XmlPullParser.START_DOCUMENT)
                {
                    Log.d(logTag,"Begin Document");
                }
                //  begin tag
                else if(eventType == XmlPullParser.START_TAG)
                {
                    String tagName = quizDataXmlParser.getName();
                    xmlTagStack.add(tagName);
                    Log.d(logTag,"Begin Tag "+tagName+", depth: "+xmlTagStack.size());
                    Log.d(logTag,"Tag "+tagName+" has "+quizDataXmlParser.getAttributeCount()+" attribute(s)");
    
                    // this is a beginning of a quiz question tag so create a new QuizQuestion object
                    if (tagName.equals("quizquestion")){
                        currentQuestion = new QuizQuestion();
                    }
                    else if(tagName.equals("answer"))
                    {
                        isCurrentAnswerCorrect = quizDataXmlParser.getAttributeBooleanValue(null,"correct",false);
                        if (isCurrentAnswerCorrect == true) {
                            Log.d(logTag, "Tag " + tagName + " has attribute correct = true");
                        }
                        else
                        {
                            Log.d(logTag, "Tag " + tagName + " has attribute correct = false");
                        }
                    }
                }
                //  end tag
                else if(eventType == XmlPullParser.END_TAG)
                {
                    String tagName = quizDataXmlParser.getName();
                    if (xmlTagStack.size() < 1)
                    {
                        lastErrorMessage = "Error 101: encountered END_TAG "+quizDataXmlParser.getName()+" while TagStack is empty";
                        Log.e(logTag, lastErrorMessage);
                        return null;
                    }
                    xmlTagStack.remove(xmlTagStack.size()-1);
                    Log.d(logTag,"End Tag "+quizDataXmlParser.getName()+", depth: "+xmlTagStack.size());
    
                    //  reached the end of a quizquestion definition, add it to the array
                    if (tagName.equals("quizquestion")){
                        if (currentQuestion != null)
                            quizQuestions.add(currentQuestion);
                        currentQuestion = null;
                    }
                }
                //  text between tag begin and end
                else if(eventType == XmlPullParser.TEXT)
                {
                    String currentTag = xmlTagStack.get(xmlTagStack.size()-1);
                    String text = quizDataXmlParser.getText();
                    Log.d(logTag,"Text: "+text+", current tag: "+currentTag+", depth: "+xmlTagStack.size());
    
                    if (currentQuestion == null) {
                        Log.e(logTag,"currentQuestion is not initialized! text: "+text+", current tag: "+currentTag+", depth: "+xmlTagStack.size());
                        continue;
                    }
    
                    if (currentTag.equals("header_image_src"))
                    {
                        int drawableResourceId = activity.getResources().getIdentifier(text, "drawable", activity.getPackageName());
                        currentQuestion.setHeaderImageResId(drawableResourceId);
                    }
                    else if (currentTag.equals("question"))
                    {
                        currentQuestion.setQuestion(text);
                    }
                    else if (currentTag.equals("answer"))
                    {
                        currentQuestion.addAnswer(text, isCurrentAnswerCorrect);
                    }
                    else if (currentTag.equals("input_type"))
                    {
                        currentQuestion.setInputType(text);
                    }
                    else
                    {
                        Log.e(logTag,"Unexpected tag "+currentTag+" with text: "+text+", depth: "+xmlTagStack.size());
                    }
                }
                eventType = quizDataXmlParser.next();
            }
            Log.d(logTag,"End Document");
            return quizQuestions;
        }
    
    }
    
  • and finally, calling the parser:

    //  read quiz data from xml resource quiz_data
    try {
        quizQuestions = QuizXmlParser.parse(this,R.xml.quiz_data);
        Log.d("Main","QuizQuestions: "+quizQuestions);
    
    } catch (XmlPullParserException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        quizQuestions = null;
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        quizQuestions = null;
    }
    
    if (quizQuestions == null)
    {
        Toast.makeText(this,"1001 Failed to parse Quiz XML, sorry", Toast.LENGTH_LONG).show();
        finish();
    }
    
Quanta answered 23/2, 2017 at 18:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.