Подтвердить что ты не робот

Почему runnable callbacks уничтожает активность автоматически?

Я хотел знать, есть ли вероятность, что мы могли бы обрабатывать/обнаруживать runnable callbacks с задержкой (postDelayed method) на android?

Например, у меня есть одно или несколько заставки (которые работают с handler.postDelayed(new Runnable()...) в моем приложении (приложение для целей тестирования). В этом приложении у меня также есть библиотека (которую я создаю и использую в приложении), а также некоторые доступные там классы, которые работают в классе IntentService.

Иногда, когда приложение запускает те splashscreen Activities (для Testing purpose), библиотека, которую я создаю, может автоматически отображать некоторые действия в пользовательском интерфейсе. Но похоже, что если эти действия выполняются в действии splashscreen и что splashscreen уничтожается, эти действия (которые автоматически появляются) также будут уничтожены и регистрируются сообщение "просочившееся окно" в logcat.

Проблема в том, что:

  • те действия, которые автоматически появляются в пользовательском интерфейсе, не должны автоматически закрывается, это запрещено. Это требует взаимодействия от пользователя для закрытия этого действия и возврата к нормальному поведению приложения.
  • Кроме того, библиотека ничего не знает о пользовательском интерфейсе приложения.

Итак, мои вопросы (относительно библиотеки, которую я создаю, не имея информации о потоке приложения UI):

  • Есть ли способ определить, был ли создан какой-то метод postDelayed в приложении относительно библиотеки? Если да, как я могу справиться с этой проблемой?

P.S.: Обратите внимание, что обычно я использую диалоговое окно для действия Activity, которое появляется автоматически.

UPDATE

Диаграмма

Объяснение диаграммы:

Сейчас у меня есть случай, когда выполняется Splashscreen.

Класс, который расширяет класс IntentService, получил запрос из Интернета, который запустит Activity.

Между тем, заставка находится на postDelayed, другая Деятельность была создана и отображается в пользовательском интерфейсе. Когда X секунд прошло, а другая Деятельность не была уничтожена, создается Следующая активность и автоматически уничтожается другая Деятельность. При этом Android выдает сообщение "просочившееся окно" относительно Активность.

4b9b3361

Ответ 1

Есть ли способ определить, был ли создан какой-то метод postDelayed в приложении относительно библиотеки?

Вы можете использовать MessageQueue.IdleHandler API. См. LooperIdlingResource, как эспрессо выясняет, правильно ли это время срабатывать в утверждении.


    @Override
    public boolean queueIdle() {

      QueueState queueState = myInterrogator.determineQueueState();
      if (queueState == QueueState.EMPTY || queueState == QueueState.TASK_DUE_LONG) {
        ...
      } else if (queueState == QueueState.BARRIER) {
        ...
      }

      return true;
    }

Это поможет вам понять, есть ли сообщение в MessageQueue, но оно не скажет вам, какое именно сообщение существует.

Решение, с которым я столкнулся, состояло в том, чтобы отключить Runnable, который у вас есть postDelayed внутри onStop активности, потому что если был запущен Activity (один из библиотеки), чем onStop of SplashScreen:


public class SplashActivity extends AppCompatActivity {

  private final Runnable myRunnable = () -> {
    // launch `NextActivity`
  };
  private final Handler handler = new Handler();

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

    handler.postDelayed(myRunnable, 3000);
  }

  @Override
  protected void onStop() {
    super.onStop();
    handler.removeCallbacks(myRunnable);
  }

}

Ответ 2

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

Относительно postDelayed(), В общем, если вы делаете

 mHandler.postDelayed(new Runnable() { ... });

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

Runnable mLaunchSplashRunnable = new Runnable() { ... };
Runnable mLaunchContactsRunnable = new Runnable() { ... };

.
.

mHandler.postDelayed (mLaunchSplashRunnable, DELAY);
mHandler.postDelayed (mLaunchContactsRunnable, DELAY);

.
.

Поскольку runnables теперь не анонимны, вы можете удалить их из очереди в любое время:

void removeLibraryDelayedRunnables() {
   mHandler.removeCallbacks(mLaunchSplashRunnable);
   mHandler.removeCallbacks(mLaunchContactsRunnable);
}

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

Метод запроса Handler, если задана конкретная Runnable, afaik, не существует, но вы можете использовать флаг boolean, установить его, когда runnable поставлен в очередь, и reset it при запуске Runnable, чтобы указать, что runnable находится в ожидании.

Если я лучше понимаю вашу проблему, я бы смог больше помочь.

Ответ 3

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

https://developer.android.com/guide/components/activities/activity-lifecycle.html

см. после onStop()... это относится к вашему приложению.

Надеюсь, это поможет вам...

Ответ 4

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

Ответ 5

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

Для этого вы можете зарегистрировать BroadcastReceiver для создания каждого вида деятельности https://developer.android.com/reference/android/content/BroadcastReceiver.html и использовать sendBroadcast из службы https://developer.android.com/guide/components/broadcasts.html при запуске операции необходимо, чтобы передать широковещательный приемник для начала необходимой активности.

Ответ 6

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

Добавляя наблюдателя к активности, которая нуждается в данных, которая следит за тем, выполняется ли сетевой вызов, вы показываете данные только активному наблюдателю. Таким образом, если активность закрыта до завершения вызова, нет "нажатия" данных на закрытую активность.

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

Ответ 7

Так как мы еще не видели ваш код. Мой ответ будет слишком общим, поэтому извините за это.

Я думаю, вы должны сначала проверить logcat, чтобы узнать, не происходит ли какое-либо исключение, закрывая эти действия. Если в этом нет ничего. Просто проверьте все блоки catch catch, чтобы быть уверенными в этом. Поэтому, после того как вы уверены, что речь идет не о каких-либо исключениях, проверьте файл AndroidManifest, чтобы увидеть "Запуск режимов". Это также может привести к автоматическому закрытию.

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

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

Я думаю, что у вас должен быть такой механизм, как eventbus или широковещательный приемник для внутренней связи между экранами. Они будут работать на жизненном цикле вашей деятельности и не приведут ни к какому-либо исключению или anr.

Ответ 8

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

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

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    listener = new WeakReference<>((MyActionListener) activity);
    activityWeakReference = new WeakReference<>(activity);
}

Прежде чем показывать диалог, я также должен убедиться, что Activity не находится в процессе отделки:

if (myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()) {
  // show dialog
}

Затем, когда вы хотите контролировать вращение устройства, где действия заново созданный, вы можете использовать следующее вместе со своими диалогами:

    @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

/**
 * Prevent the dialog being dismissed when rotated, when using setRetainInstance
 */
@Override
public void onDestroyView() {
    if (getDialog() != null && getRetainInstance()) {
        getDialog().setDismissMessage(null);
    }
    super.onDestroyView();
}