【问题标题】:Out Of Memory Exception occuring in android even after recycling Bitmap即使在回收位图后,android中也会出现Out Of Memory Exception
【发布时间】:2016-04-30 13:39:26
【问题描述】:

我已经阅读了很多关于这个主题的主题,并尝试在我的代码中使用它们,但我仍然无法解决它。

我想要做什么: 就像 facebook 或任何其他从服务器下载包含图像的提要的应用程序一样,我也在做同样的事情并尝试在图像视图上显示提要和图像。

发生了什么: 我能够下载包含图像 URL 的提要(以 JSON 格式),并且能够在 ImageView 上显示它们。 在模拟器中,我的最大堆大小为 64MB。我在前 10 个提要中消耗了大约 30MB(不知道为什么,但这是我从 Android Studio 的 Android 监视器中的内存选项卡甚至在 Android 设备监视器中得到的)。 我的应用程序中有一个刷新按钮,它在删除之前填充的所有提要后重新加载相同的提要。我预计我会消耗相同的内存或更多。但与此相反,我的内存使用量增加到 42MB。因此,在刷新 3 到 4 次后,它会导致 OutOFMemory Execption。即使我一次加载下 10 个或 50 个提要,我也会遇到 OutOfMemory 异常。

我知道 facebook instagram 和更多这样的应用程序做同样的事情,但不确定他们如何实现代码来覆盖这种情况。

以下是我用于填充提要的代码

