2012-05-22 38 views
6

由于不建议在任务中保留对Context的强烈引用(上下文可能在任务仍在运行时被破坏,但由任务保留在内存中),但我想知道是否同样适用于碎片?在AsyncTask中保留对片段的强引用是否安全?

碎片管理他们的活动参考,并支持通过setRetainInstance保留。我能否假设在片段中创建一个非静态内部AsyncTask是安全的,不会冒险泄漏$this

回答

6

在线程之间保持引用通常是不好的方法,而AsyncTask就像是一个线程。

没关系,只要确保在完成使用时取消引用即可。

否则,你可能会得到内存泄漏。

在这种情况下,没关系,因为您的Fragment位于AsyncTask的上下文中。任务完成后,将失去该参考。

如果这是在Service中完成,这将是一个非常糟糕的主意。

+2

在这种情况下,如果您在片段死亡时保留对asynctask的引用,则风险是。用户可以说做一个动作,从堆栈中弹出片段。在这种情况下,你通常不希望片段的状态再次存在。如果你想要asynctask的状态,因为它包含了你想要使用的额外数据,那么即使Fragment不应该存在,它也会保留对片段的引用。我相信它会在经历正常生命周期后脱离活动,但它仍然“存在”。 – DeeV

+0

@DeeV:是的,这正是我所担心的 – Matthias

+0

你可以简单地将它分配给WeakReference并取消AsyncTask,如果片段变为空? – DeeV

2

Phoenixblade9的答案是正确的,但为了使它充满我会添加一件事。

一般来说,AsyncTask - AsyncTaskLoader或Loaders是很好的替代品。它根据被调用的上下文(Activity,Fragment)管理其生命周期,并实现一堆监听器来帮助您将第二个线程的逻辑与ui线程分开。它通常不会泄露上下文。

不要打扰这个名字 - 这对于保存数据也很好。


按照承诺,我会发布我的AsyncTaskLoader代码,其中包含多个返回的对象。装载机是这样的:

public class ItemsLoader extends AsyncTaskLoader<HashMap<String, Object>>{ 

HashMap<String, Object> returned; 
ArrayList<SomeItem> items; 
Context cxt; 

public EventsLoader(Context context) { 
    super(context); 
    //here you can initialize your vars and get your context if you need it inside 
} 

@Override 
public HashMap<String, Object> loadInBackground() { 


    returned = getYourData(); 

    return returned; 

} 

@Override 
public void deliverResult(HashMap<String, Object> returned) { 
    if (isReset()) { 
     return; 
    } 

    this.returned = returned; 

    super.deliverResult(returned); 
} 

@Override 
protected void onStartLoading() { 
    if (returned != null) { 
     deliverResult(returned); 
    } 

    if (takeContentChanged() || returned == null) { 
     forceLoad(); 
    } 
} 

@Override 
protected void onStopLoading() { 
    cancelLoad(); 
} 

@Override 
protected void onReset() { 
    super.onReset(); 

    onStopLoading(); 

    returned = null; 
} 

getYourData()功能我得到两个服务器消息代码或其他一些错误代码和ArrayList<SomeItem>。我可以像这样在我的片段中使用它们:

public class ItemListFragment extends ListFragment implements LoaderCallbacks<HashMap<String, Object>>{ 

private LoaderManager lm; 

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 

    lm = getLoaderManager(); 

    Bundle args = new Bundle(); 
args.putInt("someId", someId); 
lm.initLoader(0, args, this); 
} 


@Override 
public Loader<HashMap<String, Object>> onCreateLoader(int arg0, Bundle args) { 
    ItemsLoader loader = new ItemsLoader(getActivity(), args.getInt("someId")); 
    return loader; 
} 

@Override 
public void onLoadFinished(Loader<HashMap<String, Object>> loader, HashMap<String, Object> data) { 

    if(data!=null){ if(data.containsKey("items")){ 
     ArrayList<SomeItem> items = (ArrayList<EventItem>)data.get("items"); 

    } else { //error 
     int error = 0; 
     if(data.containsKey("error")){ 
      error = (Integer) data.get("error"); 
     } 
      } 

} 

@Override 
public void onLoaderReset(Loader<HashMap<String, Object>> arg0) { 

} 
+0

是的好点;我还没有到处使用'Loader',但是快速浏览表明它遭受与AsyncTask相同的缺陷(http://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-或者我只是缺少某种东西),即不提供对回调中的活动/片段的托管引用。 – Matthias

+0

但是为什么你需要从Loader内部引用Activity?这就是它的美丽 - 这是一个很好的分离。 ui线程的工作只发生在活动代码中(通过LoaderCallbacks接口),所以加载(保存等)发生在第二个线程上,并更新ui - ui线程。我能想到的唯一缺陷是在加载过程中更新ui的问题,但这对于LoaderCallbacks的扩展可能并不困难。 –

+0

如果你加载两个不同的东西呢?您不能使用不同的类型变量实现两次相同的接口,因此回调必须在委托上实现,委托又必须管理上下文引用。尽管我想可以创建一个装载器组合,它提供了一个组合了两个不同结果的组合输出。 – Matthias

相关问题