2012-05-22 60 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

相關問題