Как справиться с изменением ориентации экрана при активном диалоге прогресса и фоновом потоке?

Функция val () jQuery всегда возвращает строку. Во многих случаях вы можете смешивать числа и строки (например, в арифметическом), при сравнении двух строковых переменных javascript будет выполнять сравнение строк, а не числовое сравнение (что следует ожидать)

509
задан Alex Lockwood 15 January 2014 в 20:16
поделиться

15 ответов

Когда вы переключаете ориентацию, Android создает новый вид. Вы, вероятно, получаете сбои из-за того, что ваш фоновый поток пытается изменить состояние старого. (Также могут возникнуть проблемы, потому что ваш фоновый поток не находится в потоке пользовательского интерфейса)

Я бы предложил сделать этот mHandler изменчивым и обновлять его при изменении ориентации.

151
ответ дан 22 November 2019 в 22:31
поделиться

Если вы боретесь с обнаружением событий изменения ориентации в диалоге НЕЗАВИСИМО ОТ ССЫЛКИ О ДЕЯТЕЛЬНОСТИ , этот метод работает потрясающе хорошо. Я использую это, потому что у меня есть свой собственный класс диалога, который может отображаться в нескольких различных действиях, поэтому я не всегда знаю, в каком действии он отображается. С этим методом вам не нужно изменять AndroidManifest, беспокойтесь о ссылках на действия, и вам не нужен пользовательский диалог (как у меня). Тем не менее, вам нужен пользовательский вид контента, чтобы вы могли обнаружить изменения ориентации, используя этот конкретный вид. Вот мой пример:

Настройка

public class MyContentView extends View{
    public MyContentView(Context context){
        super(context);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        super.onConfigurationChanged(newConfig);

        //DO SOMETHING HERE!! :D
    }
}

Реализация 1 - Диалог

Dialog dialog = new Dialog(context);
//set up dialog
dialog.setContentView(new MyContentView(context));
dialog.show();

Реализация 2 - AlertDialog.Builder

AlertDialog.Builder builder = new AlertDialog.Builder(context);
//set up dialog builder
builder.setView(new MyContentView(context));        //Can use this method
builder.setCustomTitle(new MycontentView(context)); // or this method
builder.build().show();

Реализация 3 - ProgressDialog / AlertDialog

ProgressDialog progress = new ProgressDialog(context);
//set up progress dialog
progress.setView(new MyContentView(context));        //Can use this method
progress.setCustomTitle(new MyContentView(context)); // or this method
progress.show();
0
ответ дан kpninja12 15 January 2014 в 20:16
поделиться

Я более свежий в Android, и я попробовал это, и это сработало.

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}
1
ответ дан ssuperczynski 15 January 2014 в 20:16
поделиться

Я пробовал ВСЕ. Провел дни экспериментов. Я не хотел блокировать вращение активности. Мой сценарий был:

  1. Диалог прогресса, показывающий динамическую информацию для пользователя. Например: «Подключение к серверу ...», «Загрузка данных ...» и т. Д.
  2. Поток выполняет тяжелую работу и обновляет диалог.
  3. Обновление интерфейса с результатами в конце.

Проблема заключалась в том, что при повороте экрана каждое решение в книге не удавалось. Даже с классом AsyncTask, который является правильным способом Android для решения этих ситуаций. При повороте экрана текущий контекст, с которым работает начальный поток, исчезает, и это портится в отображаемом диалоговом окне. Проблема всегда была в диалоге, независимо от того, сколько трюков я добавил в код (передача нового контекста запущенным потокам, сохранение состояний потоков посредством вращений и т. Д.). Сложность кода в конце всегда была огромной, и всегда было что-то, что могло пойти не так.

Единственное решение, которое сработало для меня, это трюк «Активность / Диалог». Это просто и гениально, и все это доказательство вращения:

  1. Вместо того, чтобы создавать диалог и просить показать его, создайте действие, которое было установлено в манифесте с android: theme = "@ Android: стиль / Theme.Dialog». Так что это просто похоже на диалог.

  2. Замените showDialog (DIALOG_ID) на startActivityForResult (yourActivityDialog, yourCode);

  3. Используйте onActivityResult в вызывающем Activity для получения результатов из выполняющегося потока (даже ошибок) и обновите пользовательский интерфейс.

  4. В «ActivityDialog» используйте потоки или AsyncTask для выполнения длинных задач и onRetainNonConfigurationInstance для сохранения «диалогового» состояния при повороте экрана.

