在我最近的一个SO问题中,有人建议我使用Loader
作为我的解决方案。所以我在这里试图了解如何实现一个简单的AsyncTaskLoader
以下是我的想法:
public class Scraper extends AsyncTaskLoader<List<Event>> {
List<Event> lstEvents;
public Scraper(Context context) {
super(context);
}
public List<Event> loadInBackground() {
//This is where I will do some work and return the data
}
}
这就是我所知道的。我阅读了AyncTaskLoader
的文档,但我从来没有遇到过如此神秘和混乱的东西。有一百万种方法,所有这些方法都是相互矛盾的,看着它们是不可能推断出来的,它们被调用的顺序,甚至它们是否应该被覆盖和调用。这项任务的生命周期是一个该死的噩梦。
我正在寻找的只是简单地抓取一些数据并将其返回。我还想把它存储在一个类变量中,这样我可以在下次快速返回它而不必再次刮掉所有数据。
我没有打开的游标,流或类似的东西,只是一个名为lstEvents
的简单变量(可能很大)。我不想泄漏内存和浪费资源,所以如果有人能解释我需要关闭/无效的地点和时间,我会很高兴。
我应该将数据存储到类变量中?我应该在loadInBackground
方法结束时这样做,还是应该在deliverResult
方法中执行此操作?
在这个简单的场景中,我是否确实需要检查任务是否被取消或是否已重置,或者我是否应该覆盖这些方法并让AsyncTaskLoader
处理它。
如果有人知道,一些脚手架代码会有所帮助。非常感谢。
答案 0 :(得分:7)
在我最近的一个SO问题中,有人建议我使用Loader作为我的解决方案。
这可能不是一个好建议。
这项任务的生命周期是一个该死的噩梦。
更不用说Loader
合同表明您应该能够动态检测数据中的更改并自动将更新传递给使用Loader
的更新,这对{ {1}},对于其他设备商店来说很难,对于像你这样的场景来说并不是特别实用。
坦率地说,我只是使用ContentProvider
并称之为好。
话虽如此,您可以查看the Loader
implementations I made的SQLite数据库(无AsyncTask
)和ContentProvider
作为自定义加载器的示例。你唯一的方法
需要实施SharedPreferences
和loadInBackground()
。如果内置实现不足,可能需要onStartLoading()
- 在您的情况下,我怀疑内置的实现方式会很好。您可以 添加其他一些方法,正如您在my AbstractCursorLoader
中看到的那样,但我再次怀疑您不需要它们。
有哪些地方我真的需要检查任务是否被取消或是否被重置,或者我应该只是不覆盖这些方法并让AsyncTaskLoader处理它。
首先让deliverResult()
处理它,只有当你因某种原因确定需要它们时才会担心它们。
答案 1 :(得分:3)
除了CommonsWare的答案中的所有好信息之外,还有另外需要考虑的事情。在实施AsyncTaskLoader
时,您需要在Loader或forceLoad()
的子类上的某处调用AsyncTaskLoader
。否则,根据我的经验,您的loadInBackground()
方法将无法调用。
根据我对文档的理解,加载器可以像你在问题中删除的那样在成员变量中挂起结果。然后,您可以覆盖onStartLoading()
并检查缓存的结果列表。如果列表为null,则加载程序可以自行调用forceLoad()
。
另请注意,如果您最终实现了缓存结果,请注意在另一个线程上调用loadInBackground()
,因此您可能希望同步对任何成员变量的访问。
答案 2 :(得分:1)
这有点晚,但对于正在阅读这个问题的其他人可能会有用。这是AsyncTaskLoader的实现:
class CopyFilter(FilterSet):
book_details = RelatedFilter(BookFilter, name="book_details", queryset=BookDetail.objects.all()
在ListFragment中使用它作为:
public class EventListLoader extends AsyncTaskLoader<List<Event>> {
private List<Event> events;
public EventListLoader(Context context) {
super(context);
events = new ArrayList<Event>();
}
@Override
public List<Event> loadInBackground(){
//"Simply scrape some data and return it" as a list of events here.
}
@Override
public void deliverResult(List<Event> data) {
if (isReset()) {
releaseResources(events);
return;
}
List<Event> oldData = events;
events = data;
if (isStarted())
super.deliverResult(data);
releaseResources(oldData);
}
@Override
protected void onStartLoading() {
if (events != null && !events.isEmpty())
deliverResult(events);
if (takeContentChanged() || events == null || events.isEmpty())
forceLoad();
}
@Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
}
@Override
public void onCanceled(List<Event> data) {
super.onCanceled(data);
releaseResources(events);
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
releaseResources(events);
}
private void releaseResources(List<Event> data) {
if (data!= null && !data.isEmpty())
data.clear();
}
}
如果我们将EventListAdapter定义为:
,我们可以加载并显示事件列表(从本地数据库或互联网)作为ListFragment(例如)public class EventsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Event>> {
private static final int LOADER_ID = 0;
...
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
...
setListAdapter(new EventListAdapter());
if (savedInstanceState == null)
getLoaderManager().initLoader(LOADER_ID, null, this);
else
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
@Override
public Loader<List<Event>> onCreateLoader(int id, Bundle args) {
return new EventLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<Event>> loader, List<Event> data) {
((EventListAdapter) getListAdapter()).setData(data);
invalidateActionMode(); //Only if Contextual Action Bar (CAB) is used.
}
@Override
public void onLoaderReset(Loader<List<Event>> loader) {
((EventListAdapter) getListAdapter()).clearData();
}