0

In my project I'm going to work with a lot of XML Data like this:

<person>
    <name>Alex</name>
    <data1>data1</data1>
    <data2>data2</data2>
    <data3>data3</data3>
</person>

<person>
    <name>paul</name>
    <data1>data1</data1>
    <data2>data2</data2>
    <data3>data3</data3>
</person>

Those XML files got like 600+ entrys. I call their sites with fragments and loading them takes pretty long and throws: "I/Choreographer: Skipped 47 frames! The application may be doing too much work on its main thread." I already know how to work around that with starting another thread. Still it takes some seconds to open the pages and the "keyword serach" I've implemented causes the Android Monitor to throw "I/art: Background partial concurrent mark sweep GC freed 83556(6MB) AllocSpace objects, 834(16MB) LOS objects, 45% free, 19MB/35MB, paused 296us total 124.540ms" like every 3rd time. I've already read about that thats a normal thing happning but should be avoided.

Since I'm still pretty new to Android I wonder if I'm doing it right or if I could handle the Data better using a database for example.

private static String getValue(String tag, Element element) {
    NodeList nodeList = element.getElementsByTagName(tag).item(0).getChildNodes();
    Node node = nodeList.item(0);
    return node.getNodeValue();
}



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    //return inflater.inflate(R.layout.fragment_coffee_pref, container, false);
    final View view =  inflater.inflate(R.layout.fragment_coffee_pref, container, false);

    try {
        // get XML file
        AssetManager assetManager = getActivity().getAssets();

        if(Locale.getDefault().getLanguage().equals("de")){
            is = assetManager.open("coffee_pref_DE.xml");
        }else{
            is = assetManager.open("coffee_pref.xml");
        }


        //init XML parser
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(is);

        Element element=doc.getDocumentElement();
        element.normalize();

        // init table
        LinearLayout CPcontainer = (LinearLayout) view.findViewById(R.id.CPcontainer);
        TableRow.LayoutParams w50Layout = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT,0.50f);
        TableRow.LayoutParams w100Layout = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT,1.0f);
        //paramsExample.setMargins(2, 2, 2, 2); // (left, top, right, bottom);

        // go throught XML List and insert rows for each entry
        NodeList nList = doc.getElementsByTagName("person");

        for (int i=0; i<nList.getLength(); i++) {

            Node node = nList.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {

                TableLayout tableLayout = new TableLayout(getActivity());
                tableLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));// assuming the parent view is a LinearLayout

                TableRow row1= new TableRow(getActivity());
                TableRow row2= new TableRow(getActivity());
                TableRow row3= new TableRow(getActivity());
                TableRow row4= new TableRow(getActivity());
                TableRow.LayoutParams lp = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT);
                row1.setLayoutParams(lp);
                row2.setLayoutParams(lp);
                row3.setLayoutParams(lp);
                row4.setLayoutParams(lp);

                Element element2 = (Element) node;

                // row 1
                TextView tvName = new TextView(getActivity());
                tvName.setLayoutParams(w100Layout);
                tvName.setText(Html.fromHtml("<a href=\""+getValue("link", element2)+"\">"+getValue("name", element2)+"</a>"));
                tvName.setMovementMethod(LinkMovementMethod.getInstance());
                tvName.setBackgroundResource(R.drawable.head_shape);
                tvName.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                row1.addView(tvName);
                tableLayout.addView(row1);

                // row 2
                TextView tvData1 = new TextView(getActivity());
                tvData1.setLayoutParams(w50Layout);
                tvData1.setText(getValue("data1", element2));
                tvData1.setBackgroundResource(R.drawable.cell_shape);
                tvData1.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                TextView textData1 = new TextView(getActivity());
                textData1.setText(R.string.tableData1);
                textData1.setLayoutParams(w50Layout);
                //textData1.setTypeface(null, Typeface.BOLD);
                textData1.setBackgroundResource(R.drawable.cell_shape);
                textData1.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                row2.addView(textData1);
                row2.addView(tvData1);
                tableLayout.addView(row2);

                // row3
                TextView tvData2 = new TextView(getActivity());
                tvData2.setLayoutParams(w50Layout);
                tvData2.setText(getValue("data2", element2));
                tvData2.setBackgroundResource(R.drawable.cell_shape);
                tvData2.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                TextView textData2 = new TextView(getActivity());
                textData2.setText(R.string.tableData2);
                textData2.setLayoutParams(w50Layout);
                //textData2.setTypeface(null, Typeface.BOLD);
                textData2.setBackgroundResource(R.drawable.cell_shape);
                textData2.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                row3.addView(textData2);
                row3.addView(tvData2);
                tableLayout.addView(row3);

                // row 4
                TextView tvData3 = new TextView(getActivity());
                tvData3.setLayoutParams(w50Layout);
                tvData3.setText(getValue("sugar", element2));
                tvData3.setBackgroundResource(R.drawable.cell_shape);
                tvData3.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                TextView textData3 = new TextView(getActivity());
                textData3.setText(R.string.tableData3);
                textData3.setLayoutParams(w50Layout);
                //textData3.setTypeface(null, Typeface.BOLD);
                textData3.setBackgroundResource(R.drawable.cell_shape);
                textData3.setPadding(30, 20, 30, 20); // (left, top, right, bottom);

                row4.addView(textData3);
                row4.addView(tvData3);
                tableLayout.addView(row4);

                // add table to layout
                CPcontainer.addView(tableLayout);
            }

        }


    } catch (Exception e) {e.printStackTrace();}


    searchforName = (EditText) view.findViewById(R.id.searchForName);

    searchforName.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {

                String searchedName = searchforName.getText().toString().toLowerCase();

                    LinearLayout CPcontainer = (LinearLayout) view.findViewById(R.id.CPcontainer);
                    for (int i = 1, j = CPcontainer.getChildCount(); i < j; i++) {
                        View view2 = CPcontainer.getChildAt(i);
                        if(view2 instanceof TableLayout) {
                            TableLayout table2 = (TableLayout) view2;

                                TableRow row2 = (TableRow) table2.getChildAt(0);
                                if (!TextUtils.isEmpty(searchedName)) {
                                    TextView searchableName = (TextView) row2.getChildAt(0);
                                    String sName = Html.fromHtml(searchableName.getText().toString()).toString();
                                    if (sName.startsWith(searchedName) || sName.equals(searchedName)) {
                                        //if (sName.toLowerCase().contains(searchedName)) {
                                        table2.setVisibility(View.VISIBLE);
                                    } else {
                                        table2.setVisibility(View.GONE);
                                    }
                                } else {
                                    table2.setVisibility(View.VISIBLE);
                                }
                            }
                    }
                }
            });


        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

        public void onTextChanged(CharSequence s, int start, int before, int count) {}
    });

    return view;
}