Это быстро и отлично работает. Я все еще использую диалоги для других задач и AsyncTask для чего-то, что не требует постоянного диалога на экране. Но в этом сценарии я всегда использую шаблон «Активность / Диалог».

И я не пробовал, но даже можно заблокировать вращение Activity / Dialog во время работы потока, ускоряя процесс, одновременно позволяя вызывающему Activity вращаться.

1
ответ дан Rui 15 January 2014 в 20:16
поделиться

Самое простое и гибкое решение - использовать AsyncTask со статической ссылкой на ProgressBar . Это обеспечивает инкапсулированное и, следовательно, многоразовое решение проблем изменения ориентации. Это решение хорошо мне помогло для решения различных асинхронных задач, включая загрузку через Интернет, обмен данными с Сервисами и сканирование файловой системы. Решение было хорошо протестировано на нескольких версиях Android и моделях телефонов. Полная демонстрация может быть найдена здесь с особым интересом к DownloadFile.java

Я представляю следующее в качестве примера концепции

public class SimpleAsync extends AsyncTask<String, Integer, String> {
    private static ProgressDialog mProgressDialog = null;
    private final Context mContext;

    public SimpleAsync(Context context) {
        mContext = context;
        if ( mProgressDialog != null ) {
            onPreExecute();
        }
    }

    @Override
    protected void onPreExecute() {
        mProgressDialog = new ProgressDialog( mContext );
        mProgressDialog.show();
    }

    @Override
    protected void onPostExecute(String result) {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress( progress[0] );
    }

    @Override
    protected String doInBackground(String... sUrl) {
        // Do some work here
        publishProgress(1);
        return null;
    }

    public void dismiss() {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
        }
    }
}

Использование в Android Активность проста

public class MainActivity extends Activity {
    DemoServiceClient mClient = null;
    DownloadFile mDownloadFile = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        mDownloadFile = new DownloadFile( this );

        Button downloadButton = (Button) findViewById( R.id.download_file_button );
        downloadButton.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDownloadFile.execute( "http://www.textfiles.com/food/bakebred.txt");
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mDownloadFile.dismiss();
    }
}
0
ответ дан Derrick J Wippler 15 January 2014 в 20:16
поделиться

Первоначальная проблема заключалась в том, что код не выдерживал изменения ориентации экрана. По-видимому, это было «решено», когда программа сама обрабатывала изменение ориентации экрана, а не позволяла каркасу пользовательского интерфейса (через вызов onDestroy)).

Я бы сказал, что если основная проблема заключается в том, что программа не выживет в Destroy (), то принятое решение - это просто обходной путь, который оставляет программу с другими серьезными проблемами и уязвимостями. Помните, что платформа Android специально заявляет, что ваша деятельность может быть уничтожена практически в любое время из-за обстоятельств, не зависящих от вас. Таким образом, ваша деятельность должна быть в состоянии выжить в onDestroy () и последующих onCreate () по любой причине, а не только в изменении ориентации экрана.

Если вы собираетесь самостоятельно обрабатывать изменения ориентации экрана для решения проблемы OP, вам необходимо убедиться, что другие причины onDestroy () не приводят к той же ошибке. Вы можете сделать это? Если нет, я бы спросил, действительно ли «принятый» ответ очень хороший.

14
ответ дан gymshoe 15 January 2014 в 20:16
поделиться

Я придумал надежное решение для этих проблем, которое соответствует «Android Way» вещей. У меня есть все мои длительные операции с использованием шаблона IntentService.

То есть, моя деятельность передает намерения, IntentService выполняет работу, сохраняет данные в БД и затем передает липкие намерения. Важная часть важна, так что даже если действие было приостановлено в течение времени после того, как пользователь начал работу и пропускает трансляцию в реальном времени от IntentService, мы все равно можем ответить и забрать данные из вызывающего действия. ProgressDialog с этим паттерном вполне могут работать с onSaveInstanceState().

По сути, вам нужно сохранить флаг, что у вас есть запущенный диалог в сохраненном экземпляре пакета. Не сохраняйте объект диалога прогресса, потому что это приведет к утечке всей активности. Чтобы иметь постоянный дескриптор диалога прогресса, я храню его как слабую ссылку в объекте приложения. В случае изменения ориентации или чего-либо еще, что приводит к приостановке действия (телефонный звонок, пользователь попадает домой и т. Д.), А затем возобновляется, я закрываю старый диалог и заново создаю новый диалог во вновь созданном действии.

