远程视图用于小部件更新超过了最大位图内存使用错误

12
我有一个小部件,可以解析XML源并显示其标题和图像。在这个小部件中,我使用了一个服务来定期更改内容(即标题和图像)。为此,我使用计时器类。当我们运行这个小部件时,一些内容会正常显示,但过了一段时间之后,它会强制关闭并显示一个错误,如“远程视图小部件更新超过了最大位图内存使用量(已用:2465280,最大值:2304000),总内存不能超过填充设备屏幕所需的内存”。请有人帮助我解决这个问题...提前感谢。
这是我的AppwidgetProvider= >
 public class myappwidgetprovider extends AppWidgetProvider {
    public static String urls="http://www.abc.com/en/rssfeeds/9/latest/rss.xml";
    // XML node keys
    static final String KEY_HEAD = "item"; // parent node
    //static final String KEY_TITLE = "title";
    static final String KEY_DATE = "pubDate";
    public static String headflag="english";
    public static String[] Title;
        public static String[] Description;
        public static String[] Tit;
        public static String[] Tit2;
        public static String[] Desc;
        public static String[] Desc2;
        public static String[] image;
    public static TextView flashnews;

    public static int i=0;

     public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds )
        {

         Log.i("Tag", "onCreateView");
         parse();


           RemoteViews remoteViews;
                        ComponentName thisWidget = new ComponentName(context,myappwidgetprovider .class);

                int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

                Intent intent = new Intent(context.getApplicationContext(),
                        Updatewidget.class);
                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
                            context.startService(intent);

               }





     public static void parse()
     {

            URL url;

            try {

                url = new URL(urls);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                if((conn.getResponseCode() == HttpURLConnection.HTTP_OK)){
                      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                      DocumentBuilder db = dbf.newDocumentBuilder();
                      Document doc;
                      doc = db.parse(url.openStream());
                      doc.getDocumentElement().normalize();

                      NodeList itemLst = doc.getElementsByTagName("item");
                      Description = new String[itemLst.getLength()];//........
                      Title = new String[itemLst.getLength()];
                      Tit=new String[itemLst.getLength()];
                      Tit2=new String[itemLst.getLength()];
                      Desc=new String[itemLst.getLength()];
                      Desc2=new String[itemLst.getLength()];
                      image= new String[itemLst.getLength()];

                      for(int i=0; i < itemLst.getLength(); i++){

                            Node item = itemLst.item(i);
                            if(item.getNodeType() == Node.ELEMENT_NODE){
                                  Element ielem = (Element)item;
                                  NodeList title = ielem.getElementsByTagName("title");
                                  NodeList date = ielem.getElementsByTagName("pubDate");
                                  NodeList description = ielem.getElementsByTagName("description");
                                  Tit[i]= title.item(0).getChildNodes().item(0).getNodeValue();
                                  Desc[i]= description.item(0).getChildNodes().item(0).getNodeValue();
                                  Tit2[i]=Translate.title(Tit[i]);
                                  Desc2[i]=Translate.description(Desc[i]);
                                  if(headflag=="malayalam")
                                    {
                                      Desc2[i]=Desc2[i].replace("read more","IqSpXÂ");
                                    }
                                  Title[i] =Tit2[i];
                                  if (Desc2[i].contains("<img ")){
                                      String img  = Desc2[i].substring(Desc2[i].indexOf("<img "));
                                      String cleanUp = img.substring(0, img.indexOf(">")+1);
                                      img = img.substring(img.indexOf("src=") + 5);
                                      int indexOf = img.indexOf("'");
                                      if (indexOf==-1){
                                          indexOf = img.indexOf("\"");
                                        }
                                      img = img.substring(0, indexOf);

                                //setImgLink(img);
                                    if(headflag=="malayalam")
                                    {
                                        String img2=img.replace("files","files/imagecache/android_320");
                                        Description[i]=Desc2[i].replace(img,img2);
                                        image[i]=img2;
                                    }

                                else
                                {
                                    String img2=img.replace("files","files/imagecache/android_1_img");
                                    Description[i]=Desc2[i].replace(img,img2);
                                    image[i]=img2;
                                }
                                  }
                                else
                                {
                                    Description[i] =Desc2[i];
                                }



                            }

                          }

                        }
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (DOMException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ParserConfigurationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SAXException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
     }

}

这是我的服务=>

public class Updatewidget extends Service {
    static String UPDATEMOOD ="UPDATEMOOD";
    public Intent newintent;
    public AppWidgetManager app;
    public RemoteViews newviews;
    public int[] newappid;
    int i=0;
    @Override
    public void onStart(final Intent intent, int startId) {

         Log.i("Tag", "Service Called!!!!!!!!!!!!!");

         newintent=intent;
                 int[] allWidgetIds = intent
                    .getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
         newappid=allWidgetIds;

         final AppWidgetManager appWidgetMan = AppWidgetManager.getInstance(this);
         app=appWidgetMan;
         final RemoteViews views = new RemoteViews(this.getPackageName(),R.layout.widget_main);
         newviews=views;
         views.setTextViewText(R.id.title, myappwidgetprovider.Title[0]);
         Bitmap bitmap;
            try {
                bitmap = BitmapFactory.decodeStream((InputStream)new URL(myappwidgetprovider.image[0]).getContent());


                views.setImageViewBitmap(R.id.imageView4, bitmap);
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         appWidgetMan.updateAppWidget(allWidgetIds, views); 

         new ProgressAsyncTask().execute();








    }



     public class ProgressAsyncTask extends 
        AsyncTask<Void, Integer, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub
             int delay = 5000; // delay for 5 sec.

             int period = 5000; // repeat every sec.

             Timer timer = new Timer();
              timer.scheduleAtFixedRate(new TimerTask() {

                     public void run() {
                         i++;
                         if(i==5)
                         {
                             i=0;
                         }

                         int[] allWidgetIds = newintent
                                    .getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                                 newviews.setTextViewText(R.id.title, myappwidgetprovider .Title[i]);
                         Bitmap bitmap;
                            try {
                                bitmap = BitmapFactory.decodeStream((InputStream)new URL(myappwidgetprovider .image[i]).getContent());


                                newviews.setImageViewBitmap(R.id.imageView4, bitmap);
                            } catch (MalformedURLException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                         app.updateAppWidget(allWidgetIds, newviews); 
                                 }


                     }, delay, period);
            return null;
        }
     }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }





}
3个回答

