【问题标题】:How can I implement a simple AsyncTaskLoader?如何实现一个简单的 AsyncTaskLoader?
【发布时间】:2012-09-19 16:18:53
【问题描述】:

在我最近的一个 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 处理它。

如果有人知道,一些脚手架代码会很有帮助。非常感谢。

【问题讨论】:

    标签: java android android-asynctask android-loader


    【解决方案1】:

    有点晚了,但可能对正在阅读此问题的其他人有用。这是AsyncTaskLoader的一个实现:

    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();
        }
    }
    

    在 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();
        }
    

    如果我们将 EventListAdapter 定义为:

    public class EventListAdapter extends BaseAdapter {
        protected List<Event> mItems = new ArrayList<>();
    
        @Override                               
        public boolean isEnabled(int position) {
            return true;
        }
    
        @Override
        public boolean hasStableIds() {
            return true;
        }
    
        @Override
        public int getCount() {
            return mItems.size();
        }
    
        @Override
        public Object getItem(int position) {
            return mItems.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
           ...
        }
    
        public void setData(List<Event> items){
            mItems.clear();
            mItems.addAll(items);
            notifyDataSetChanged();
        }
    
        public void clearData(){
            mItems.clear();
            notifyDataSetChanged();
        }
    }
    

    【讨论】:

      【解决方案2】:

      除了来自 CommonsWare 答案的所有好信息之外,还有另一件事需要考虑。在实现AsyncTaskLoader 时,您需要在您的加载程序或AsyncTaskLoader 的子类上的某处调用forceLoad()。否则,根据我的经验,您的 loadInBackground() 方法将不会被调用。

      根据我对文档的理解,加载程序可以在成员变量中保留其结果,就像您在问题中提出的那样。然后,您可以覆盖 onStartLoading() 并检查缓存的结果列表。如果列表为空,加载器可以调用自己的forceLoad()

      还请注意,如果您最终实现了缓存结果,请注意loadInBackground() 在另一个线程上被调用,因此您可能希望同步对任何成员变量的访问。

      【讨论】:

      • “你需要调用 forceLoad()”——这是不正确的。请参阅:github.com/commonsguy/cw-omnibus/tree/master/Loaders/… 以获取不需要执行此操作的应用示例。
      • 你能澄清一下吗?发布的示例使用CursorLoader,它继承自AsyncTaskLoader,后者在onStartLoading() 中调用forceLoad()
      • 哦,没关系。我误解了你认为forceLoad() 会被调用的地方。对于我的困惑,我深表歉意。
      【解决方案3】:

      在我最近的一个 SO 问题中,有人建议我使用 Loader 作为我的解决方案。

      这可能不是一个好建议。

      这个任务的生命周期是一场该死的噩梦。

      更不用说Loader 合约表明您应该能够动态检测数据的变化并使用Loader 自动将更新交付给使用ContentProvider 的人,这对于ContentProvider 来说很容易,但很难对于其他设备上的商店,对于像您这样的场景并不是特别实用。

      坦率地说,我只是使用 AsyncTask 并称其为好。

      话虽如此,您可以看看the Loader implementations I made for SQLite 数据库(没有ContentProvider)和SharedPreferences 作为自定义加载器的示例。你唯一的方法 需要实施的是loadInBackground()onStartLoading()。如果内置实现不足,您可能需要deliverResult() - 在您的情况下,我怀疑内置实现会很好。您可以添加一些其他方法,正如您将在my AbstractCursorLoader 中看到的那样,尽管我再次怀疑您不需要它们。

      我是否真的需要检查任务是否被取消或是否被重置,或者我是否应该不重写这些方法并让 AsyncTaskLoader 处理它。

      从让AsyncTaskLoader 处理它开始,只有在您确定出于某种原因需要它们时才担心它们。

      【讨论】:

        猜你喜欢
        • 2012-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-24
        • 2017-10-06
        • 2012-02-04
        • 1970-01-01
        相关资源
        最近更新 更多