Для неопределенных диалогов прогресса это легко. Для стиля индикатора выполнения вы должны поместить в пакет последний известный прогресс и любую информацию, которую вы используете локально в упражнении, чтобы отслеживать прогресс. При восстановлении прогресса вы будете использовать эту информацию, чтобы повторно вызывать индикатор выполнения в том же состоянии, что и раньше, а затем обновлять его в зависимости от текущего состояния.

Итак, подведем итог: размещение длительных задач в IntentService в сочетании с разумным использованием onSaveInstanceState() позволяет эффективно отслеживать диалоги и затем восстанавливать их в событиях жизненного цикла Activity. Соответствующие биты кода активности приведены ниже. Вам также понадобится логика в BroadcastReceiver для надлежащей обработки намерений Sticky, но это выходит за рамки этого.

public void doSignIn(View view) {
    waiting=true;
    AppClass app=(AppClass) getApplication();
    String logingon=getString(R.string.signon);
    app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    ...
}

@Override
protected void onSaveInstanceState(Bundle saveState) {
    super.onSaveInstanceState(saveState);
    saveState.putBoolean("waiting",waiting);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState!=null) {
        restoreProgress(savedInstanceState);    
    }
    ...
}

private void restoreProgress(Bundle savedInstanceState) {
    waiting=savedInstanceState.getBoolean("waiting");
    if (waiting) {
        AppClass app=(AppClass) getApplication();
        ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
        refresher.dismiss();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    }
}
66
ответ дан neonblitzer 15 January 2014 в 20:16
поделиться

Если вы создадите фон Service , который выполняет всю тяжелую работу (запросы / ответы tcp, демаршаллинг), View и Activity могут быть уничтожены и воссозданы без утечки окна или потерять данные. Это позволяет поведение, рекомендованное Android, которое заключается в уничтожении активности при каждом изменении конфигурации (например, для каждого изменения ориентации).

Это немного сложнее, но это лучший способ для вызова запроса к серверу, предварительной / пост-обработки данных и т. Д.

Вы можете даже использовать Service для постановки каждого запроса в очередь на сервер, поэтому с ним легко и эффективно справляться.

В руководстве разработчика есть полная глава , посвященная Services .

2
ответ дан rds 15 January 2014 в 20:16
поделиться

Я собираюсь поделиться своим подходом к решению этой проблемы ротации. Это может не относиться к OP, поскольку он не использует AsyncTask, но, возможно, другие найдут его полезным. Это довольно просто, но мне кажется, что эта работа мне подходит:

У меня есть вход в систему с вложенным классом AsyncTask, который называется BackgroundLoginTask.

В моем BackgroundLoginTask я не делаю ничего необычного, кроме добавления нулевой проверки при вызове dismiss ProgressDialog:

@Override
protected void onPostExecute(Boolean result)
{    
if (pleaseWaitDialog != null)
            pleaseWaitDialog.dismiss();
[...]
}

Это для обработки случая, когда Фоновое задание завершается, пока Activity не виден, и поэтому диалоговое окно прогресса уже закрыто методом onPause().

Затем, в моем родительском классе Activity, я создаю глобальные статические дескрипторы для моего класса AsyncTask и моего ProgressDialog (будучи вложенным, AsyncTask может обращаться к этим переменным):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

Это служит двум целям: во-первых, это позволяет my Activity всегда получать доступ к объекту AsyncTask даже из новой, пост-повернутой деятельности. Во-вторых, он позволяет моему BackgroundLoginTask получать доступ и отклонять ProgressDialog даже после поворота.

Затем я добавляю это к onPause(), в результате чего диалоговое окно прогресса исчезает, когда наш Activity покидает передний план (предотвращая это безобразное «принудительное закрытие» сбоя):

    if (pleaseWaitDialog != null)
    pleaseWaitDialog.dismiss();

Наконец, У меня есть следующее в моем методе onResume():

if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }

Это позволяет Dialog снова появляться после воссоздания Activity.

Вот весь класс:

public class NSFkioskLoginActivity extends NSFkioskBaseActivity {
    private static BackgroundLoginTask backgroundLoginTask;
    private static ProgressDialog pleaseWaitDialog;
    private Controller cont;

