2017-01-29 3 views
10

У меня есть простая программа таймера в Flutter, которая показывает обратный отсчет с оставшимся количеством секунд. У меня есть:Как запустить код в фоновом режиме, даже если экран выключен?

new Timer.periodic(new Duration(seconds: 1), _decrementCounter); 

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

+0

Возможно, настоящий вопрос: возможно ли запустить код в фоновом режиме (например, таймеры) для приложения Flutter с уничтожением Activity? В моем случае таймер будет продолжать работать, даже если я выключу дисплей (см. Ответ ниже). –

ответ

6

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

  • Framework пересмотр b339c71523 (6 часов назад), 2017-02-04 00:51 : 32
  • редакция двигателя cd34b0ef39

Создать новое приложение флаттера, и заменить содержимое lib/main.dart с этим кодом:

import 'dart:async'; 

import 'package:flutter/material.dart'; 

void main() { 
    runApp(new MyApp()); 
} 

class LifecycleWatcher extends StatefulWidget { 
    @override 
    _LifecycleWatcherState createState() => new _LifecycleWatcherState(); 
} 

class _LifecycleWatcherState extends State<LifecycleWatcher> 
    with WidgetsBindingObserver { 
    AppLifecycleState _lastLifecyleState; 

    @override 
    void initState() { 
    super.initState(); 
    WidgetsBinding.instance.addObserver(this); 
    } 

    @override 
    void dispose() { 
    WidgetsBinding.instance.removeObserver(this); 
    super.dispose(); 
    } 

    @override 
    void onDeactivate() { 
    super.deactivate(); 
    } 

    @override 
    void didChangeAppLifecycleState(AppLifecycleState state) { 
    print("LifecycleWatcherState#didChangeAppLifecycleState state=${state.toString()}"); 
    setState(() { 
     _lastLifecyleState = state; 
    }); 
    } 

    @override 
    Widget build(BuildContext context) { 
    if (_lastLifecyleState == null) 
     return new Text('This widget has not observed any lifecycle changes.'); 
    return new Text(
     'The most recent lifecycle state this widget observed was: $_lastLifecyleState.'); 
    } 
} 

class MyApp extends StatelessWidget { 
    // This widget is the root of your application. 
    @override 
    Widget build(BuildContext context) { 
    return new MaterialApp(
     title: 'Flutter Demo', 
     theme: new ThemeData(
     primarySwatch: Colors.blue, 
    ), 
     home: new MyHomePage(title: 'Flutter App Lifecycle'), 
    ); 
    } 
} 

class MyHomePage extends StatefulWidget { 
    MyHomePage({Key key, this.title}) : super(key: key); 

    final String title; 

    @override 
    _MyHomePageState createState() => new _MyHomePageState(); 
} 

class _MyHomePageState extends State<MyHomePage> { 
    int _timerCounter = 0; 
    // ignore: unused_field only created once 
    Timer _timer; 

    _MyHomePageState() { 
    print("_MyHomePageState#constructor, creating new Timer.periodic"); 
    _timer = new Timer.periodic(
     new Duration(milliseconds: 3000), _incrementTimerCounter); 
    } 

    void _incrementTimerCounter(Timer t) { 
    print("_timerCounter is $_timerCounter"); 
    setState(() { 
     _timerCounter++; 
    }); 
    } 

    @override 
    Widget build(BuildContext context) { 
    return new Scaffold(
     appBar: new AppBar(
     title: new Text(config.title), 
    ), 
     body: new Block(
     children: [ 
      new Text(
      'Timer called $_timerCounter time${ _timerCounter == 1 ? '' : 's' }.', 
     ), 
      new LifecycleWatcher(), 
     ], 
    ), 
    ); 
    } 
} 

При запуске приложения значение _timerCounter увеличивается каждые 3 секунды. Текстовое поле ниже счетчика будет показывать любые AppLifecycleState изменений для приложения Flutter, вы увидите соответствующий вывод в журнале отладки Flutter, например:

[[email protected]:~/flutter/helloworld]$ flutter run 
Launching lib/main.dart on SM N920S in debug mode... 
Building APK in debug mode (android-arm)...   6440ms 
Installing build/app.apk...       6496ms 
I/flutter (28196): _MyHomePageState#constructor, creating new Timer.periodic 
Syncing files to device... 
I/flutter (28196): _timerCounter is 0 

    To hot reload your app on the fly, press "r" or F5. To restart the app entirely, press "R". 
