由於不建議在任務中保留對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