Also since I've implemented a HTML link inside the name TextView I can't search for it anymore and I couldn't figure out how to get around it.

String sName = Html.fromHtml(searchableName.getText().toString()).toString();

I'm gonna be thankful for every help or information about my issues!

  • You have two questions - you should ask them separately (the searchable text is deg a different question) – Jim Jan 21 '17 at 16:43
  • If you have html, then just extract the relevant data of use better string comparison than starts with and equals – OneCricketeer Jan 21 '17 at 17:01
  • is it seems to me that you have a TableLayout with 600+ rows. Is that correct? PS. If yes, that's a very bad idea and you should look into using ListView or RecyclerView instead. – Budius Jan 21 '17 at 18:28
  • Actually I'm having 600+ TableLayouts with 4 rows each. Why are many rows a bad idea? Could you provide information about the advantage of ListView instead of TableLayout? – Julia Müller Jan 22 '17 at 20:08

2 Answers2

0

XML parsing is just nasty in general for large documents- you create lots of small objects and it causes a lot of havoc in the garbage collector. Libraries that try to minimize object creation can help, but its always going to be bad.

So I wouldn't really worry about the GC warnings. In the end, you aren't going OOM or causing leaks. For the chorographer warning- when parsing large documents do it on another thread or on an AsyncTask. Then the main UI thread can continue to process normally. Send an event to the main thread when the parsing is done to make any UI changes.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
0

Mostly likely you've got a slow DocumentBuilder for parsing your XML. If you have that many entries you have a couple options:

  1. See if you can improve your builder performance by preventing validations, etc. (see this link as one approach: https://jdevel.wordpress.com/2011/03/28/java-documentbuilder-xml-parsing-is-very-slow/). I would recommend wrapping that in code to enable/disable it for debug purposes.

  2. Investigate the differences between DOM and SAX (What is the difference between SAX and DOM?) - you may want and be able to use the SAX approach.

  3. Look to change how your data is organized and/or split it into different files.

Last, XML has it's place but maybe you should use a different file format if performance is the highest priority then document structure/transfer/parsing can be optimized based on you needs better if you build a custom solution (generally this is only for very intense/advanced work and should be avoided if possible).

Community
  • 1
  • 1
Jim
  • 10,172
  • 1
  • 27
  • 36
  • First of all thanks for your answer! I'm going to read the links you provided tomorrow. Could you provide some more information about the file formats? Which would be the best to handle lots of data? – Julia Müller Jan 22 '17 at 20:04