2011-04-20 66 views
2

在我的界面中,用户从可变数量的歌曲中进行选择,当选择歌曲时,我需要显示相关的背景图像。 用户需要在加载图像时保持对界面的控制,并且仍然能够改变歌曲。如何取消运行BitmapFactory.decodeFile()的AsyncTask并清理

我目前这样做的方式是使用AsyncTask。

我使用执行它:

if (LoadBG!=null&&!LoadBG.isCancelled()) 
LoadBG.cancel(false); 

LoadBG = new loadBG(); 
LoadBG.execute((Object) diff.BGPath); 

尝试,如果它仍在运行,取消以前的任务并重新创建它。

任务代码将位图加载:

protected Boolean doInBackground(Object... param) { 

     String pathName = param[0].toString(); 
     if (!pathName.equals(gfxStore.currentBGPath)) { 

      currentBGLoaded = false; 
      while(overlayOpacity!=255) 
       Thread.yield(); 
      //set current bg 
      if (this.isCancelled()) 
       return true; 
      Bitmap d; 
      try 
      { 
      d = gfxStore.factory.decodeFile(pathName,gfxStore.opts); 
      } 
      catch (OutOfMemoryError e) 
      { 
       System.gc(); 
       return true; 
      } 
      if (this.isCancelled()) 
      { 
       d.recycle(); 
       d = null; 
       System.gc(); 
       return true; 
      } 
      Bitmap s; 
      try 
      { 
      s = gfxStore.scaleImageForCanvas(canvasWidth, canvasHeight,d); 
      } 
      catch (OutOfMemoryError e) 
      { 
       //XXX uuuugh 
       System.gc(); 
       return true; 
      } 
      if (this.isCancelled()) 
      { 
       d.recycle(); 
       d=null; 
       s.recycle(); 
       s=null; 
       System.gc(); 
       return true; 
      } 
      d.recycle(); 
      d=null; 
      System.gc(); 
      gfxStore.currentBG = s; 
      gfxStore.currentBGPath = pathName; 
      wasChange = true; 
     } 
     else 
      wasChange=false; 
     return true; 
    } 

我做了回收,归零的挺乱的,运行GC,所有试图取消当前的任务,以便新创建一个将有足够的内存可供分配,但无论我尝试什么,当尝试运行太多太快(大约4/5次)时,我总是会出现内存异常

图像是1024x768 jpg文件,并要求1.5mb内存分配,我使用bitmapfactory选项:

opts.inPreferredConfig = Bitmap.Config.RGB_565; 
    opts.inPurgeable = true; 
    opts.inSampleSize = 1; 

绝对的任何建议,将不胜感激,我已经无休止地搜索回收位图,nulling,GCing,试图使用可清除的位图。

+0

您也可以考虑http://code.google.com/p/android/issues/detail?id=8488。 – sstn 2011-04-20 17:23:40

+0

我有,谢谢,但事实并非如此我相信 – Guykun 2011-04-20 17:38:16

回答

4

您可以尝试使用互斥锁序列化调用,以便只有一个操作(即使它由于某种原因被取消)执行?请参阅下面的一个非常基本的方法。显然你可以在方法中做更多的选择性锁定,但你可以得到图片。

static class DecodeLock extends Object { 
} 
static public DecodeLock lockObject = new DecodeLock(); 

// ... 

protected Boolean doInBackground(Object... param) { 
    synchronized (lockObject) { 
     String pathName = param[0].toString(); 
      if (!pathName.equals(gfxStore.currentBGPath)) { 

      //[..snip] 
    } 
} 
+0

哇,它似乎现在工作正常,没有任何OOM错误。为了理解这一点,是否导致当前任务在下一个开始运行之前完成它的取消(因为它正在等待同步)? – Guykun 2011-04-20 16:05:23

+0

是的,基本上每个AsyncTask都在一个单独的线程上运行(这是一个简化,但足以满足我们的目的)。当一个线程击中“synchronized”关键字时,它会锁定'lockObject',直到块末尾的匹配大括号。因此,如果另一个线程同时进入,它将在“同步”线路上阻塞,直到当前线程完成处理(无论它是成功处理还是取消)。希望有所帮助。 – 2011-04-20 16:27:23

+0

是的,这是有道理的,是完美的,谢谢。 – Guykun 2011-04-20 17:38:47