ProgressBar和AsyncTask

时间:2012-11-03 22:32:11

标签: android service android-asynctask progress-bar

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

我尝试在服务中运行AsyncTask但我不知道如何将ProgressBar值发送回我的A​​ctivity的UI线程。

3 个答案:

答案 0 :(得分:1)

  1. 在您的活动中制作静态进度变量
  2. 将进度保存到首选项
  3. 当您重新输入活动抓取并从静态变量或首选项设置进度时。

答案 1 :(得分: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 :(得分:0)

这很正常。当您离开或旋转设备时,活动会死亡。然后重新创建它们。 AsyncTasks不会自动重新链接到新的Activity实例。

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

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

获得一个好的&快速了解此问题,请查看this infographics

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