2017-01-29 8 views
10

मेरे पास फ़्लटर में एक साधारण टाइमर ऐप है, जो शेष सेकंड की संख्या के साथ उलटी गिनती दिखाता है। मेरे पास है:मैं पृष्ठभूमि में कोड कैसे चला सकता हूं, यहां तक ​​कि स्क्रीन के साथ भी?

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

यह (भले ही मैं एक ऐप्लिकेशन पर स्विच), जब तक मेरी फ़ोन के प्रदर्शन के स्विच बंद ठीक से काम करने लगता है और सोने के लिए चला जाता है। फिर, टाइमर रुक जाता है। स्क्रीन बंद होने पर भी पृष्ठभूमि में चलने वाली सेवा बनाने का कोई अनुशंसित तरीका है?

+0

शायद वास्तविक सवाल यह है: क्या गतिविधि को नष्ट करने के साथ एक फ्लटर ऐप के लिए पृष्ठभूमि (जैसे टाइमर) में कोड चलाने के लिए संभव है? मेरे मामले में टाइमर चलाना जारी रखेगा भले ही मैं डिस्प्ले बंद कर दूं (नीचे जवाब देखें)। –

उत्तर

6

संक्षिप्त उत्तर: नहीं, यह संभव नहीं है, हालांकि मैंने सोने के प्रदर्शन के लिए एक अलग व्यवहार देखा है। निम्नलिखित कोड आप Android पर एक स्पंदन अनुप्रयोग, इन स्पंदन और स्पंदन इंजन संस्करणों के साथ परीक्षण किया गया के विभिन्न राज्यों को समझने में मदद मिलेगी:

  • फ्रेमवर्क संशोधन 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 एस में बढ़ता है।

