0

現在、ファイルのダウンロード中にアクティビティ内の進行状況バーを更新する AsyncTask を実行しています。問題は、アクティビティを離れて再入力すると、ProgressBar が更新されなくなることです。

サービス内で AsyncTask を実行しようとしましたが、ProgressBar の値をアクティビティの UI スレッドに戻す方法がわかりません。

4

3 に答える 3

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 が割り当てられています。
  • 各ジョブの結果は、データベース (またはその他の永続ストレージ) に保持されます。このようにして、受信したブロードキャストがその時点で聞いていなかった (画面がオフだった) 場合でも、ジョブの結果を取得できます。

それが役立つことを願っています。

于 2012-11-04T20:36:54.600 に答える
1
  1. アクティビティの静的進行状況変数を作成します
  2. 進行状況を設定に保存

アクティビティ グラブを再入力すると、静的変数または設定から​​進行状況を設定します。

于 2012-11-03T23:04:50.047 に答える
0

これは正常です。アクティビティから離れたり、デバイスを回転させたりすると、アクティビティは消滅します。その後、それらは再作成されます。AsyncTasks は、新しい Activity インスタンスに自動的に再リンクしません。

実際、RoboSpiceはあなたが探しているライブラリです: アクティビティからダウンロードを開始したり、デバイスを回転させたり、アクティビティを終了したり、アプリを終了したり、タスク スイッチャーを使用して終了したりすることができ、ダウンロードは存続します。アクティビティ、以前のアクティビティの新しいインスタンス、またはまったく異なるアクティビティでさえ、ダウンロードに再リンクできます。

ストアからアプリRoboSpice Motivationsをダウンロードする必要があります。ネットワークに AsyncTasks を使用するのが得策ではない理由が説明され、RoboSpice の使用方法が示されます。

この問題の概要をすばやく把握するには、このインフォグラフィックをご覧ください。

また、たとえば、バイナリ データのみをダウンロードし、POJO に関心がない場合でも、RoboSpice またはDownloadManagerを使用できます(ただし、API は RoboSpice ほど直感的ではありません)。

于 2012-11-03T23:40:56.203 に答える