Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: EditText causing memory leak

I want to start by saying that it's the first time that I have to deal with performance, because it's the first time that I develop an android app.

The app

The app is a source code editor, where you can open files, modify them and save them back. The app consist of 4 parts:

  • The navigator view: contains a ListView for the open files and a TreeView for the open folders.
  • The code views container: this hold the views containing the actual code.
  • The code container: this is a little view containing a text view and a custom EditText (created by me extending EditText class, but its not implements yet, so it behave exactly like an EditText). The TextView is just showing the lines of code.
  • The open and save fragments: there are 2 fragments that I use as DialogFragment: the save fragment let you navigate the local file system as well as the Dropbox file system of the linked accounts and save the current file. The open fragment let you navigate the same file systems and open a file.

The problem

After I finished the base code editor, I moved to syntax highlighting. Now, I want to mke clear that the leaks are generated even without the syntax highlighting, so this is not the problem.

Anyway, by testing the syntax highlithing, I was opening "large" files (1200 lines of code) and I noticed that the app becomes extremly slow, which is obvious because I'm regexing the whole text (I will avoid this by highlight only the visible text). This push me to test the app without syntax highlithing with large files and I found that the app become still a bit slow and I noticed that some memory leaks happened.

In particular, when I open a large file (1200 lines of code) the app take 1 second to display the lines of code in the textview and when I type the drawing of the character is slow. In addition whenever I type of remove a character a memory leak happen.

The inspection

I tried to inspect the heap (with MAT), but as said I don't have any experience in this and I'm not sure what to do to investigate this problem. I'm sorry but I cannot upload screenshots (don't have permission by stackoverflow for that), but I can report you some numbers:

System before opening the large file

System overview

enter image description here

Leaks suspects

enter image description here

Problem 1 enter image description here

Details: enter image description here

Problem 2 enter image description here

Problem 3 enter image description here

Biggest top-level dominator packages

enter image description hereenter image description here

Biggest objects

enter image description here

System after opening the large file

System overview

enter image description here

Leaks suspects:

enter image description here

Problem 1: enter image description here Details: enter image description here

Problem 2: enter image description here

Problem 3 Problem 4 enter image description here

Biggest top-level dominator packages

enter image description hereenter image description here

Biggest objects

enter image description here

From the Android device monitor:

System before opening the large file

enter image description here

System after opening the large file

enter image description here

Some parts of the allocations:

enter image description hereenter image description hereenter image description here Thank you in advance

EDIT:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/codeScrollView"
android:fillViewport="true">

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:background="@drawable/lines_stroke"
        android:textColor="@android:color/white"
        android:text="@string/first_line"
        android:textSize="15dp"
        android:gravity="right"
        android:paddingLeft="15dp"
        android:paddingRight="5dp"
        android:id="@+id/edit_code_lines_view"/>

              <com.example.green.bachelorproject.customViews.codeEditView.TouchEditText
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/code_stroke"
        android:gravity="top"
        android:textColor="@android:color/white"
        android:textSize="15dp"
        android:paddingLeft="3dp"
        android:paddingRight="3dp"
        android:textCursorDrawable="@color/white"
        android:id="@+id/edit_code_content_view"/>

    </LinearLayout>
</ScrollView>

EDIT

