Избегайте перезапуска темы при изменении ориентации - PullRequest
0 голосов
/ 29 января 2012

В методе oncreate () моей деятельности есть несколько потоков.Когда меняется ориентация, потоки перезапускаются снова (новый экземпляр потоков создается при каждом изменении ориентации).

Я не хочу использовать android:configChanges или android:screenOrientationПотому что активность зависит от ориентации.

Ответы [ 2 ]

2 голосов
/ 29 января 2012

Используйте android:configChanges, но в переопределенном методе onConfigurationChanged() вызывайте только метод super.onConfigurationCanged() (или вообще не переопределяйте его).

Во время вращения onCreate () не вызывается, и ваши шаги не будут перезапущены, но ваш макет будет вращаться.

2 голосов
/ 29 января 2012

Я использую этот подход: У меня есть поле в деятельности, которая хранит поток. В onRetainNonConfigurationInstance () ответьте на это поле. Таким образом он сохраняется и становится доступным для нового экземпляра действия позже.

В onStart () я получаю поток из getLastNonConfigurationInstance (). Это либо значение NULL (поток не запущен), либо ссылка на поток, сохраненный функцией onRetainNonConfigurationInstance (). Если вы показываете (и вам необходимо восстановить) диалоговое окно прогресса, у вас также должно быть состояние в потоке (например, STARTED, RUNNING, DONE и т. Д.) Для обработки восстановления отображения прогресса в onStart ().

Если вам нужно связаться с потоком, вы можете захотеть добавить обработчик (например, как параметр для конструктора потока).

Вот пример. Поток считывает данные GPS из базы данных для последующей постобработки. Я пытался показать только соответствующий код здесь, имена методов пропущенных методов должны говорить сами за себя,

Это все из класса занятий:

private ProgressDialog progressDialog = null;
private LoadGpsDataThread loadGpsLogThread = null;

Это обработчик, используемый для связи:

/**
 * This handler updates the progress dialog when the logged GPS data is loaded.
 */
final Handler progressHandler = new Handler() {
    @Override
    public void handleMessage(final Message msg) {
        Bundle b;
        switch( msg.arg2 ) {
        case UPDATE_LOADER:
            // Update from GPS data loading thread
            final int total = msg.arg1;
            if( GpsPostprocessingActivity.this.progressDialog != null )
                GpsPostprocessingActivity.this.progressDialog.setProgress(total);
            if( GpsPostprocessingActivity.this.loadGpsLogThread != null && GpsPostprocessingActivity.this.loadGpsLogThread.state == STATE_DONE ) {
                GpsPostprocessingActivity.this.dismissProgress();
                GpsPostprocessingActivity.this.fillGraphView();
            }
            break;
        case IGpsDataPostProccessor.STATUS_ANALYZER:
            GpsPostprocessingActivity.this.statusView.setText(msg.arg1);
            break;
        case IGpsDataPostProccessor.UPDATE_ANALYZER:
            int sample;
            switch( msg.arg1 ) {
            // ...
            }
            break;
        case IGpsDataPostProccessor.GRAPH_UPDATE:
                GpsPostprocessingActivity.this.fillGraphView();
                break;
            }
            break;
        }
    }
};

Вот метод, запускающий поток, обратите внимание на обработчик как параметр конструктора:

/**
 * Load the GPS data from the database.
 * @param loading if <code>true</code> the load thread is already
 *                 running. In this case only the progress dialog is opened.
 */
private void loadGpsData(final boolean loading) {
    if( DEBUG )
        Log.d( TAG, "loadGpsData: Loading GPS data, already loading = " + loading);
    final int dataSize = this.gpsFlight.size();

    final String title = this.globalState.getString(R.string.titel_load_gps_data);
    final String msg = this.globalState.getFormattedTemplate(R.string.msg_tmpl_loading_gps_data, this.flightDesc);
    this.showProgress(title, msg, dataSize);

    if( ! loading ) {
        this.loadGpsLogThread = new LoadGpsDataThread(this.progressHandler);
        this.loadGpsLogThread.start();
    }
}

@Override
public Object onRetainNonConfigurationInstance() {
    // Dialog is removed in onSaveInstanceState(), see comment there
    // Check that there is a worker thread that
    // needs preserving
    if (this.loadGpsLogThread != null) {
        // remove reference to this activity (important to avoid memory leak)
        this.loadGpsLogThread.handler = null;
        // Return the instance to be retained
        if( DEBUG )
            Log.d( TAG, "onRetainNonConfigurationInstance: saved process");
        return this.loadGpsLogThread;
    }
    return super.onRetainNonConfigurationInstance();
}

Вот логика запуска:

@Override
protected void onStart() {
    if( DEBUG )
        Log.d(TAG, "onStart");
    super.onStart();

    this.refreshData();
    this.flightView.setText(this.flightDesc);
    this.logView.setText(this.getGpsLogDescription());
    this.statusView.setText(null);
    this.initProfileSpinner();
    // graphView is set asynchronously by the GPS data loading thread

    // Get the last load thread and check whether it is still running
    if (this.getLastNonConfigurationInstance() != null) {
        this.loadGpsLogThread = (LoadGpsDataThread) this.getLastNonConfigurationInstance();
        this.loadGpsLogThread.handler = this.progressHandler;
        switch (this.loadGpsLogThread.state) {
        case STATE_RUNNING:
            // Show the progress dialog again
            this.loadGpsData(true);
            break;
        case STATE_NOT_STARTED:
            // Close the progress dialog in case it is open
            this.dismissDialog(PROGRESS_DIALOG);
            break;
        case STATE_DONE:
            this.loadGpsLogThread = null;
            // Close the progress dialog in case it is open
            this.dismissDialog(PROGRESS_DIALOG);
            break;
        default:
            // Close the progress dialog in case it is open
            // Get rid of the sending thread
            if( DEBUG )
                Log.d(TAG, "Unknown progress thread state");
            this.dismissProgress();
        }
    }
    else {
        if( ! this.globalState.detectorState.isGpsDataCacheAvailable(this.gpsFlight) ) {
            this.loadGpsData(false);
            this.analysisResult = null;
        }
        else
            // data already loaded
            this.fillGraphView();
    }

    this.graphView.setShowLines(this.globalState.getBooleanPref(IPreferences.PREFS_GPS_GRAPH_LINES));
    this.processSubActivityResult();
}

Это нить как внутренний класс:

/**
 * This thread loads the GPS data from the database and
 * updates the progress dialog via the handler.
 */
private class LoadGpsDataThread extends Thread {
    Handler handler;
    int state;
    int stepsDone;

    LoadGpsDataThread(final Handler h) {
        this.handler = h;
        this.state = STATE_NOT_STARTED;
    }

    @Override
    public void run() {
        this.state = STATE_RUNNING;
        this.stepsDone = 0;
        final Cursor c =  GpsPostprocessingActivity.this.queryGpsData();
        try {
            while (c.moveToNext() && (this.state == STATE_RUNNING)) {
                final TrackData row = GpsPostprocessingActivity.this.globalState.getDb().readGpsData(c);
                GpsPostprocessingActivity.this.globalState.detectorState.gpsData[this.stepsDone] = row;
                this.stepsDone += 1;

                if( this.handler != null ) {
                    // can be null if the activity has been destroyed
                    final Message msg = this.handler.obtainMessage();
                    msg.arg1 = this.stepsDone;
                    msg.arg2 = UPDATE_LOADER;
                    this.handler.sendMessage(msg);
                }
            }
        }
        finally {
            this.state = STATE_DONE;
            c.close();
        }
        if( DEBUG )
            Log.d(TAG, "Data load thread finished");
    }
}
...