安卓:EditText导致内存泄漏

10

首先我想说,这是我第一次处理性能问题,因为这是我第一次开发安卓应用程序。

应用程序

该应用程序是一个源代码编辑器,您可以在其中打开文件,修改它们并将其保存回来。该应用程序由4部分组成:

  • 导航视图:包含ListView用于打开文件和TreeView用于打开文件夹。
  • 代码视图容器:这个视图包含实际代码的视图。
  • 代码容器:这是一个小视图,包含一个文本视图和一个自定义EditText(由我扩展EditText类创建,但尚未实现,因此它的行为与EditText完全相同)。 TextView只显示代码行。
  • 打开和保存片段:有两个我用作DialogFragment的片段:保存片段让您浏览本地文件系统以及链接帐户的Dropbox文件系统,并保存当前文件。打开片段让您浏览相同的文件系统并打开文件。

问题

在完成了基本代码编辑器之后,我开始进行语法高亮。现在,我想澄清,即使没有语法高亮,也会生成泄漏,因此这不是问题。

无论如何,通过测试语法高亮,我打开了“大”文件(1200行代码),我注意到应用程序变得非常缓慢,这是显而易见的,因为我正在对整个文本进行正则表达式匹配(我将通过仅突出显示可见文本来避免这种情况)。这促使我测试没有语法高亮的大文件的应用程序,并发现应用程序仍然有点慢,并且我注意到发生了一些内存泄漏。

特别是当我打开一个大文件(1200行代码)时,应用程序需要1秒钟才能在textview中显示代码行,当我键入字符时,绘制缓慢。此外,每当我键入或删除字符时,就会发生内存泄漏。

检查

我尝试检查堆(使用MAT),但正如我所说,我没有任何经验,不确定该怎么做来调查这个问题。很抱歉,我无法上传截图(stackoverflow没有权限),但我可以向您报告一些数字:

打开大文件之前的系统

系统总览

enter image description here

泄漏嫌疑

enter image description here

问题1 enter image description here

详情: enter image description here

问题2 enter image description here

问题3 enter image description here

最大顶级支配包

enter image description here enter image description here

最大对象

enter image description here

打开大文件后的系统情况

系统概述

enter image description here

泄漏嫌疑:

enter image description here

问题1enter image description here 详情: enter image description here

问题2enter image description here

问题3 问题4 enter image description here

最大顶级支配包

enter image description here enter image description here

最大对象

enter image description here

来自 Android 设备监视器:

打开大文件之前的系统情况

enter image description here

打开大文件后的系统情况

enter image description here

分配的一些部分:

输入图片说明文字 输入图片说明文字 输入图片说明文字 提前致谢

编辑:

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

编辑

好的,大家我找到问题了。如果你们看到了,每次我输入一些东西时,我都会更新行EditText,由于文本很长(1200行),重新计算需要一段时间。即使考虑过这个问题!我必须找到一种更快的方法来显示代码行。一种选择是为每行使用一个TextView,在这种方式中,我只更新需要更改的TextView。但我不知道拥有1200个TextView对象是否好。

    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;
/** * 代码编辑视图 */ 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); }
/** * 初始化方法 * @param 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 <= usedLines; i++) { text += i + System.lineSeparator(); }
lines.setText(text); }
/** * 设置字体 * @param typeFace 字体 */ public void setFont(Typeface typeFace) { this.lines.setTypeface(typeFace); this.code.setTypeface(typeFace); } }

编辑: 除了最近发现的,没有语法高亮时打字速度很快之外,启用语法高亮后我仍然遇到了卡顿。当我打开文件时,高亮非常快,但打字仍然很慢,而且会出现内存泄漏的消息。

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

出现了垃圾回收。其中,“GC_EXPLICIT”表示手动启动的垃圾回收,“freed 185K”表示释放了185KB的内存,“17% free 6027K/7244K”表示总共有7244KB的内存,其中6027KB是可用的。

不过我想知道这个1字节数组(byte[],boolean[])是什么东西,因为它实际上使用了2MB的内存。你有什么建议吗?

编辑:

终于找到问题所在。由于文件很大并且创建了很多段落,当我在文件顶部进行更改时,编辑框必须重新计算所有段落的位置。


SO应该允许您附加图片。否则请将它们上传到某个地方,以便我们可以查看。 - Jonas Czech
这个视频也提供了一些提示,其中大部分你已经完成了。不过还是值得一看的。 - Jonas Czech
1个回答

3

许多人遇到了与您相同的问题。以下是一些提示:

来自codeninja

那么实际解决方案是什么?避免在RelativeLayout中使用EditText,改用LinearLayout。根据James的说法,如果您查看DDMS,输入文本时会发生大量重绘和重新计算,这与RelativeLayout有关。因此,这使我们发现问题确实是RelativeLayout。更新:我忘了提到设置EditText的固定宽度将对性能有很大帮助。它可以防止布局的重新计算和重新绘制。感谢Giorgos Kylafas在评论区指出的内容!他还包括了一些在Android性能提示方面可能有用的链接,建议您阅读他的评论。

在第一种情况下,EditText的宽度为“wrap_content”。每次更改文本,即EditText的内容,视图都需要重新测量和重新布局,这很慢。被包含在RelativeLayout内会使事情变得更糟,因为RelativeLayout总是多次通行。

在第二种情况下,EditText的宽度固定为“220 dip”。其测量和布局通行简单而快速。再加上没有使用“layout_weight”,所以其父LinearLayout是单通行的。 http://developer.android.com/guide/topics/ui/how-android-draws.html

来自另一个stackoverflow 问题:

避免在RelativeLayout中使用EditText,改用LinearLayout。

来自另一个stackoverflow 问题:

我在使用ListView中的EditText时遇到了类似的问题,通过使用加权宽度将EditText宽度设置为0dp来修复了该问题,以匹配/填充父级。

我不确定为什么会出现这种情况,但我认为是因为当EditText的宽度设置为包裹内容时,它将调整/重新绘制自身以使所有内容都适合,而ListView也将尝试重新绘制自身以使所有内容都适合。因此,通过使EditText具有固定宽度,可以避免此重新绘制。

总之:一定不要将EditText的宽度设置为wrap-content!


你好,感谢回复。但实际上我的EditText是fill_parent fill_parent,并且它在一个线性布局中。我将编辑问题并附上XML。 - Christopher A
尝试设置固定宽度!你能分享一下你的EditText自定义组件的代码吗? - Matt
我会将其添加到问题中。 - Christopher A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接