The Observatory debugger and profiler is available at: http://127.0.0.1:8108/ 
For a more detailed help message, press "h" or F1. To quit, press "q", F10, or Ctrl-C. 
I/flutter (28196): _timerCounter is 1 
I/flutter (28196): LifecycleWatcherState#didChangeAppLifecycleState state=AppLifecycleState.paused 
I/flutter (28196): _timerCounter is 2 
I/flutter (28196): _timerCounter is 3 
I/flutter (28196): LifecycleWatcherState#didChangeAppLifecycleState state=AppLifecycleState.resumed 
I/flutter (28196): _timerCounter is 4 
I/flutter (28196): LifecycleWatcherState#didChangeAppLifecycleState state=AppLifecycleState.paused 
I/flutter (28196): _timerCounter is 5 
I/flutter (28196): _timerCounter is 6 
I/flutter (28196): _timerCounter is 7 
I/flutter (28196): LifecycleWatcherState#didChangeAppLifecycleState state=AppLifecycleState.resumed 
I/flutter (28196): LifecycleWatcherState#didChangeAppLifecycleState state=AppLifecycleState.paused 
I/flutter (28196): _timerCounter is 8 
I/flutter (28196): _MyHomePageState#constructor, creating new Timer.periodic 
I/flutter (28196): _timerCounter is 0 
I/flutter (28196): _timerCounter is 1 

Для вышеприведенного вывода журнала, вот шаги, которые я сделал :

  1. Запуск приложения с flutter run
  2. Переключение в другое приложение (значение _timerCounter 1)
  3. Возврат к Flutter приложения (значение _timerCounter 3)
  4. прессованная кнопка питания, дисплей выключена (значение _timerCounter 4)
  5. разблокирована телефона, флаттер приложение возобновилось (значение _timerCounter 7)
  6. оттеснена кнопки на телефоне (значение _timerCounter не изменилось). Это момент, когда FlutterActivity уничтожается и Dart VM Isolate.
  7. Флаттер приложение возобновило (значение _timerCounter равно 0 раз)

Переключение между приложениями, нажав мощность или кнопку назад
При переключении на другое приложение, или при нажатии на кнопку питания, чтобы включить в экране таймер продолжает работать. Но при нажатии кнопки «Назад», когда приложение «Флаттер» имеет фокус, «Активность» уничтожается, а вместе с ней выделяется дротик. Вы можете проверить это, подключившись к Dart Observatory при переключении между приложениями или повороте экрана. В Обсерватории будет показано активное приложение Isolate приложения Flutter. Но при нажатии кнопки «Назад» обсерватория не показывает бегущую изоляцию. Поведение было подтверждено в Galaxy Note 5 под управлением Android 6.x и Nexus 4 под управлением Android 4.4.x.

флаттер приложение жизненным циклом и Android Жизненный цикл Для виджета слоя Flutter только остановился и возобновили государства подвергаются. Уничтожить обрабатывается Android Activity для приложения Android Flutter:

/** 
* @see android.app.Activity#onDestroy() 
*/ 
@Override 
protected void onDestroy() { 
    if (flutterView != null) { 
     flutterView.destroy(); 
    } 
    super.onDestroy(); 
} 

Поскольку Dart VM для флаттер приложение работает внутри деятельности, виртуальная машина будет остановлена ​​каждый раз, когда активность разрушается.

код логики Детонация двигателя
Это не прямо ответить на ваш вопрос, но даст вам некоторые более подробную справочную информацию о том, как флаттер двигатель обрабатывает изменения состояния для Android.
Просматривая код двигателя Flutter, становится очевидным, что цикл анимации приостанавливается, когда FlutterActivity получает событие Android Activity#onPause. Когда приложение переходит в паузе состояния, в соответствии с source comment here происходит следующее:

«Приложение в настоящее время не видно пользователю Когда приложение находится в этом состоянии, двигатель не будет вызывать [onBeginFrame. ] Перезвони."

Основываясь на моих тестах, таймер продолжает работать даже при приостановке отображения пользовательского интерфейса, что имеет смысл. Было бы неплохо отправить событие в слой виджетов, используя WidgetsBindingObserver, когда действие будет уничтожено, поэтому разработчики могут следить за сохранением состояния приложения Flutter до тех пор, пока активность не будет возобновлена.

4

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

Например, IOS документации обсуждает фоновый код более подробно здесь: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

Вместо мобильных операционных систем обеспечивают Apis (как таймер/тревога/уведомление Apis) перезвонить в приложение по истечении определенного времени. Например, на iOS вы можете запросить, чтобы ваше приложение было уведомлено/прозвано в определенный момент в будущем через UINotificationRequest: https://developer.apple.com/reference/usernotifications/unnotificationrequest Это позволяет им убивать/приостанавливать ваше приложение, чтобы добиться большей экономии энергии и вместо этого иметь единую высокоэффективную общую системное обслуживание для отслеживания этих уведомлений/аварийных сигналов/геообъективов и т. д.

В настоящее время Flutter не предоставляет каких-либо оберток вокруг этих услуг ОС, но его можно написать самостоятельно, используя нашу платформу-сервисную модель : flutter.io/platform-services

Мы работаем над системой публикации/совместного использования сервисов таким образом, чтобы один человек на запись этой интеграции (например, планирование будущего исполнения вашего приложения) каждый может извлечь выгоду.

Отдельно, более общий вопрос: «возможно ли запустить фоновый код Дарта» (без наличия активного экрана FlutterView на экране), «еще не».У нас есть ошибка в файл: https://github.com/flutter/flutter/issues/3671

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

Смежные вопросы