由于不建议在任务中保留对Context
的强烈引用(上下文可能在任务仍在运行时被破坏,但由任务保留在内存中),但我想知道是否同样适用于碎片?在AsyncTask中保留对片段的强引用是否安全?
碎片管理他们的活动参考,并支持通过setRetainInstance
保留。我能否假设在片段中创建一个非静态内部AsyncTask是安全的,不会冒险泄漏$this
?
由于不建议在任务中保留对Context
的强烈引用(上下文可能在任务仍在运行时被破坏,但由任务保留在内存中),但我想知道是否同样适用于碎片?在AsyncTask中保留对片段的强引用是否安全?
碎片管理他们的活动参考,并支持通过setRetainInstance
保留。我能否假设在片段中创建一个非静态内部AsyncTask是安全的,不会冒险泄漏$this
?
在线程之间保持引用通常是不好的方法,而AsyncTask
就像是一个线程。
没关系,只要确保在完成使用时取消引用即可。
否则,你可能会得到内存泄漏。
在这种情况下,没关系,因为您的Fragment
位于AsyncTask
的上下文中。任务完成后,将失去该参考。
如果这是在Service
中完成,这将是一个非常糟糕的主意。
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) {
}
是的好点;我还没有到处使用'Loader',但是快速浏览表明它遭受与AsyncTask相同的缺陷(http://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-或者我只是缺少某种东西),即不提供对回调中的活动/片段的托管引用。 – Matthias
但是为什么你需要从Loader内部引用Activity?这就是它的美丽 - 这是一个很好的分离。 ui线程的工作只发生在活动代码中(通过LoaderCallbacks接口),所以加载(保存等)发生在第二个线程上,并更新ui - ui线程。我能想到的唯一缺陷是在加载过程中更新ui的问题,但这对于LoaderCallbacks的扩展可能并不困难。 –
如果你加载两个不同的东西呢?您不能使用不同的类型变量实现两次相同的接口,因此回调必须在委托上实现,委托又必须管理上下文引用。尽管我想可以创建一个装载器组合,它提供了一个组合了两个不同结果的组合输出。 – Matthias
在这种情况下,如果您在片段死亡时保留对asynctask的引用,则风险是。用户可以说做一个动作,从堆栈中弹出片段。在这种情况下,你通常不希望片段的状态再次存在。如果你想要asynctask的状态,因为它包含了你想要使用的额外数据,那么即使Fragment不应该存在,它也会保留对片段的引用。我相信它会在经历正常生命周期后脱离活动,但它仍然“存在”。 – DeeV
@DeeV:是的,这正是我所担心的 – Matthias
你可以简单地将它分配给WeakReference并取消AsyncTask,如果片段变为空? – DeeV