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 is a source code editor, where you can open files, modify them and save them back. The app consist of 4 parts:
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.
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:
Problem 1
Details:
Problem 2
Problem 3
Problem 1: Details:
Problem 2:
Problem 3 Problem 4
From the Android device monitor:
Some parts of the allocations:
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With