[[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 

ऊपर लॉग आउटपुट के लिए, यहां दिए गए चरणों मैंने किया हैं: काउंटर नीचे एक टेक्स्ट फ़ील्ड स्पंदन अनुप्रयोग के लिए किसी भी AppLifecycleState परिवर्तन भी दिखाई देंगे, आप स्पंदन डीबग लॉग में इसी उत्पादन, जैसे देखेंगे :

  1. ऐप्लिकेशन लॉन्च के साथ flutter run
  2. अन्य अनुप्रयोग पर स्विच करें (_timerCounter मूल्य 1) ​​
  3. स्पंदन एप्लिकेशन पर लौटें (_timerCounter मूल्य 3)
  4. दबाया पावर बटन, प्रदर्शन फोन पर बंद कर दिया (_timerCounter मूल्य 4)
  5. खुला फोन, स्पंदन एप्लिकेशन फिर से शुरू (_timerCounter मूल्य 7)
  6. वापस दबाया बटन (_timerCounter मूल्य बदल नहीं)। यही वह क्षण है जहां FlutterActivity नष्ट हो जाता है और डार्ट वीएम भी अलग हो जाता है।
  7. स्पंदन एप्लिकेशन फिर से शुरू (_timerCounter मान 0 फिर से है)

क्षुधा के बीच स्विच, बिजली या वापस बटन दबाने
दूसरे ऐप्स पर स्विच, या जब स्क्रीन के चालू करने के लिए पावर बटन दबाकर जब टाइमर चलना जारी है। लेकिन जब फ्टरर ऐप पर फोकस होता है तो बैक बटन दबाते समय, गतिविधि नष्ट हो जाती है, और इसके साथ डार्ट अलग हो जाता है। ऐप्स के बीच स्विच करते समय, या स्क्रीन को चालू करते समय आप Dart Observatory से कनेक्ट करके परीक्षण कर सकते हैं। वेधशाला एक सक्रिय Flutter ऐप अलग चलाना दिखाएगा। लेकिन बैक बटन दबाते समय, वेधशाला कोई रनिंग पृथक नहीं दिखाती है। एंड्रॉइड 6.x चलाने वाले गैलेक्सी नोट 5 पर और एंड्रॉइड 4.4.x चलाने वाला नेक्सस 4 पर व्यवहार की पुष्टि हुई।

स्पंदन एप्लिकेशन जीवन चक्र और Android जीवन चक्र स्पंदन विजेट परत के लिए, केवल रुका हुआ है और फिर से शुरू राज्यों संपर्क में हैं। नष्ट एक Android स्पंदन अनुप्रयोग के लिए Android Activity द्वारा नियंत्रित किया जाता: डार्ट वी एम के बाद से

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

के लिए एक स्पंदन एप्लिकेशन गतिविधि के अंदर चल रहा है, वीएम हर बार गतिविधि को नष्ट कर दिया जाता है रोक दिया जाएगा।

स्पंदन इंजन कोड तर्क
यह सीधे आपके प्रश्न का उत्तर नहीं है, लेकिन आपको इस बारे में स्पंदन इंजन Android के लिए राज्य में परिवर्तन संभालती है कुछ और अधिक विस्तृत पृष्ठभूमि जानकारी दे देंगे।
स्पंदन इंजन कोड के माध्यम से देख रहे हैं यह स्पष्ट हो जाता है कि एनीमेशन पाश रुका हुआ है जब FlutterActivity एंड्रॉयड Activity#onPause घटना प्राप्त करता है। जब आवेदन में चला जाता है रुका हुआ राज्य, source comment here निम्न होता है के अनुसार:।

"एप्लिकेशन को वर्तमान में उपयोगकर्ता को दिखाई नहीं देता जब अनुप्रयोग इस स्थिति में है, इंजन [onBeginFrame फोन नहीं होगा ] वापस कॉल करें।"

मेरी घड़ी परीक्षण के आधार पर यूआई प्रतिपादन रुक, जो समझ में आता है के साथ भी काम करने के लिए जारी है। यह WidgetsBindingObserver जब गतिविधि को नष्ट कर दिया जाता है का उपयोग कर विजेट परत में एक घटना भेजने के लिए अच्छा होगा, इसलिए डेवलपर्स जब तक गतिविधि फिर से शुरू है स्पंदन एप्लिकेशन के राज्य स्टोर करने के लिए सुनिश्चित करें।

4

कैसे अपने विशिष्ट टाइमर मामले को लागू करने के लिए वास्तव में पृष्ठभूमि कोड से कोई लेना देना नहीं है के प्रश्न का उत्तर देना। पृष्ठभूमि में कुल मिलाकर चल रहा कोड मोबाइल ऑपरेटिंग सिस्टम पर कुछ हतोत्साहित है।

उदाहरण के लिए, आईओएस प्रलेखन अधिक विस्तार यहाँ पृष्ठभूमि कोड की चर्चा: 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.io/platform-services

हम इस तरह की सेवा एकीकरण को प्रकाशित/साझा करने के लिए एक सिस्टम पर काम कर रहे हैं ताकि एक बार एक पर्स इस एकीकरण को लिखने पर (आपके ऐप के भविष्य के निष्पादन को शेड्यूल करने के लिए कहें) हर कोई लाभ उठा सकता है।

अलग से, के अधिक सामान्य प्रश्न, "अभी तक नहीं" है (एक FlutterView स्क्रीन पर सक्रिय बिना) "यह संभव पृष्ठभूमि डार्ट कोड को चलाने के लिए है।"हम फ़ाइल पर एक बग है: जब अपने अनुप्रयोग, एक सूचना प्राप्त होता है सामने के लिए अपने app लाए बिना कुछ डार्ट कोड का उपयोग कर इसे संसाधित करने चाहता है https://github.com/flutter/flutter/issues/3671

यूज-केस वापस जमीन कोड निष्पादन उस तरह का ड्राइविंग है। यदि आपके पास पृष्ठभूमि कोड के लिए अन्य उपयोग के मामले हैं, तो आप हमें इसके बारे में जानना चाहते हैं, उस बग पर टिप्पणियों का स्वागत है!

संबंधित मुद्दे