Android Kotlin Coroutine при повороте экрана

Я запускаю сопрограмму, которая после указанной задержки отображает на экране значение счетчика.

job = launch(UI) {
    var count= 0
      while (true) {
        textView.text = "${count++}"
        delay(200L)
      }
   }

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


person user5407225    schedule 15.09.2018    source источник


Ответы (3)


Есть ли у кого-нибудь идеи, как возобновить работу по изменению конфигурации (например, поворота экрана).

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

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

Я заставил свои сопрограммы работать с воссозданием активности, полагаясь на Fragment, в котором я установил

retainInstance = true

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

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

  1. onDestroyView
  2. onCreateView

Это не проходит _6 _ / _ 7_, это происходит только при переключении действий или выходе из приложения. Вы можете запустить свою сопрограмму в onResume и отменить ее в onPause.

Начиная с недавно выпущенной версии 0.23 kotlinx.coroutines, launch стал функцией расширения: вы должны вызывать ее в контексте некоторого CoroutineScope, которое контролирует жизненный цикл результирующего задания. Вы должны привязать его жизненный цикл к фрагменту, поэтому пусть ваш фрагмент реализует CoroutineScope. Другое изменение состоит в том, что контекст сопрограммы UI теперь устарел в пользу Dispatchers.Main.

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

class MyFragment : Fragment, CoroutineScope {
    private var textView: TextView? = null
    private var rootJob = Job()

    override val coroutineContext: CoroutineContext 
        get() = Dispatchers.Main + rootJob

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance = true
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        val rootView = inflater.inflate(R.layout.frag_id, container, false)
        this.textView = rootView.findViewById(R.id.textview)
        return rootView
    }

    override fun onDestroyView() {
        this.textView = null
    }

    override fun onResume() {
        this.launch {
            var count = 0
            while (true) {
                textView?.text = "$count"
                count++
                delay(200L)
            }
        }    
    }

    override fun onPause() {
        rootJob.cancel()
        rootJob = Job()
    }
}

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

person Marko Topolnik    schedule 16.09.2018

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

Возможны следующие варианты:

1) Добавьте configSettings в свой манифест, чтобы отключить это поведение.

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

Лично, если у вас нет другого макета для портретной и альбомной ориентации, я бы просто выбрал номер 1, это проще.

person Gabe Sechan    schedule 15.09.2018

Вы можете сделать это в ViewModel вместо Activity.

person Mike    schedule 15.09.2018
comment
Думаю, я неправильно ответил на вопрос. В основном мой вопрос больше связан с перезапуском задания kotlin coroutine. Могу ли я сохранить саму Job и возобновить ее? - person user5407225; 15.09.2018
comment
Вам все еще нужно место для хранения работы. - person Mike; 16.09.2018