【问题标题】:ProgressBar and AsyncTaskProgressBar 和 AsyncTask
【发布时间】:2012-11-03 22:32:10
【问题描述】:

我目前正在运行一个 AsyncTask,它会在下载文件时更新 Activity 中的进度条。问题是当我离开 Activity 并重新进入时,ProgressBar 将不再更新。

我尝试在 Service 中运行 AsyncTask,但我不知道如何将 ProgressBar 值发送回我的 Activity 的 UI 线程。

【问题讨论】:

    标签: android service android-asynctask progress-bar


    【解决方案1】:

    我遇到了和你一样的问题:

    • 我想要执行一个异步任务(作为服务)
    • 我希望能够旋转设备,能够更新 UI,即使任务在屏幕关闭时完成并且我没有收到任何通知。

    我想出了类似的东西:

    public class DatabaseIncompleteActivity extends RoboActivity {
    private BroadcastReceiver       receiver;
    private ProgressDialog          progressDialog;
    
    @Inject
    private DatabaseSetManager      databaseSetManager;
    @Inject
    private DatabaseDownloadLogger  databaseDownloadLogger;
    
    private LocalBroadcastManager   localBroadcastManager;
    private String                  jobId;
    
    private static final int        ERROR_RETRY_DIALOG  = 1;
    private static final String     ERROR_MESSAGE       = "errorMsg";
    
    @Override
    protected void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        localBroadcastManager = LocalBroadcastManager.getInstance( this );
    
        showProgressDialog();
        if ( getLastNonConfigurationInstance() == null ) {
            startService();
        }
    }
    
    private void showProgressDialog() {
        progressDialog = new ProgressDialog( this );
        progressDialog.setMessage( getString( R.string.please_wait ) );
        progressDialog.setProgressStyle( ProgressDialog.STYLE_HORIZONTAL );
        progressDialog.setIndeterminate( true );
        progressDialog.setCancelable( false );
        progressDialog.show();
    }
    
    private void startService() {
        this.jobId = UUID.randomUUID().toString();
        Intent intent = new Intent( this, ClientDatabaseDroidService.class );
        intent.putExtra(    ClientDatabaseDroidService.JOB_ID,
                            jobId );
        intent.putExtra(    ClientDatabaseDroidService.INTERACTIVE,
                            true );
        startService( intent );
    }
    
    private void registerListenerReceiver() {
        if ( receiver != null ) {
            return;
        }
        localBroadcastManager.registerReceiver( this.receiver = new ClientDatabaseBroadcastReceiver(),
                                                new IntentFilter( ClientDatabaseDroidService.PROGRESS_NOTIFICATION ) );
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        unregisterListenerReceiver();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
    
        DatabaseDownloadLogEntry logEntry = databaseDownloadLogger.findByJobId( jobId );
        // check if service finished while we were not listening
        if ( logEntry != null ) {
            if ( logEntry.isSuccess() )
                onFinish();
            else {
                Bundle bundle = new Bundle();
                bundle.putString(   ERROR_MESSAGE,
                                    logEntry.getErrorMessage() );
                onError( bundle );
            }
            return;
        }
    
        registerListenerReceiver();
    }
    
    @Override
    public Object onRetainNonConfigurationInstance() {
        return Boolean.TRUE;
    }
    
    final class ClientDatabaseBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive( Context context, Intent intent ) {
            Bundle extras = intent.getExtras();
    
            int eventType = extras.getInt( ClientDatabaseDroidService.EVENT_TYPE );
    
            switch ( eventType ) {
            case ClientDatabaseDroidService.EVENT_TYPE_DOWNLOADING:
                onDownloading( extras );
                break;
            case ClientDatabaseDroidService.EVENT_TYPE_FINISHED:
                onFinish();
                break;
            case ClientDatabaseDroidService.EVENT_TYPE_ERROR:
                Bundle bundle = new Bundle();
                bundle.putString(   ERROR_MESSAGE,
                                    extras.getString( ClientDatabaseDroidService.EXTRA_ERROR_MESSAGE ) );
                onError( bundle );
                break;
            default:
                throw new RuntimeException( "should not happen" );
            }
        }
    
    }
    
    private void unregisterListenerReceiver() {
        if ( receiver != null ) {
            localBroadcastManager.unregisterReceiver( receiver );
            receiver = null;
        }
    }
    
    private void onError( Bundle extras ) {
        progressDialog.dismiss();
        showDialog( ERROR_RETRY_DIALOG,
                    extras );
    }
    
    private void onFinish() {
        progressDialog.dismiss();
        setResult( RESULT_OK );
        finish();
    }
    
    @Override
    protected Dialog onCreateDialog( final int id, Bundle args ) {
        if ( id == ERROR_RETRY_DIALOG ) {
            Builder builder = new AlertDialog.Builder( this );
            builder.setTitle( R.string.error );
            builder.setMessage( "" );
            builder.setPositiveButton(  R.string.yes,
                                        new OnClickListener() {
                                            @Override
                                            public void onClick( DialogInterface dialog, int which ) {
                                                showProgressDialog();
                                                startService();
                                            }
                                        } );
            builder.setNegativeButton(  R.string.no,
                                        new OnClickListener() {
                                            @Override
                                            public void onClick( DialogInterface dialog, int which ) {
                                                setResult( RESULT_CANCELED );
                                                finish();
                                            }
                                        } );
            return builder.create();
        }
    
        return super.onCreateDialog(    id,
                                        args );
    }
    
    @Override
    protected void onPrepareDialog( int id, Dialog dialog, Bundle args ) {
        if ( id == ERROR_RETRY_DIALOG ) {
            ( (AlertDialog) dialog ).setMessage( String.format( "%s\n\n%s",
                                                                args.getString( ERROR_MESSAGE ),
                                                                getString( R.string.do_you_wish_to_retry ) ) );
            return;
        }
    
        super.onPrepareDialog(  id,
                                dialog );
    }
    
    private void onDownloading( Bundle extras ) {
        String currentDatabase = extras.getString( ClientDatabaseDroidService.EXTRA_CURRENT_CLASSIFIER );
        Progress databaseProgress = extras.getParcelable( ClientDatabaseDroidService.EXTRA_DATABASE_PROGRESS );
        Progress downloadProgress = extras.getParcelable( ClientDatabaseDroidService.EXTRA_DOWNLOAD_PROGRESS );
        progressDialog.setIndeterminate( false );
        progressDialog.setMessage( String.format(   "[%d/%d] %s",
                                                    databaseProgress.getProcessed(),
                                                    databaseProgress.getSize(),
                                                    currentDatabase ) );
        progressDialog.setProgress( (int) downloadProgress.getProcessed() );
        progressDialog.setMax( (int) downloadProgress.getSize() );
    }
    

    }

    主要思想是:

    • 活动通过广播与服务通信。以下事件从服务分派到 UI:下载进度(每 x 个字节报告一次)、下载完成、发生错误
    • 为每个活动开始分配一个唯一的作业 ID,该 ID 保留用于屏幕旋转。
    • 每个作业结果都被持久化到数据库(或任何其他持久性存储)中。这样,即使收到的广播目前没有收听(屏幕关闭),我也可以获得作业结果。

    希望有帮助。

    【讨论】:

      【解决方案2】:
      1. 在您的活动中设置静态进度变量
      2. 将进度保存到首选项

      当您重新进入活动时,从静态变量或首选项中抓取并设置进度。

      【讨论】:

      • 但是如果我将进度保存到首选项并从静态变量或首选项中设置它,progressBar 不会卡在保存在静态变量/首选项中的进度上吗?
      【解决方案3】:

      这是正常的。当您离开它们或旋转您的设备时,活动就会消失。然后它们被重新创建。 AsyncTask 不会自动重新链接到新的 Activity 实例。

      实际上,RoboSpice 是您正在寻找的库:它可以让您从 Activity 启动下载、旋转设备、离开 Activity,甚至离开应用程序,甚至使用任务切换器和您的下载将继续存在。任何活动、以前活动的新实例,甚至完全不同的活动都可以重新链接到您的下载。

      您应该从商店下载应用程序RoboSpice Motivations,它将向您解释为什么使用 AsyncTasks 进行网络不是一个好主意,并告诉您如何使用 RoboSpice。

      要快速了解此问题,请查看this infographics

      另外请注意,如果您只下载二进制数据并且对 POJO 不感兴趣,那么您仍然可以使用 RoboSpice 或 DownloadManager(但它的 API 不如 RoboSpice 直观)。

      【讨论】:

        猜你喜欢
        • 2021-06-28
        • 1970-01-01
        • 1970-01-01
        • 2015-09-20
        • 2016-12-19
        • 2016-09-26
        • 1970-01-01
        • 1970-01-01
        • 2014-02-27
        相关资源
        最近更新 更多