    // This is the app entry point.
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CredentialsAvailableAndValidated())
        {
        //Go to main menu and don't run rest of onCreate method.
            gotoMainMenu();
            return;
        }
        setContentView(R.layout.login);
        populateStoredCredentials();   
    }

    //Save current progress to options when app is leaving foreground
    @Override
    public void onPause()
    {
        super.onPause();
        saveCredentialsToPreferences(false);
        //Get rid of progress dialog in the event of a screen rotation. Prevents a crash.
        if (pleaseWaitDialog != null)
        pleaseWaitDialog.dismiss();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }
    }

    /**
     * Go to main menu, finishing this activity
     */
    private void gotoMainMenu()
    {
        startActivity(new Intent(getApplicationContext(), NSFkioskMainMenuActivity.class));
        finish();
    }

    /**
     * 
     * @param setValidatedBooleanTrue If set true, method will set CREDS_HAVE_BEEN_VALIDATED to true in addition to saving username/password.
     */
    private void saveCredentialsToPreferences(boolean setValidatedBooleanTrue)
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = settings.edit();
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
        prefEditor.putString(USERNAME, usernameText.getText().toString());
        prefEditor.putString(PASSWORD, pswText.getText().toString());
        if (setValidatedBooleanTrue)
        prefEditor.putBoolean(CREDS_HAVE_BEEN_VALIDATED, true);
        prefEditor.commit();
    }

    /**
     * Checks if user is already signed in
     */
    private boolean CredentialsAvailableAndValidated() {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
                MODE_PRIVATE);
        if (settings.contains(USERNAME) && settings.contains(PASSWORD) && settings.getBoolean(CREDS_HAVE_BEEN_VALIDATED, false) == true)
         return true;   
        else
        return false;
    }

    //Populate stored credentials, if any available
    private void populateStoredCredentials()
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
            MODE_PRIVATE);
        settings.getString(USERNAME, "");
       EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
       usernameText.setText(settings.getString(USERNAME, ""));
       EditText pswText = (EditText) findViewById(R.id.editTextPassword);
       pswText.setText(settings.getString(PASSWORD, ""));
    }

    /**
     * Validate credentials in a seperate thread, displaying a progress circle in the meantime
     * If successful, save credentials in preferences and proceed to main menu activity
     * If not, display an error message
     */
    public void loginButtonClick(View view)
    {
        if (phoneIsOnline())
        {
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
           //Call background task worker with username and password params
           backgroundLoginTask = new BackgroundLoginTask();
           backgroundLoginTask.execute(usernameText.getText().toString(), pswText.getText().toString());
        }
        else
        {
        //Display toast informing of no internet access
        String notOnlineMessage = getResources().getString(R.string.noNetworkAccessAvailable);
        Toast toast = Toast.makeText(getApplicationContext(), notOnlineMessage, Toast.LENGTH_SHORT);
        toast.show();
        }
    }

    /**
     * 
     * Takes two params: username and password
     *
     */
    public class BackgroundLoginTask extends AsyncTask<Object, String, Boolean>
    {       
       private Exception e = null;

       @Override
       protected void onPreExecute()
       {
           cont = Controller.getInstance();
           //Show progress dialog
           String pleaseWait = getResources().getString(R.string.pleaseWait);
           String commWithServer = getResources().getString(R.string.communicatingWithServer);
            if (pleaseWaitDialog == null)
              pleaseWaitDialog= ProgressDialog.show(NSFkioskLoginActivity.this, pleaseWait, commWithServer, true);

       }

        @Override
        protected Boolean doInBackground(Object... params)
        {
        try {
            //Returns true if credentials were valid. False if not. Exception if server could not be reached.
            return cont.validateCredentials((String)params[0], (String)params[1]);
        } catch (Exception e) {
            this.e=e;
            return false;
        }
        }

        /**
         * result is passed from doInBackground. Indicates whether credentials were validated.
         */
        @Override
        protected void onPostExecute(Boolean result)
        {
        //Hide progress dialog and handle exceptions
        //Progress dialog may be null if rotation has been switched
        if (pleaseWaitDialog != null)
             {
            pleaseWaitDialog.dismiss();
                pleaseWaitDialog = null;
             }

        if (e != null)
        {
         //Show toast with exception text
                String networkError = getResources().getString(R.string.serverErrorException);
                Toast toast = Toast.makeText(getApplicationContext(), networkError, Toast.LENGTH_SHORT);
            toast.show();
        }
        else
        {
            if (result == true)
            {
            saveCredentialsToPreferences(true);
            gotoMainMenu();
            }
            else
            {
            String toastText = getResources().getString(R.string.invalidCredentialsEntered);
                Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
            toast.show();
            } 
        }
        }

    }
}

