如何从AsyncTaskLoader更新进度条?

16
使用AsyncTaskLoader时,如何更新一个显示更新状态的进度条?通常情况下,您需要等待回调完成后才能删除进度条,但如何进行运行中的更新呢?您是否让主线程(UI)轮询正在设置的数据或其他方法?
注:我正在谈论AsyncTaskLoader,请查看加载器部分。这是类链接:http://developer.android.com/reference/android/content/AsyncTaskLoader.html 我想使用它,因为它是未来 :),我知道如何使用AsyncTask完成此操作。
6个回答

4
你可以使用handler,我认为它比intent更轻量级,对系统的负担更小。
public class MyFragmentActivity extends FragmentActivity{
private final static int MSGCODE_PROGRESS = 1;
private final static int MSGCODE_SIZE = 2;

    private final Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        Bundle bundle = msg.getData();
        if(null != bundle){
            int data = msg.getData().getInt(BUNDLE_PROGRESS);
            if(msg.what == MSGCODE_SIZE){
                mProgressBar.setMax(data);
            } else if(msg.what == MSGCODE_PROGRESS){
                mProgressBar.setProgress(data);
            }
        }
    }
};
}

将mHandler设置为AsyncTaskLoader的构造函数,并从loadInBackground中更新进度。
Bundle bundle = new Bundle();
bundle.putInt(BUNDLE_PROGRESS, lenghtOfFile);
Message msg = new Message();
msg.setData(bundle);
msg.what = MSGCODE_SIZE;
mHandler.sendMessage(msg);

但是当屏幕方向改变时会发生什么呢?处理程序将引用旧的上下文,导致内存泄漏。您需要以某种方式更新处理程序。 - Warpzit
1
这是另一个问题。您可以在活动配置中设置android:configChanges="orientation"。或者在onCreate中定义handler,然后handler将在每次旋转时获取新实例。 - garmax1
这是我自己期望的方式,只是想在stackoverflow上分享一下以确保 :) - Warpzit

4

我正在使用它与我的AsyncTaskLoader一起,在loadInBackground内部。

runOnUiThread(new Runnable() {
    public void run() {
        //UI code
    }
});

然而,当配置发生更改时(如方向更改),这种方法将无法正常工作。如果需要更新UI,我并不认为AsyncTaskLoader是最好的选择,但在处理配置更改时它效果最佳。我不知道为什么他们创建了一个AsyncTaskLoader和一个AsyncTask,每个都有自己的权衡取舍。这让开发人员感到沮丧和困惑。此外,AsyncTaskLoader的文档非常少。

另一个可能的解决方案,但文档很烂(就像 Android 的许多其他领域一样) :) - Warpzit

4
您可以使用加载器来实现,但需要在Activity上保留并更新WeakReference:
public class LoaderTestActivity extends FragmentActivity implements LoaderCallbacks<Void> {
    private static final int MAX_COUNT = 100;

    private ProgressBar progressBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task_test);

        progressBar = (ProgressBar) findViewById(R.id.progressBar1);
        progressBar.setMax(MAX_COUNT);
        AsyncTaskCounter.mActivity = new WeakReference<LoaderTestActivity>(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
        return true;
    }

    public void onStartButtonClick(View v) {
        startWork();
    }

    void startWork() {
        getSupportLoaderManager().initLoader(0, (Bundle) null, this);
    }

    static class AsyncTaskCounter extends AsyncTaskLoader<Void> {
        static WeakReference<LoaderTestActivity> mActivity;

        AsyncTaskCounter(LoaderTestActivity activity) {
            super(activity);
            mActivity = new WeakReference<LoaderTestActivity>(activity);
        }

        private static final int SLEEP_TIME = 200;

        @Override
        public Void loadInBackground() {
            for (int i = 0; i < MAX_COUNT; i++) {
                try {
                    Thread.sleep(SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(getClass().getSimpleName(), "Progress value is " + i);
                Log.d(getClass().getSimpleName(), "getActivity is " + getContext());
                Log.d(getClass().getSimpleName(), "this is " + this);

                final int progress = i;
                if (mActivity.get() != null) {
                    mActivity.get().runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            mActivity.get().progressBar.setProgress(progress);
                        }
                    });
                }
            }
            return null;
        }

    }

    @Override
    public Loader<Void> onCreateLoader(int id, Bundle args) {
        AsyncTaskLoader<Void> asyncTaskLoader = new AsyncTaskCounter(this);
        asyncTaskLoader.forceLoad();
        return asyncTaskLoader;
    }

    @Override
    public void onLoadFinished(Loader<Void> arg0, Void arg1) {

    }

    @Override
    public void onLoaderReset(Loader<Void> arg0) {

    }

}

2
我认为大多数看到这个问题的人都会使用AsyncTaskLoader从网络中读取一些数据。在这种情况下,请查看RoboSpice:当涉及到网络时,它比loader更符合您的需求:https://github.com/octo-online/robospice - Snicolas
1
我们需要同步访问“mActivity”吗? - Alexander Mironov
3
这是错误的。仅在第一次调用initLoader时才创建Loader,因此如果旋转活动,则会丢失对它的引用以及任何进度。正确的处理方式是在您的Loader上拥有一个setActivity(...)方法,用于将弱引用更改为新的activity,然后使用getSupportLoaderManager().getLoader(<id>)来获取Loader,将其转换为您的Loader,检查它是否为非空,并在再次调用initLoader()之前调用其setActivity()方法。此外,在setActivity()中,您应该确保Loader将其状态与进度视图同步。 - Daniele Segato
你有任何与 OP 相关的示例吗? - Snicolas
1
我建议不要放置Activity,而是放置一个进度本身的接口。 - android developer
显示剩余3条评论

3
我向Activity发送了一个Intent(并由BroadcastReceiver接收)。我对此解决方案不是很满意,但它可以工作。AsynTask的publishProgress真的更容易使用。你找到其他解决方案了吗?

1
我还没有找到任何解决方案,但我也没有认真搜索 :) 我问这个问题是因为我正在研究AsyncTaskLoader,并发现它在某些方面与asynctask相比存在不足。 - Warpzit

1
我刚遇到了这个问题。在我的活动中,我使用一个静态AtomicInteger来存储进度。加载器通过回调更新它,活动轮询并显示进度。
在加载器的回调onLoadFinished中,我隐藏了我的进度面板,这导致轮询循环退出。
通常我会避免使用静态状态,但我认为总体而言,这比其他选择更简单。在我的情况下,我在横向模式下有不同的布局,所以我更愿意让方向更改表现正常。
private Handler progressHandler; // init with new Handler(getMainLooper())
private static AtomicInteger progress = new AtomicInteger(0);

...

private void beginProgressUiUpdates() {
    progress.set(0);
    displayProgress();
    progressHandler.postDelayed(pollProgress, PROGRESS_POLL_PERIOD_MILLIS);
}

private Runnable pollProgress = new Runnable() {
    public void run() {
        if (findViewById(R.id.progressPanel).getVisibility() == View.VISIBLE) {
            displayProgress();
            progressHandler.postDelayed(pollProgress, PROGRESS_POLL_PERIOD_MILLIS);
        }
    }
};

private void displayProgress() {
    ((ProgressBar)findViewById(R.id.progressBar)).setProgress(progress.get());
}

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接