private void loadFeed(List<Feed> feedList)
{
    Log.v(Constant.TAG,"Loading Feed in social feed page");
    for(final Feed feed:feedList) {
        LinearLayout feedBox = new LinearLayout(this);
        feedBox.setOrientation(LinearLayout.VERTICAL);
        FrameLayout profileDetailContainer= new FrameLayout(this);
        LinearLayout profileDetailContainerParent=new LinearLayout(this);
        LinearLayout profileDetailContainerChild=new LinearLayout(this);
        profileDetailContainerChild.setOrientation(LinearLayout.VERTICAL);
        ImageView imgProfile= new ImageView(this);
        TextView txtDate= new TextView(this);
        TextView txtName= new TextView(this);
        ImageView imgProduct= new ImageView(this);
        txtName.setText(feed.getUserFullName());
        TextView txtDesciption= new TextView(this);
        txtName.setTypeface(null, Typeface.BOLD);
        if(feed.getDescription().length()>Constant.NUMBER_OF_DESCRIPTION_CHAR_SHOW_ON_RESULT)
        {
            txtDesciption.setText(feed.getDescription().substring(0,Constant.NUMBER_OF_DESCRIPTION_CHAR_SHOW_ON_RESULT)+"...");
        }
        else
            txtDesciption.setText(feed.getDescription());

        if(!IOUtil.fileExists(this,feed.getProductImageName())) {
            WebRequest request = new WebRequest();
            request.setUrl(Constant.ROOT_APPLICATION_URL_WITH_SEPARATOR + feed.getImgPath());
            request.setParam(feed.getId() + "");
            new ImageDownloadTask(this, true, feed.getProductImageName(), this).execute(request);
            Log.v(Constant.TAG,"URL:"+Constant.ROOT_APPLICATION_URL_WITH_SEPARATOR+feed.getImgPath());
            Picasso.with(getApplicationContext()).load(R.drawable.logo).into(imgProduct);
            feedBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ScreenUtility.alertException(v.getContext(),"Please wait untill product image loads");
                }
            });
            PixyfiSession.save(feed.getId() + "", imgProduct);
            PixyfiSession.save(feed.getId() + "_feed", feed);

        }
        else
        {
            ImageUtil.recycleIfPossible(imgProduct);
            try {
                imgProduct.setImageBitmap(ImageUtil.getLocalImage(feed.getProductImageName(),this));
                FeedboxOnClickListener feedboxOnClickListener = new FeedboxOnClickListener(feed);
                feedBox.setOnClickListener(feedboxOnClickListener);
            } catch (FileNotFoundException e) {
                Log.v(Constant.TAG,e.getMessage(),e);
            }

        }

        if(FacebookUtil.localProfilePicExists(feed.getUserName(),this))
        {
            Bitmap profileImage= FacebookUtil.getLocalProfilePicture(feed.getUserName(),this);
            imgProfile.setImageBitmap(profileImage);
        }
        else {
            FacebookUtil.loadProfilePicture(feed.getUserName(),this,this);
            PixyfiSession.save(feed.getUserName(),imgProfile);
            imgProfile.setImageResource(R.drawable.profile);
        }
        try {

            if(feed.getDate()==null) {
                txtDate.setText(new SimpleDateFormat("dd MMM yyyy").format(new SimpleDateFormat("yyyy-MM-dd").parse(feed.getFeedDate())));
            }
            else {
                txtDate.setText(new SimpleDateFormat("dd MMM yyyy").format(feed.getDate()));
            }
        } catch (ParseException e) {
            Log.e(Constant.TAG,e.getMessage(),e);
        }
        LinearLayout.LayoutParams layoutParam= new LinearLayout.LayoutParams(140,140);
        layoutParam.setMargins(5,5,0,0);
        imgProfile.setLayoutParams(layoutParam);

        layoutParam= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,Gravity.TOP|Gravity.LEFT);
        layoutParam.setMargins(20,5,0,0);
        txtName.setLayoutParams(layoutParam);
        layoutParam= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,Gravity.LEFT);
        layoutParam.setMargins(20,5,0,0);
        txtDate.setLayoutParams(layoutParam);

        profileDetailContainerParent.addView(imgProfile);
        profileDetailContainerChild.addView(txtName);
        profileDetailContainerChild.addView(txtDate);
        profileDetailContainerParent.addView(profileDetailContainerChild);
        feedBox.addView(profileDetailContainerParent);
        LinearLayout.LayoutParams feedLayoutParam=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT,Gravity.CENTER_VERTICAL);
        feedLayoutParam.setMargins(5,5,5,5);
        imgProduct.setLayoutParams(feedLayoutParam);
        txtDesciption.setLayoutParams(feedLayoutParam);
        feedBox.addView(txtDesciption);
        feedBox.addView(imgProduct);
        feedLayoutParam=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT,Gravity.CENTER_VERTICAL);
        feedLayoutParam.setMargins(0,5,0,5);
        feedBox.setLayoutParams(feedLayoutParam);

        feedBox.setBackgroundColor(Color.parseColor("#cccccc"));
        PixyfiSession.save(feed.getId()+"_feedbox",feedBox);
        imgProfile.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PixyfiSession.save(Constant.SELECTED_USER_ID,feed.getUserName());
                ScreenUtility.loadScreen(v.getContext(),UsersProfile.class,false);
            }
        });
        this.feedContainer.addView(feedBox);

    }
    if(feedList.size()==Constant.FEED_SIZE_IN_ONE_REQUEST)
    {
        Button moreFeed= new Button(this);
        moreFeed.setText("Load MOre Feed");
        moreFeed.setOnClickListener(new MoreFeedButtonListener(this));
        this.feedContainer.addView(moreFeed);
    }
    this.progressBar.setVisibility(View.INVISIBLE);
}

用于重新加载/刷新提要

this.currentPage=1;
this.recycleImages();
this.feedContainer.removeAllViews();
PixyfiSession.save(Constant.CURRENT_FEED_PAGE,this.currentPage);
this.progressBar.setVisibility(View.VISIBLE);
loadFeed();

recycleImages 方法:

private void recycleImages()
{
    for(int i=0;i<this.feedContainer.getChildCount();i++)
    {
        if(this.feedContainer.getChildAt(i) instanceof  ImageView)
        {
            ImageView view=(ImageView)this.feedContainer.getChildAt(i);
            ImageUtil.recycleIfPossible(view);
        }
    }
}

如果您需要有关代码的更多详细信息,请告诉我。 还可以在 android 设备监视器中查看其他应用程序(如 facebook)的内存使用情况吗? 更新 ImageUtil.getLocalImage 方法

public static Bitmap getLocalImage(String imageName, Context context) throws FileNotFoundException
{
    Bitmap bitmap=null;
    InputStream is=null;
    if(IOUtil.fileExists(context,imageName)) {
        is = context.openFileInput(imageName);
        bitmap= BitmapFactory.decodeStream(is);
    }
    else {
        throw new FileNotFoundException("Image file doesn't exists");
    }
    return bitmap;
}

