在使用Gallery组件时,我遇到了只能归类为ScrollView元素的内存泄漏的内容。
简短背景。我有一个现有的应用程序是一个照片幻灯片应用程序。 它使用Gallery组件,但适配器中的每个元素都以全屏显示。 (完整资料来源于this link)
适配器View元素包含一个ImageView,以及两个用于标题和描述的TextView。 由于这些照片分辨率非常高,因此该应用程序使用了相当多的内存,但Gallery通常会设法好好回收它们。
但是,当我现在为描述TextView实现ScrollView时,我几乎立即遇到了内存问题。这是我做的唯一改变
<ScrollView
android:id="@+id/description_scroller"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:fillViewport="true">
<TextView
android:id="@+id/slideshow_description"
android:textSize="@dimen/description_font_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_below="@id/slideshow_title"
android:singleLine="false"
android:maxLines="4"/>
</ScrollView>
我做了堆转储,可以清楚地看到Scrollview是内存问题的根源。
以下是堆转储分析的两个屏幕截图。请注意,ScrollView保留对mParent的引用,其中包含我使用的大型照片
如果我使用TextView的滚动(android:scrollbars =“vertical”和.setMovementMethod(new ScrollingMovementMethod()),PS会出现同样的问题;
PSS尝试关闭持久绘图缓存,但没有不同的dreaandroid:persistentDrawingCache =“none”
答案 0 :(得分:4)
您是否尝试在容器视图滚出屏幕时删除滚动视图?我不确定这对你有用但值得一试吗?或者,尝试在滚动视图离开屏幕时调用setScrollContainer(false)。这似乎从mScrollContainers集中删除了视图。
此外,由Dianne Hackborn(安卓工程师)回答的this question明确表示不在Gallery中使用可滚动视图。也许这个问题是为什么?
答案 1 :(得分:3)
添加此内容 - &gt;机器人:isScrollContainer = “假”
<ScrollView
android:id="@+id/description_scroller"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:fillViewport="true"
android:isScrollContainer="false">
有一些消息来源出现这种情况: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/view/View.java
问题是:setScrollContainer(boolean isScrollContainer)
默认情况下:
boolean setScrollContainer = false;
但在某些情况下喜欢这个
if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {
setScrollContainer(true);
}
它可能是真的,当它发生时
/ ** *更改此视图是否为其中一组可滚动容器 *它的窗口。这将用于确定窗口是否可以 *当软输入区域打开时,调整大小或必须平移 - 可滚动 *容器允许窗口使用自容器以来的调整大小模式 *将适当缩小。 * /
public void setScrollContainer(boolean isScrollContainer) {
if (isScrollContainer) {
if (mAttachInfo != null && (mPrivateFlags&SCROLL_CONTAINER_ADDED) == 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= SCROLL_CONTAINER_ADDED;
}
mPrivateFlags |= SCROLL_CONTAINER;
} else {
if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) {
mAttachInfo.mScrollContainers.remove(this);
}
mPrivateFlags &= ~(SCROLL_CONTAINER|SCROLL_CONTAINER_ADDED);
}
}
mAttachInfo.mScrollContainers.add(this) - 放入ArrayList的所有视图都会导致内存泄漏
答案 2 :(得分:2)
是的,我注意到了这个问题,对不起我以前的评论,我试图清空Drawables
通过设置上一个Drawable.setCallBack(null);
但没有工作,顺便说一句,我有几乎相同的项目,我使用ViewFlipper
而不是画廊,所以我可以控制每一件事,我只是在其中使用2个视图,并切换它们,没有内存泄漏,为什么不在显示之前调整图像大小,这样会减少内存使用量(在读取之前搜索SO以调整图像大小)
答案 3 :(得分:0)
尝试将TextView中的“android:layout_below =”@ id / slideshow_title“移动到ScrollView。
答案 4 :(得分:0)
最后实现了一个使用TextSwitcher的变通方法,该变换器每隔x秒自动更改为剩余的子字符串。
以下是布局
中的相关xml定义 <TextSwitcher
android:id="@+id/slideshow_description"
android:textSize="@dimen/description_font_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/slideshow_description_anim1"
android:textSize="@dimen/description_font_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="@color/white"
android:singleLine="false"/>
<TextView
android:id="@+id/slideshow_description_anim2"
android:textSize="@dimen/description_font_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="@color/white"
android:singleLine="false"/>
</TextSwitcher>
这里我将过渡动画添加到TextSwitcher(在适配器的getView方法中)
final TextSwitcher slideshowDescription = (TextSwitcher)slideshowView.findViewById(R.id.slideshow_description);
Animation outAnim = AnimationUtils.loadAnimation(context,
R.anim.slide_out_down);
Animation inAnim = AnimationUtils.loadAnimation(context,
R.anim.slide_in_up);
slideshowDescription.setInAnimation(inAnim);
slideshowDescription.setOutAnimation(outAnim);
以下是我如何交换描述的部分
private void updateScrollingDescription(SlideshowPhoto currentSlideshowPhoto, TextSwitcher switcherDescription){
String description = currentSlideshowPhoto.getDescription();
TextView descriptionView = ((TextView)switcherDescription.getCurrentView());
//note currentDescription may contain more text that is shown (but is always a substring
String currentDescription = descriptionView.getText().toString();
if(currentDescription == null || description==null){
return;
}
int indexEndCurrentDescription= descriptionView.getLayout().getLineEnd(1);
//if we are not displaying all characters, let swap to the not displayed substring
if(indexEndCurrentDescription>0 && indexEndCurrentDescription<currentDescription.length()){
String newDescription = currentDescription.substring(indexEndCurrentDescription);
switcherDescription.setText(newDescription);
}else if(indexEndCurrentDescription>=currentDescription.length() && indexEndCurrentDescription<description.length()){
//if we are displaying the last of the text, but the text has multiple sections. Display the first one again
switcherDescription.setText(description);
}else {
//do nothing (ie. leave the text)
}
}
最后,我在这里设置了定时器,使其每3.5秒更新一次
public void setUpScrollingOfDescription(){
final CustomGallery gallery = (CustomGallery) findViewById(R.id.gallery);
//use the same timer. Cancel if running
if(timerDescriptionScrolling!=null){
timerDescriptionScrolling.cancel();
}
timerDescriptionScrolling = new Timer("TextScrolling");
final Activity activity = this;
long msBetweenSwaps=3500;
//schedule this to
timerDescriptionScrolling.scheduleAtFixedRate(
new TimerTask() {
int i=0;
public void run() {
activity.runOnUiThread(new Runnable() {
public void run() {
SlideshowPhoto currentSlideshowPhoto = (SlideshowPhoto)imageAdapter.getItem(gallery.getSelectedItemPosition());
View currentRootView = gallery.getSelectedView();
TextSwitcher switcherDescription = (TextSwitcher)currentRootView.findViewById(R.id.slideshow_description);
updateScrollingDescription(currentSlideshowPhoto,switcherDescription);
//this is the max times we will swap (to make sure we don't create an infinite timer by mistake
if(i>30){
timerDescriptionScrolling.cancel();
}
i++;
}
});
}
}, msBetweenSwaps, msBetweenSwaps);
}
最后我可以把这个问题搁置一边:)