Ok guys I found the problem. If you see, every time that I type something, I update the lines EditText and, since the text is long (1200 lines), it take a while to recompute it. Did event though about that! I have to find a faster way to show the lines of code. One option is to use one TextView for each line, in this way I only update the TextView that need to be changed. But I don't know if having 1200 TextView objects is nice.

    package com.example.green.bachelorproject.customViews.codeEditView;

    import android.content.Context;
    import android.graphics.Color;
    import android.graphics.Typeface;
    import android.text.Editable;
    import android.text.Spannable;
    import android.text.SpannableStringBuilder;
    import android.text.TextWatcher;
    import android.text.style.ForegroundColorSpan;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.TextView;

    import utils.Colorizer;
    import utils.Lexer;
    import com.example.green.bachelorproject.events.UpdateCacheFileEvent;
    import com.example.green.bachelorproject.R;

    import de.greenrobot.event.EventBus;
    import com.example.green.bachelorproject.internalFileSystem.InternalFile;

    import java.util.ArrayList;

    /**
     * Created by Green on 26/02/15.
     */
    public class CodeEditView extends LinearLayout {

        private Context context;
        private TextView lines;
        private EditText code;
        private Typeface currentTypeface;
        private InternalFile internalFile;
        private Lexer lexer;
        private Colorizer colorizer;

        public CodeEditView(Context context) {
            super(context);
            this.context = context;
            init(null);
        }   

        public CodeEditView(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
            init(attrs);
        }

        private void init(AttributeSet attrs) {
            //CHECK THIS
            LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            layoutInflater.inflate(R.layout.edit_code_layout, this);

    //        this.colorizer = new Colorizer();
    //        this.colorizer.setColor("String", Color.rgb(218, 220, 95));
    //        this.colorizer.setColor("Number", Color.rgb(173, 125, 255));
    //        this.colorizer.setColor("Character", Color.rgb(218, 220, 95));
    //        this.colorizer.setColor("Operator", Color.rgb(234, 38, 116));
    //        this.colorizer.setColor("Keyword", Color.rgb(234, 38, 116));
    //        this.colorizer.setColor("Identifier", Color.WHITE);
    //        this.colorizer.setColor("Type", Color.rgb(105, 216, 238));
    //        this.colorizer.setColor("Comment", Color.rgb(117, 113, 91));
            this.lexer = new Lexer();
            this.lines = (TextView) findViewById(R.id.edit_code_lines_view);
            //this.lines.setTypeface(currentTypeface);
            this.code = (EditText) findViewById(R.id.edit_code_content_view);
            //this.code.setTypeface(currentTypeface);
            this.code.addTextChangedListener(new TextWatcher() {



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

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

            @Override
            public void afterTextChanged(Editable s) {
    //                writeToFile();
                //EventBus.getDefault().post(new UpdateCacheFileEvent(code.getText().toString(), internalFile));
                //setLines();
            }
        });
    }

    private void setLines() {
        int usedLines = code.getLineCount();

        String text = "1" + System.lineSeparator();

        for(int i = 2; i  tokens = lexer.tokenize(content);
    //        SpannableStringBuilder text = new SpannableStringBuilder(content);
    //
    //        for(Lexer.Token t: tokens) {
    //            text.setSpan(new ForegroundColorSpan(colorizer.getColor(t)), t.start, t.end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    //        }

    //        code.setText(text);
    //        code.post(new Runnable() {
    //            @Override
    //            public void run() {
    //                setLines();
    //            }
    //        });
    }

    public void setFont(Typeface typeFace) {
        this.lines.setTypeface(typeFace);
        this.code.setTypeface(typeFace);
    }
    }

EDIT: Beside the recent discover, whithout syntax highlighting the typing is fast, but I still encounter lag when enabling the syntax highlighting. When I open the file, the highlight is very quick, but the typing is still slow and memory leak messages

04-28 04:49:58.119: D/dalvikvm(2437): GC_EXPLICIT freed 185K, 17% free 6027K/7244K, paused 1ms+1ms, total 5ms

appear. Anyway, I'm wondering what is the object 1-byte array (byte[], boolean[]) is, because it's actually using 2 MB. Any suggestions?

EDIT:

Definitely found the problem. Since the file is big and lot of spans are created, when I change something on the top of the file, the editext has to recalculate the position of all spans.

like image 642
Christopher A Avatar asked Apr 27 '15 21:04

Christopher A


1 Answers

many others faced your same problem. Here are a couple of hints:

from codeninja:

So what’s the actual solution? Avoid using EditText inside a RelativeLayout, use LinearLayout instead. According to James, If you look at the DDMS, a lot of redraws and recalculations occur while entering the text which is related to the RelativeLayout. So that gives us a clue the the problem is indeed the RelativeLayoutUpdate: I forgot to mention that setting a fixed with of an EditText will help a lot with the performance. It prevents re-calculation and re-drawing of layout. Thanks to Giorgos Kylafas for pointing it out in the comments section below! He also included links that can be useful for you when it comes to Android performance tips so I suggest reading his comment.

In the first case, EditText's width is "wrap_content". Everytime you change the text, i.e. EditText's content, the view needs to re-measure and re-layout, which is slow. Being contained insided a RelativeLayout makes things even worse, because RelativeLayout is always multi-pass.

In the second case, EditText's width is fixed to "220 dip". Its measuring and layout pass is simple and quick. Plus you use no "layout_weight", so its parent LinearLayout is single-pass. http://developer.android.com/guide/topics/ui/how-android-draws.html

From another stackoverflow question:

Avoid using EditText inside a RelativeLayout, use LinearLayout instead.

From another stackoverflow question:

I was having a similar issue using EditText inside a ListView, that was fixed by changing the EditText width to 0dp using weighted widths to match/fill the parent.

I don't know for sure why this was occurring, however I believe it is because when the width of the EditText is set to wrap content it will adjust/redraw itself so that everything fits, and the ListView will also attempt to redraw itself so everything fits. So by making the EditText have a fixed width, this redraw is no longer required.

In conclusion: be sure to not to set the width of the EditText to wrap-content!

like image 170
Matt Avatar answered Nov 03 '22 06:11

Matt