9
我刚解决了类似的问题,但我没有从网络加载任何内容。我的小部件为不同的小部件大小显示不同的图像。对于一些屏幕,也会出现与您相同的错误,在4x4小部件上显示。

我在developer.google.com上找到了这个:

RemoteViews对象使用的总位图内存不能超过填充屏幕1.5倍所需的内存,即(屏幕宽度x屏幕高度x4x1.5)字节。

因此,我只需在小部件中限制我的位图大小以满足此要求。

在您的特定情况下,当从流中加载的位图比可能的位图大时,会出现错误。

我认为使用带有正确选项的

public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts)

应该会有帮助。(可能需要使用inSampleSize选项)。

希望我的帖子有所帮助。


我想我已经解决了那个问题。不管怎样,谢谢你 :) - Basim Sherif
@Rusanovskiy Artem:你怎么知道传递给decodeStream()的选项是准确无误的?如果在小部件中有四个位图,并且您不知道源图像的大小,那么您如何知道要使用哪个sampleSize来防止出现异常? - caw
1
@BasimSherif,你能解释一下你是如何解决这个问题的吗?谢谢! - zys
我的屏幕分辨率是3088 x 1440:3088 x 1440 * 4 * 1.5 = 27兆字节将被占用在RAM中。这比使用1张图片和最多1兆字节的RAM更加耗费CPU时间来压缩图片和更改内存中的数据。如果我们这样浪费资源,就没有足够的资源用于其他事情了。这不是专业的做法。 - Master

2
为了展示RusArtM回答的第二部分。
val bitmapOptions = BitmapFactory.Options()
bitmapOptions.inSampleSize = 4
val bitmap = BitmapFactory.decodeFile(codeData.imagePath, bitmapOptions)

可能需要解决这个问题。使用大于1的值的inSampleSize可以缩小图像。使用inSampleSize = 4,我们可以获得一个宽度/高度为原图像的1/4的图像。

这里是文档


当然,这会降低质量,问题在于图像太大了,所以最简单的解决方法是缩小尺寸。我只是举个例子来说明被接受的答案。 - Pierre Monier
我只是展示了一种减小图像尺寸的方法。我认为这并不会有害。如果您能向我展示它的危害性,我将非常感激。在那种情况下,您可能需要评论已接受的答案。 - Pierre Monier
处理器将会忙于压缩图像,这需要资源。其他程序员也会走捷径,而非正确之路,他们的应用会因此消耗更多的RAM和CPU时间。这都是非常有害的。我建议你删除你的回复。除此之外,你的回答没有回答问题。为什么会发生内存溢出?正确的答案是内存泄漏。如何修复,我写在下面了。 - Master
1
我理解你的观点,我认为你有些极端,但这是一个有趣的观点。是的,我的回答只是演示了被接受答案的建议,我应该评论这个答案而不是创建自己的答案。我不会删除它。我认为它仍然有价值,我们在评论区的小辩论可以让人们思考,所以我认为这很好。我也不认为人们会盲目地复制/粘贴并用否定投票回答。 - Pierre Monier
1
如果您从服务器获取图像,并且这些图像足够大,则即使具有干净的缓存,您仍将遇到相同的问题。因此,在这种情况下,您确实需要将图像缩小。我建议您下次尝试更加礼貌、少一点固执己见,@Master。 - Georgios
这些是存储在其原始质量中的本地图片。而且只有一张图片。你在说什么? - Master

0

谷歌发明了一个有趣的图片缓存。我不知道为什么,但它大大增加了系统的负载。这就是为什么我每次都这样做,不保存图片历史记录的原因:

        contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.notification_media);
        Intent notify = new Intent(getApplicationContext(), MainActivity.class);
        PendingIntent butt = PendingIntent.getActivity(getApplicationContext(), 0, notify, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        contentView.setOnClickPendingIntent(R.id.ibShowApp, butt);
        contentView.setTextViewText(R.id.tvTitle, title);
        if (coverArt != null) {
            if (coverCache != null) coverCache.recycle();
            try {
                coverCache = BitmapFactory.decodeFile(coverArt);
            } catch (Throwable throwable) {
                coverCache = null;
            }
            contentView.setImageViewBitmap(R.id.ivCover, coverCache);
        } else if (coverCache != null) {
            contentView.setImageViewBitmap(R.id.ivCover, null);
            coverCache.recycle();
            coverCache = null;
        }
        mBuilder.setContentTitle(title)
                .setCustomBigContentView(contentView)
                .setContent(contentView);
        Notification not = mBuilder.build();
        not.flags |= Notification.FLAG_NO_CLEAR;
        startForeground(Constants.NOTIFICATION_ID, not);

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