Я ни в коем случае не опытный разработчик Android, поэтому не стесняйтесь комментировать.

7
ответ дан Catalina 15 January 2014 в 20:16
поделиться

Это - мое решение, когда я столкнулся с ним: ProgressDialog не Fragment ребенок, таким образом, мой пользовательский класс "ProgressDialogFragment" может расшириться DialogFragment вместо этого для хранения диалогового окна показанным для изменений конфигурации.

import androidx.annotation.NonNull;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle; 
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

 /**
 * Usage:
 * To display the dialog:
 *     >>> ProgressDialogFragment.showProgressDialogFragment(
 *              getSupportFragmentManager(), 
 *              "fragment_tag", 
 *              "my dialog title", 
 *              "my dialog message");
 *              
 * To hide the dialog
 *     >>> ProgressDialogFragment.hideProgressDialogFragment();
 */ 


public class ProgressDialogFragment extends DialogFragment {

    private static String sTitle, sMessage;
    private static ProgressDialogFragment sProgressDialogFragment;

    public ProgressDialogFragment() {
    }

    private ProgressDialogFragment(String title, String message) {
        sTitle = title;
        sMessage = message;
    }


    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return ProgressDialog.show(getActivity(), sTitle, sMessage);
    }

    public static void showProgressDialogFragment(FragmentManager fragmentManager, String fragmentTag, String title, String message) {
        if (sProgressDialogFragment == null) {
            sProgressDialogFragment = new ProgressDialogFragment(title, message);
            sProgressDialogFragment.show(fragmentManager, fragmentTag);

        } else { // case of config change (device rotation)
            sProgressDialogFragment = (ProgressDialogFragment) fragmentManager.findFragmentByTag(fragmentTag); // sProgressDialogFragment will try to survive its state on configuration as much as it can, but when calling .dismiss() it returns NPE, so we have to reset it on each config change
            sTitle = title;
            sMessage = message;
        }

    }

    public static void hideProgressDialogFragment() {
        if (sProgressDialogFragment != null) {
            sProgressDialogFragment.dismiss();
        }
    }
}

проблема состояла в том, чтобы сохранить диалоговый заголовок & обменивайтесь сообщениями, в то время как экранное вращение, поскольку они сбрасывают к пустой строке по умолчанию, хотя диалоговое окно, все еще показанное

существует 2 подхода для решения этого:

Первый подход: Делают действие, которое использует диалоговое окно для сохранения состояния во время изменения конфигурации в файле манифеста:

android:configChanges="orientation|screenSize|keyboardHidden"

Этот подход не предпочтен Google.

1119-секундный подход: на действии onCreate() метод, необходимо сохранить Ваш DialogFragment путем восстановления ProgressDialogFragment снова с заголовком & обменивайтесь сообщениями следующим образом, если эти savedInstanceState не является пустым:

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_deal);

 if (savedInstanceState != null) {
      ProgressDialogFragment saveProgressDialog = (ProgressDialogFragment) getSupportFragmentManager()
              .findFragmentByTag("fragment_tag");
      if (saveProgressDialog != null) {
          showProgressDialogFragment(getSupportFragmentManager(), "fragment_tag", "my dialog title", "my dialog message");
      }
  }
}
1
ответ дан Zain 17 September 2019 в 06:24
поделиться
  • 1
    @JoelFan - Если необходимо использовать отладчик для понимания рабочего кода, тот код должен быть переписан. – Stephen C 22 December 2009 в 00:54

Когда Вы изменяете ориентации, Android уничтожают то действие и создал новое действие. Я предлагаю использовать модификацию с Rx Java. которые обрабатывают катастрофические отказы автоматически.

Использование они метод, когда вызов модификации.

.subscribeOn (Schedulers.io ()) .observeOn (AndroidSchedulers.mainThread ())

0
ответ дан 22 November 2019 в 22:31
поделиться

Переместите длинную задачу в отдельную учебный класс. Реализуйте это как шаблон субъект-наблюдатель. Каждый раз, когда создается активность, регистрируйтесь, а при закрытии - отменяйте регистрацию в классе задачи. Класс задачи может использовать AsyncTask.

4
ответ дан 22 November 2019 в 22:31
поделиться

Попробуйте использовать шаблон вместо макроса.

