【问题标题】:Android Loader vs AsyncTask on button tapAndroid Loader vs AsyncTask on button 点击
【发布时间】:2013-05-29 01:00:28
【问题描述】:

我有一个活动在加载时不需要来自服务器的数据 - 只需简单的 ui 初始化

UI 有几个按钮。

用户单击其中一个,应用程序向服务器发送请求(休息调用) 请求正在处理时显示微调器(大约 10 秒)

现在它使用 AsyncTask - 所以如果应用程序将纵向更改为横向 - 活动会重新启动,我会松开进程

第二个选项是使用 Loader - 问题是它是在点击按钮时启动的 - 而不是在活动开始时

这会导致很多异常——当 LoaderManager 向未启动项发送事件时

有什么解决办法吗?

几个cmets: - 10 秒只是一个例子 - 将用户锁定到一个方向不是一种选择 - 服务对于简单的休息调用来说太过分了

【问题讨论】:

    标签: android android-activity android-asynctask loader


    【解决方案1】:
    public class TestActivity extends FragmentActivity {
    
        private Button one;
        private Button two;
    
        private final int ONE_ID = 0;
        private final int TWO_ID = 1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            one = (Button) findViewById(R.id.one);
            two = (Button) findViewById(R.id.two);
    
            one.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getLoaderManager().restartLoader(ONE_ID, null, callbacks);
                }
            });
    
            two.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getLoaderManager().restartLoader(ONE_ID, null, callbacks);
                }
            });
    
            Loader<AsyncTaskLoaderResult<Result>> loader = getLoaderManager().getLoader(ONE_ID);
            if (loader != null) {
                getLoaderManager().initLoader(ONE_ID, null, callbacks);
            }
            loader = getLoaderManager().getLoader(TWO_ID);
            if (loader != null) {
                getLoaderManager().initLoader(TWO_ID, null, callbacks);
            }
    
    
        }
    
        public static class AsyncTaskLoaderResult<E> {
            public E data;
            public Bundle args;
        }
    
        public static class Result {
    
        }
    
        private LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<Result>> callbacks = new LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<Result>>() {
            @Override
            public Loader<AsyncTaskLoaderResult<Result>> onCreateLoader(int id, Bundle args) {
                /**
                 * according different Id, create different AsyncTaskLoader
                 */
                switch (id) {
                    case ONE_ID:
                        return new OneAsyncTaskLoader(TestActivity.this);
                    case TWO_ID:
                        return new TwoAsyncTaskLoader(TestActivity.this);
                }
                return null;
            }
    
            @Override
            public void onLoadFinished(Loader<AsyncTaskLoaderResult<Result>> loader, AsyncTaskLoaderResult<Result> data) {
                /**
                 * handle result
                 */
                switch (loader.getId()) {
    
                }
    
                getLoaderManager().destroyLoader(loader.getId());
            }
    
            @Override
            public void onLoaderReset(Loader<AsyncTaskLoaderResult<Result>> loader) {
    
            }
        };
    
        public static class OneAsyncTaskLoader extends AsyncTaskLoader<AsyncTaskLoaderResult<Result>> {
    
            private AsyncTaskLoaderResult<Result> result;
    
            public OneAsyncTaskLoader(Context context) {
                super(context);
            }
    
            @Override
            protected void onStartLoading() {
                super.onStartLoading();
                if (result != null) {
                    deliverResult(result);
                } else {
                    forceLoad();
                }
            }
    
            @Override
            public AsyncTaskLoaderResult<Result> loadInBackground() {
                /**
                 * send request to server
                 */
                result = new AsyncTaskLoaderResult<Result>();
                result.data = null; //  result.data comes from server's response
                return result;
            }
        }
    
        public static class TwoAsyncTaskLoader extends AsyncTaskLoader<AsyncTaskLoaderResult<Result>> {
    
    
            private AsyncTaskLoaderResult<Result> result;
    
            public TwoAsyncTaskLoader(Context context) {
                super(context);
            }
    
            @Override
            protected void onStartLoading() {
                super.onStartLoading();
                if (result != null) {
                    deliverResult(result);
                } else {
                    forceLoad();
                }
            }
    
            @Override
            public AsyncTaskLoaderResult<Result> loadInBackground() {
                /**
                 * send request to server
                 */
                result = new AsyncTaskLoaderResult<Result>();
                result.data = null; //  result.data comes from server's response
                return result;
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      首先,您可以通过声明来消除方向变化问题

      android:configChanges="orientation"
      

      savedInstanceState()

      但这里真正的问题是让用户盯着微调器 10 秒钟。大多数用户对此没有足够的耐心。我不知道你的应用在做什么,所以很难给出准确的建议,但我可以说你需要在AsyncTask 中做你的网络工作,但允许用户做其他事情

      您可以允许用户在 AsyncTask 完成时执行其他操作,或者将该代码放入 [Service(http://developer.android.com/guide/components/services.html)。无论哪种方式,不要让您的用户盯着屏幕旋转 10 秒...他们不会长时间成为您的用户

      【讨论】:

      • 10 秒只是一个例子:假设用户提交表单并获得确认 - 如果使用 AsyncTask 并且应用程序更改了方向:结果丢失,用户将再次看到他的表单(当然我可以添加变量来检查状态提交/正在进行/完成,但这是非常糟糕的解决方案)
      • 您在 OP 中听起来很确定大约 10 秒,所以我想确保您理解对于用户在移动设备上等待的时间太长了。如果你处理configChanges,那么AsyncTask不会丢失
      【解决方案3】:

      如果您为此使用AsyncTask,您可能希望改用Service 或使用onRetainNonConfigurationInstanceFragment.setRetainInstance 以允许AsyncTask 承受配置更改。

      或禁用配置更改:我过去曾成功使用过它。

      【讨论】:

      • 服务对于使用一些 REST 服务的应用来说绝对是矫枉过正。 OnRetailN... 并不总是被称为 Loader 看起来像是完美的解决方案 - 应用程序可以在启动时检查它是否正在运行以在活动重启的情况下显示进度
      • 如果您坚持:绝对没有理由不使用服务。服务旨在用于可能跨越多个活动生命周期的后台任务。您所描述的情况正是如此。你可以很容易地使用IntentService 来处理这种事情。
      • 示例:在时间 0 用户通过 10 秒的网络调用提交表单,在 5 秒时他改变方向并重新创建活动,在启动时检查状态并显示进度或结果(如果准备好)示例 2:等待时用户离开应用程序并在 2 天内返回 - 结果已经无效(没有必要显示消息“已提交”) - 所以我需要添加额外的逻辑来检查时间戳?
      【解决方案4】:

      这是一篇关于这个主题的好文章:

      http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html

      无论如何,正如@codeMagic 所提到的,AsyncTaskandroid:configChanges="orientation|screenSize" 对您来说应该足够了(它可以防止在配置更改时重新创建活动)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-04
        • 2018-02-18
        • 2017-02-13
        • 2017-10-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多