【问题讨论】:

  • UI 组件太多了,你应该增加布局,这样可以减少视图泄漏的机会。而且,如果您可以使用回收站视图,那将会很有帮助。
  • 我不关注你。我正在动态添加视图,如果这就是膨胀布局的意思。你能再解释一下吗?
  • 通过扩展布局均值,您应该在 xml 文件中定义布局,然后使用它。它将易于管理,recyclerview 将帮助您再次重用相同的视图,最终在提要加载期间使用更少的视图数
  • 实际上观看次数我不知道。我一次收到 10 个提要,用户可以加载接下来的 10 个提要,我需要在同一活动/屏幕中显示这些提要。因此,我动态地创建了 10 个以上的布局/视图以添加到我的线性布局中。当用户重新填充提要时,我会从线性布局的提要容器中删除所有视图并开始添加新提要。
  • 不,我的意思是 recyclerview ,替代列表视图检查链接developer.android.com/reference/android/support/v7/widget/…

标签: java android out-of-memory picasso


【解决方案1】:

我正在添加答案:

您应该始终更喜欢列表视图或更好的回收器视图,而不是自己管理视图组件。 因为它可以帮助您避免视图泄漏并更好地重用您已经创建的视图。

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,将此行添加到应用程序标记中的清单中:

        <application
            android:largeHeap="true"
            .
            .
            .
    

    【讨论】:

    • 很遗憾,这并不能解决问题,只能拖延。您需要使用适当的小部件或视图类型,如上面接受的答案。
    【解决方案3】:

    正如Saber Safavi 所说,先这样做,然后添加

     defaultConfig {
        ....
        multiDexEnabled true
        ....
    }
    

    在你的

    app/build.gradle

    文件..

    【讨论】:

    • Multidex 允许我编写超过 65536 个方法。我没有问题是指我的方法。如果我错了,请纠正我。
    【解决方案4】:

    看起来您在加载图片时遇到了问题。请检查您的ImageUtils.getLocalImages,您是解码图像的缩放版本并将其加载到内存中,还是将原始图像加载到内存中?

    关注this document from Android developer,我认为这是在Android中高效加载图像的最佳教程。

    【讨论】:

    • 尝试对图像进行采样,结果我发现我的模拟器的宽度为 1080 像素(我正在使用全宽度的图像视图),而我正在加载的图像最大为 1024 像素。所以这也行不通。
    • 你能告诉我这个方法ImageUtils.getLocalImages的实现吗?
    【解决方案5】:

    Android 中的图像处理非常棘手。更好地使用为处理所涉及的情况和复杂性而构建的库。 我更喜欢 UniversalImageLoader 或 Piccaso!

    【讨论】:

    • 出于同样的原因,我使用毕加索。它也在上面的代码中。你觉得我使用毕加索的方式有什么问题吗?
    • 您好 Kumar,您为什么要使用名为 getLocalImage 的方法?据我了解,毕加索负责处理磁盘映像。如果不是请纠正我。谢谢。
    • Ruchira:我猜你误会我了。实际上我对 android 很陌生,这就是为什么我要求你纠正我。
    • 嗯,我觉得这可能是我正在做的错误。我会尽力让你知道。谢谢
    • 好的,库马尔!祝你好运!
    【解决方案6】:

    这是因为您在运行时内存中保留了太多位图图像,请尽量不要创建太多图像并尝试将它们无效。

    移动设备通常具有有限的系统资源。 Android 设备可以为单个应用程序提供低至 16MB 的内存。虚拟机兼容性为各种屏幕尺寸和密度提供所需的最低应用程序内存。应优化应用程序以在此最小内存限制下执行。但是,请记住,许多设备都配置了更高的限制。

    我不会推荐你在 android manifest 中使用 heapSize 标志。我希望你不是在开发游戏 :),如果你愿意,宁愿去改造或任何其他图像处理库。我建议您查看 Google 提供的 BitmapFun 示例。它可以在开发者门户上找到。

    http://developer.android.com/training/displaying-bitmaps/index.html

    干杯!!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多