Скотт Мейерс: эффективный C++ Пункт 2: Предпочтительны баллы, перечисления и строки для # определяет

-121--4585649-

Я встретил ту же самую проблему. Мои действия должны анализировать некоторые данные с URL-адреса, и это медленно. Поэтому я создаю поток для этого, а затем показываю диалог хода выполнения. Я разрешаю потоку отправить сообщение обратно в поток пользовательского интерфейса через Обработчик по завершении. В Handler.handleMessage объект данных (готовый сейчас) передается из потока в пользовательский интерфейс. Так что это очень похоже на ваш пример.

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

То, что я сделал, показано ниже. Цель состоит в том, чтобы заполнить мою модель данных ( mDataObject ) и затем заполнить ее пользовательским интерфейсом. Должна позволять вращение экрана в любой момент без удивления.

class MyActivity {

    private MyDataObject mDataObject = null;
    private static MyThread mParserThread = null; // static, or make it singleton

    OnCreate() {
        ...
        Object retained = this.getLastNonConfigurationInstance();
        if(retained != null) {
            // data is already completely obtained before config change
            // by my previous self.
            // no need to create thread or show dialog at all
            mDataObject = (MyDataObject) retained;
            populateUI();
        } else if(mParserThread != null && mParserThread.isAlive()){
            // note: mParserThread is a static member or singleton object.
            // config changed during parsing in previous instance. swap handler
            // then wait for it to finish.
            mParserThread.setHandler(new MyHandler());
        } else {
            // no data and no thread. likely initial run
            // create thread, show dialog
            mParserThread = new MyThread(..., new MyHandler());
            mParserThread.start();
            showDialog(DIALOG_PROGRESS);
        }
    }

    // http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html
    public Object onRetainNonConfigurationInstance() {
        // my future self can get this without re-downloading
        // if it's already ready.
        return mDataObject;
    }

    // use Activity.showDialog instead of ProgressDialog.show
    // so the dialog can be automatically managed across config change
    @Override
    protected Dialog onCreateDialog(int id) {
        // show progress dialog here
    }

    // inner class of MyActivity
    private class MyHandler extends Handler {
        public void handleMessage(msg) {
            mDataObject = mParserThread.getDataObject();
            populateUI();
            dismissDialog(DIALOG_PROGRESS);
        }
    }
}

class MyThread extends Thread {
    Handler mHandler;
    MyDataObject mDataObject;

    // constructor with handler param
    public MyHandler(..., Handler h) {
        ...
        mHandler = h;
    }

    public void setHandler(Handler h) { mHandler = h; } // for handler swapping after config change
    public MyDataObject getDataObject() { return mDataObject; } // return data object (completed) to caller

    public void run() {
        mDataObject = new MyDataObject();
        // do the lengthy task to fill mDataObject with data
        lengthyTask(mDataObject);
        // done. notify activity
        mHandler.sendEmptyMessage(0); // tell activity: i'm ready. come pick up the data.
    }
}

Это то, что работает для меня. Я не знаю, является ли это «правильным» методом, разработанным Android - они утверждают, что это «уничтожить/воссоздать активность во время вращения экрана» на самом деле делает вещи проще, так что я думаю, что это не должно быть слишком сложно.

Дайте мне знать, если вы видите проблему в моем коде. Как сказано выше, я на самом деле не знаю, есть ли какой-либо побочный эффект.

28
ответ дан 22 November 2019 в 22:31
поделиться

Изменить: Инженеры Google не рекомендуют этот подход, как описано Дайанн Хакборн (она же hackbod ) в сообщении StackOverflow . Прочтите эту запись в блоге для получения дополнительной информации.


Вы должны добавить это к объявлению активности в манифесте:

android:configChanges="orientation|screenSize"

, чтобы это выглядело как

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

Дело в том, что система уничтожает активность, когда происходит изменение конфигурации. См. Изменения конфигурации .

Таким образом, включение этого параметра в файл конфигурации не позволит системе разрушить вашу деятельность. Вместо этого он вызывает метод onConfigurationChanged (Configuration) .

259
ответ дан 22 November 2019 в 22:31
поделиться

Я нашел решение этой проблемы, которого я еще нигде не видел. Вы можете использовать настраиваемый объект приложения, который знает, выполняются ли у вас фоновые задачи, вместо того, чтобы пытаться сделать это в действии, которое уничтожается и воссоздается при изменении ориентации. Я писал об этом в здесь .

7
ответ дан 22 November 2019 в 22:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: