Flutter: Status instans tidak disimpan saat aplikasi dimatikan oleh OS

Dibuat pada 12 Nov 2016  ·  139Komentar  ·  Sumber: flutter/flutter

Apa itu keadaan instan, dan mengapa itu ada

Di Android, suatu Aktivitas dapat dimatikan kapan saja oleh sistem. Ini biasanya terjadi saat Android membutuhkan memori saat Aktivitas Anda tidak berada di latar depan, atau karena perubahan konfigurasi yang tidak ditangani, seperti perubahan lokal.
Untuk menghindari pengguna harus memulai ulang apa yang dia lakukan dari awal saat Android mematikan Aktivitas, sistem memanggil onSaveInstanceState(…) saat Aktivitas dijeda, tempat aplikasi seharusnya menyimpan datanya di Bundle , dan meneruskan bundel yang disimpan di onCreate(…) dan onRestoreInstanceState(…) saat tugas dilanjutkan jika aktivitas telah dihentikan oleh sistem.

Masalah tentang itu di flutter

Di aplikasi flutter yang saya coba (Galeri Flutter, dan proyek dasar dengan penghitung ketuk FAB), jika saya membuka cukup banyak aplikasi untuk membuat Android mematikan Aktivitas aplikasi flutter, semua status hilang ketika saya kembali ke aktivitas flutter (sementara tidak harus menghapus tugas dari terbaru).

Langkah-langkah untuk Reproduksi

  1. Instal Galeri Flutter
  2. Buka pengaturan perangkat, dan di opsi pengembang, aktifkan "Jangan pertahankan aktivitas". (lihat tangkapan layar)
    dev_option
    Ini akan memungkinkan untuk mensimulasikan ketika Android mematikan Aktivitas karena kekurangan memori dan Anda tidak berada di latar depan.
  3. Buka aplikasi Galeri Flutter dan pergi ke mana pun selain layar utama.
  4. Buka peluncur dengan menekan tombol beranda perangkat.
  5. Tekan tombol ikhtisar dan kembali ke Galeri Flutter. Inilah bugnya.

Apa yang diharapkan: Aplikasi ini dalam keadaan yang sama dengan yang kami tinggalkan, dengan UI yang tidak tersentuh.
Apa yang terjadi: Aktivitas dimulai kembali dari awal, kehilangan semua status UI, bahkan formulir yang sangat panjang.

P2 annoyance crowd routes framework new feature

Komentar yang paling membantu

Pembaruan cepat: Saat ini saya sedang aktif mencari solusi untuk masalah ini.

Semua 139 komentar

@eseidelGoogle Itu benar. Dalam format apa status aktivitas flutter dapat disimpan, jika bisa sama sekali pada versi saat ini?

Saat ini kami tidak melakukan apa pun untuk menyelamatkan apa pun.

Kerangka kerja itu sendiri memiliki sedikit status yang layak disimpan -- semuanya berupa animasi dan hal-hal seperti itu -- jadi mungkin kita selalu menyerahkan ini kepada aplikasi untuk dilakukan. Kita mungkin harus mengeksposnya di level Dart. (Saat ini hanya diekspos di level Java.)

@Hixie Di Android, semua Tampilan kerangka kerja memiliki

Di Flutter, hanya ada sedikit status kerangka kerja yang harus disimpan. Misalnya, status tombol yang diaktifkan bukanlah status, melainkan input yang disediakan oleh aplikasi. Riwayat rute saat ini disimpan dalam kerangka kerja, tetapi tidak disimpan dalam keadaan yang dapat dibangun kembali oleh kerangka kerja (karena itu semua contoh objek yang disediakan oleh kode aplikasi). Posisi gulir adalah tentang satu-satunya hal yang benar-benar dapat kami simpan (dan kami menyimpannya di toko saat ini, hanya saja bukan yang bertahan dari aplikasi).

Tapi bagaimanapun juga kita harus melakukan yang lebih baik dari hari ini.

Sebagai Pengembang Android yang berpengalaman, saya ingin mengambil bagian dalam percakapan dengan pengembang iOS juga ketika itu akan dibahas sehingga flutter bisa menjadi kerangka kerja yang sempurna untuk membangun aplikasi iOS dan Android.
Saya pikir kemampuan ketekunan kerangka kerja mungkin penting untuk menyimpan data, preferensi, data cache, dan status dengan cara yang paling ramah pengembang.

Whoa saya tidak menyadari hal ini terjadi? Ini sebenarnya penting, dan perlu disebutkan bahwa pada perangkat dengan memori lebih sedikit, prosesnya dapat dihentikan dengan mudah!

Bagaimana dengan menyimpan PageStore (atau terkait) sebagai aliran byte melalui serialization.dart di onSaveInstanceState ?

@Takhion Bisakah Anda menautkan "PageStore" yang Anda sebutkan? Saya tertarik untuk mencoba mengatasi masalah ini karena tampaknya tidak akan segera diperbaiki (31 Desember 2029 IMHO sedikit jauh)

@LouisCAD yakin ada di sini: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_storage.dart

Saya pikir Anda harus menyimpan lebih dari itu: setidaknya rute saat ini dan mungkin beberapa negara bagian?
Bagaimana menurutmu @Hixie?

Saat ini kami tidak melakukan apa pun untuk membuatnya mudah. Kami belum mempelajari masalah ini secara detail. Untuk saat ini, saya sarankan untuk menyimpan informasi yang ingin Anda simpan secara manual, dan menerapkannya lagi saat aplikasi dipulihkan.

Apakah kami mengekspos peristiwa siklus hidup yang diperlukan ("Anda akan dibunuh!", "Selamat, Anda sekarang dipulihkan") ke dalam kode Dart, sehingga pengembang dapat menangani status yang bertahan? Itu sepertinya kemampuan yang cukup untuk membuka kunci pengembang untuk mengeksplorasi solusi. Pikiran?

@sethladd Hanya mengeksposnya ke pengembang tidak cukup IMHO. Flutter juga perlu menyimpan status UI, tanpa pengembang harus melakukannya secara manual berulang kali untuk setiap aplikasi dengan kode boilerplate verbose yang kotor dan sulit dipelihara.

@LouisCAD terima kasih atas umpan baliknya. Saya berpikir dalam langkah... apa langkah pertama? Apakah kita sudah membuka peristiwa siklus hidup?

@sethladd yang bisa saya temukan hanyalah this , yang tidak mengekspos panggilan balik apa pun untuk menyimpan/memulihkan status instance

@Hixie yang Anda sebutkan di # 3427 bahwa kami memiliki kait untuk siklus hidup. Apakah maksud Anda https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html ?

Apakah Flutter menggunakan beberapa Aktivitas secara default? (misalnya jika Anda pergi ke layar baru).

Proses kematian cukup umum, pengguna menekan tombol Home dan meluncurkan beberapa aplikasi intensif memori/CPU lainnya dan aplikasi Anda akan dimatikan di latar belakang (atau aplikasi Anda terlalu lama berada di latar belakang). Ini adalah bagian integral dari OS Android - setiap layar (Aktivitas) harus dapat bertahan dan memulihkan statusnya dan prosesnya dapat dihentikan kapan saja setelah onStop().

Android akan membuat ulang backstack Aktivitas jika pengguna kembali ke aplikasi yang dimatikan, Aktivitas teratas dibuat terlebih dahulu dan kemudian aktivitas di backstack dibuat ulang sesuai permintaan jika Anda kembali ke riwayat backstack. Ini bisa menjadi masalah yang lebih besar jika Flutter menggunakan beberapa Aktivitas (tidak yakin apakah itu benar).

Ini berarti bahwa jika Anda tidak menerapkan penyimpanan status maka sistem akan membuat ulang Aktivitas tetapi yang lainnya hilang (proses dimatikan), sehingga menyebabkan inkonsistensi dan crash.

Saya menulis artikel tentang kematian proses di Android https://medium.com/inloop/android-process-kill-and-the-big-implications-for-your-app-1ecbed4921cb

Juga tidak ada cara (bersih) untuk mencegah Android mematikan aplikasi Anda setelah melewati status onStop() (setelah mengklik Beranda atau jika aplikasi bukan aplikasi latar depan saat ini). Jadi entah bagaimana Anda harus menghadapinya. Widget Android default menyimpan statusnya (misalnya teks yang dimasukkan dalam EditText) secara otomatis ke dalam instance Bundle. Aktivitas Anda akan memberi tahu Anda bahwa perlu untuk menyimpan status Anda dengan memanggil onSaveInstanceState(Bundle) . Jadi mungkin Flutter harus dapat meneruskan panggilan balik onSaveInstanceState ini dari Aktivitas ke "layar" Anda. Anda akan mendapatkan Bundle dengan status tersimpan kembali dalam panggilan balik siklus hidup onCreate(Bundle savedInstanceState) atau onRestoreInstanceState(Bundle savedInstanceState) dalam aktivitas Anda.

Jadi untuk rekap - Flutter mungkin bisa meneruskan callback onSaveInstanceState() dan onRestoreInstanceState() ke developer. Idealnya Anda juga akan membungkus objek Android Bundle menjadi sesuatu yang juga dapat digunakan di Flutter. Langkah selanjutnya adalah semua widget Flutter di dalam layar juga akan diberi tahu tentang callback ini dan menggunakannya untuk mempertahankan statusnya saat ini.
OS Android kemudian akan mengambil Bundle dan benar-benar menyimpannya di disk sehingga tidak hilang jika prosesnya dimatikan dan dapat di-deserialized lagi.

Semoga beruntung dengan itu :-). Harap berhati-hati dan jangan terlalu banyak memperkenalkan status/siklus hidup Android ini ke Flutter.

Saya tidak yakin bagaimana cara kerjanya di iOS - tetapi saya pikir ada sesuatu yang serupa tetapi tidak "perlu" untuk menggunakannya (?).

Panggilan balik siklus hidup akan baik-baik saja bagi saya. Saya hanya akan menyimpan/memuat status redux serial.

Terima kasih untuk semua umpan balik! @Takhion juga menawarkan bantuan di sini. Mungkin desain API dan perpustakaan adalah awal yang baik? Jika perpustakaan itu berfungsi, kita dapat melihat untuk mengintegrasikan lebih formal. Juga, perpustakaan akan membantu mengidentifikasi apa yang perlu kita lakukan pada mesin tingkat rendah (jika ada). Pada dasarnya: apa proposal API konkret?

Apakah Flutter menggunakan beberapa Aktivitas secara default? (misalnya jika Anda pergi ke layar baru)

@DanielNovak Flutter menggunakan sistem "perutean" itu sendiri dan secara default terintegrasi dengan Android melalui satu Tampilan dalam satu Aktivitas. Anda dapat memiliki aplikasi Android/Flutter hybrid dan dengan demikian berpotensi memiliki beberapa Aktivitas dan Tampilan Flutter, tetapi dalam hal ini Anda dapat dengan mudah menyimpan/mengembalikan status instance melalui Android secara langsung.

Saya hanya akan menyimpan/memuat status redux serial

@zoechi Anda mungkin tidak ingin melakukan itu karena bundel status instance yang disimpan harus melalui IPC dengan batas keras 1MB untuk seluruh status aplikasi Android Anda. Status instance, menurut definisi, seharusnya hanya berupa potongan data yang memungkinkan Anda membuat ulang kondisi yang sama dari aktivitas apa pun yang sedang berlangsung yang dilakukan pengguna, jadi: input teks, posisi gulir, halaman saat ini, dll. dipertahankan pada disk atau diturunkan dari status lain.

Flutter mengekspos peristiwa siklus hidup Android onPause . Peristiwa siklus hidup Android onDestroy() tidak diekspos. Saya kira pendekatan yang tepat adalah menghubungkan ke acara onPause() untuk menyimpan status terkait instance.
Agar kompatibel dengan onSaveInstanceState() dan onRestoreInstanceState() Android , mungkin masuk akal untuk menambahkan metode serupa ke widget Flutter.
@sethladd @LouisCAD Apakah ada yang membuat proposal desain?

@raju-bitter onPause() tidak optimal, lebih baik untuk menghubungkan ke onSaveInstanceState(). Berikut adalah javadoc dari onSaveInstanceState yang menyebutkan bahwa onPause() dipanggil lebih teratur daripada onSaveInstanceState (Anda akan memicu penghematan status lebih sering daripada yang diperlukan atau ketika tidak diperlukan sama sekali):

Jangan bingung metode ini dengan callback siklus hidup aktivitas seperti onPause(), yang selalu dipanggil saat aktivitas ditempatkan di latar belakang atau dalam perjalanan menuju penghancuran, atau onStop() yang dipanggil sebelum penghancuran. Salah satu contoh saat onPause() dan onStop() dipanggil dan bukan metode ini adalah saat pengguna menavigasi kembali dari aktivitas B ke aktivitas A: tidak perlu memanggil onSaveInstanceState(Bundle) pada B karena instance tertentu tidak akan pernah dipulihkan , sehingga sistem tidak akan memanggilnya. Contoh saat onPause() dipanggil dan bukan onSaveInstanceState(Bundle) adalah saat aktivitas B diluncurkan di depan aktivitas A: sistem dapat menghindari panggilan onSaveInstanceState(Bundle) pada aktivitas A jika tidak dimatikan selama masa hidup B sejak keadaan antarmuka pengguna A akan tetap utuh.

@Takhion

bundel status instance harus melalui IPC dengan batas keras 1MB untuk seluruh status aplikasi Android Anda

Itu bukan masalah saya.
Saya hanya perlu tahu kapan harus bertahan/mengembalikan, bertahan di disk tidak masalah bagi saya.

Menyebarkan onSaveInstanceState / onRestoreInstanceState adalah bagian yang mudah: Flutter terkandung dalam Tampilan dan memiliki akses yang tepat ke panggilan balik tersebut. Apa yang saya coba lakukan adalah membuat proposal desain dan prototipe untuk secara otomatis membuat serial objek yang terkandung dalam "repositori" tertentu, sehingga menggunakannya tidak akan menyakitkan (sebagai lawan harus melakukan semuanya secara manual).

@Takhion kamu adalah angin di bawah sayapku. Tidak sabar untuk melihat proposalnya!

OK jadi saya sedang membangun PR untuk ini dan saya menemui hambatan: [ View.onSaveInstanceState ] (atau [ Activity.onSaveInstanceState ], dalam hal ini) perlu menyediakan status instance yang disimpan secara sinkron , dan dari pengalaman saya (dan [komentar] ini) sepertinya pesan Host/platform hanya bisa async . Saya menduga ini karena mereka menggunakan antrian pesan Android.

@sethladd @Hixie @eseidelGoogle @jason-simmons tolong beri tahu saya ada cara sederhana untuk membuat satu panggilan sinkron/pemblokiran dari Android ke Flutter yang tidak melibatkan kode C/C++ ad-hoc, karena [ dart:jni telah pensiun]?

Cc @mravn-google

Sifat asinkron dari pesan platform berasal dari fakta bahwa UI Dart dijalankan pada utas yang berbeda dari yang digunakan untuk callback platform Android. Namun Anda selalu dapat mengubah panggilan API asinkron menjadi panggilan sinkron dengan menggunakan latch:

final MethodChannel channel = new MethodChannel(messenger, someName);
final CountDownLatch latch = new CountDownLatch(1);
channel.invokeMethod("someMethod", someArguments, new MethodChannel.Result() {
  <strong i="6">@Override</strong>
  public void success(Object result) {
    // handle successful invocation
    latch.countDown();
  }
  <strong i="7">@Override</strong>
  public void error(String errorCode, String errorMessage, Object errorDetails) {
    // handle failed invocation
    latch.countDown();
  }
  <strong i="8">@Override</strong>
  public void notImplemented() {
    // handle invocation of unimplemented method
    latch.countDown();
  }
});
try {
  latch.await();
} catch (InterruptedException e) {
  // handle interruption
}

Anda dapat mendeklarasikan variabel final AtomicReference<SomeType> (atau yang serupa) di sebelah kait, jika Anda perlu menyampaikan nilai dari lokasi // handle xxx ke kode yang berjalan setelah latch.await() kembali.

terima kasih @mravn-google !

@mravn-google itulah pendekatan pertama yang saya coba, tetapi utas utama di Android hanya hang pada panggilan await() selamanya :(

@Takhion Benar, tentu saja.

Anda selalu dapat mengubah panggilan API asinkron menjadi panggilan sinkron dengan menggunakan latch

... kecuali respons async dikirim ke utas yang sudah Anda gunakan :-/

Maka, kita tampaknya membutuhkan semacam jabat tangan sinkronisasi untuk onSaveInstanceState . Apakah kita mengetahui panggilan balik serupa lainnya yang memerlukan jawaban sinkronisasi yang melibatkan perhitungan sisi Dart? Itu akan membutuhkan tujuan umum, pertukaran pesan platform sinkron. Jika tidak, kita dapat melakukan sesuatu yang khusus untuk onSaveInstanceState .

@mravn-google sayangnya Android adalah _full_ dari "jendela peluang sinkron" yang jahat ini, di mana semuanya harus terjadi sebelum kembali dari pemanggilan metode! Di atas kepala saya: banyak Activity peristiwa siklus hidup, metode dalam Broadcast Receiver , SyncAdapter , dll. Sejujurnya saya terkejut bahwa ini bukan masalah yang lebih besar sebelumnya!

Menurut pendapat saya, jika kita ingin Flutter benar-benar mencapai paritas fitur dengan apa yang mungkin dilakukan melalui API "asli", maka komunikasi pesan yang sinkron adalah suatu keharusan.

@mit-mit maksud saya SystemChannels.lifecycle dan apa pun yang menggunakan itu, ya.

@sethladd saya pikir begitu.

@mravn-google terima kasih! Saya telah menutup #7560 untuk yang ini, hanya untuk menghilangkan penipuan dan memusatkan percakapan.

Saya condong untuk menambahkan mitra sinkron ke MethodChannel untuk jenis komunikasi platform-ke-Dart ini.

@mravn-google bagaimana kabarnya? Bisa saya bantu?

@Takhion Terima kasih! Harus ada segera. Saya sedang mengerjakan saluran metode sinkron, dan saya berharap memiliki PR dalam satu atau dua hari lagi. Setelah itu saya akan meminta ulasan Anda. Saya ingin menghubungkan saluran baru ke onSaveInstanceState secepatnya untuk menguji apakah semuanya berfungsi sebagaimana dimaksud dalam skenario itu. Jika Anda sudah memiliki contoh aplikasi yang dapat Anda sumbangkan, itu akan luar biasa.

@mravn-google luar biasa! Saya tidak memiliki aplikasi karena saya memodifikasi kerangka secara langsung, tetapi saya pasti dapat memberikan contoh. Btw saya menggunakan BinaryMessages secara langsung karena tidak perlu codec di sisi Android (hanya byte yang diedarkan), apakah boleh?

kalian keren.

@Takhion Penggunaan langsung BinaryMessages baik-baik saja, jika Anda tidak memerlukan codec. Saya akan menambahkan

void setSynchronousMessageHandler(String channel, ByteData handler(ByteData message))

ke kelas itu.

@Takhion Pukul itu. Kami tidak bisa begitu saja melakukan panggilan sinkron ke Dart VM untuk memanggil handler . Jadi BinaryMessages tidak akan berubah. Sebaliknya, semuanya akan asinkron dari sudut pandang kode Dart, seperti biasa. Perubahan akan dilakukan pada BinaryMessenger dan implementasinya di sisi platform. saya akan menambahkan

ByteBuffer sendSynchronous(String channel, ByteBuffer message);

Kode tampilan platform yang mendukung FlutterView akan mengimplementasikannya dengan mengirimkan pesan platform async dan kemudian menunggu di gerendel. Objek respons yang disertakan dengan pesan platform itu tidak akan memposting penutupan penanganan balasan ke utas platform (yang sekarang diblokir), tetapi sebaliknya akan menyimpan balasan dan melepaskan kaitnya.

@mravn-google berfungsi untuk saya, karena itu tidak menghentikan kami mengirim kembali data dari sisi Dart, meskipun itu panggilan asinkron :+1:
Saya kira tidak akan ada pengoptimalan jika seseorang menggunakan Future.value(...) dalam respons Dart async?

Saya melihat Anda kebanyakan berbicara tentang platform Android di sini. Namun, sebenarnya, iOS juga memiliki mekanisme yang agak mirip untuk menyimpan/mengembalikan status UI saat aplikasi dimatikan karena suatu alasan. Misalnya, ketika pengguna beralih ke aplikasi lain, aplikasi saat ini ditangguhkan dan bahkan dapat dimatikan karena keterbatasan memori. Anda dapat menemukan info lebih lanjut tentang proses ini di dokumen resmi .

Pengembang iOS mengharapkan prosedur pelestarian/pemulihan ini hampir otomatis. Saya kira, jika tujuan Anda adalah membuat kerangka kerja lintas platform yang sesungguhnya, Anda harus mempertimbangkan hal ini.

Anda akan berpikir sesuatu yang bermaksud menargetkan Android sebagai platform setidaknya mencoba untuk mematuhi dasar-dasar kontrak Aktivitas....

@Zhuinden jangan mengeluh tentang hal-hal yang masih dalam versi beta? :)

Api sinkron lainnya adalah onLowMemory dan onTrimMemory dan onConfigurationChanged tidak tahu apakah IOS juga memiliki api atau mekanisme yang serupa

Kami mungkin tidak ingin mereproduksi mekanisme penyimpanan&pemulihan android yang cacat di Flutter: Anda tidak dapat menyimpan seluruh status dalam bundel karena batas ukuran transaksi.

Di Android, Anda akhirnya memiliki beberapa logika penyimpanan/pemulihan status:

  • SaveInstanceState pengembang, tetapi Anda tidak dapat menyimpan objek besar di dalamnya, seperti daftar elemen
  • database, tempat Anda dapat menyimpan objek besar
  • sebagian besar widget juga mengelola statusnya sendiri
  • tumpukan aktivitas/fragmen

Sangat mudah bagi pengembang untuk menerapkan logika yang berantakan dan bermasalah. Dalam pengalaman saya sendiri, sangat jarang melihat penyimpanan & pemulihan negara dilakukan dengan benar.

Saat bergetar, status pengembang adalah satu-satunya sumber kebenaran.
Pengembang harus mempertahankan status ini ke dalam disk saat berubah, tanpa melalui mekanisme platform asli yang terbatas.

Tentu, Anda tidak akan dapat dengan mudah menyimpan posisi gulir, tetapi siapa yang peduli.
Sebagian besar waktu, UX yang baik untuk pemulihan berarti bahwa pengguna harus melihat layar yang sama dari sebelumnya. Anda sudah dapat melakukannya dengan Flutter hari ini.

Intinya: ada ruang untuk perbaikan di sisi Flutter untuk membuat penghematan/pemulihan status menjadi lebih sedikit boilerplate (untuk hal-hal seperti Navigator). Tapi saya tidak berpikir itu harus menyediakan pengait ke platform save/restore state.

Tentu, Anda tidak akan dapat dengan mudah menyimpan posisi gulir, tetapi siapa yang peduli.

Setiap orang? Saya akan sangat frustrasi jika aplikasi mengatur ulang daftarnya setiap kali saya secara tidak sengaja memicu perubahan orientasi. :P

@Saketme FWIW, Flutter tidak mengatur ulang posisi gulir pada perubahan orientasi.

Menggunakan aplikasi Flutter dengan sedikit multitasking seharusnya menyenangkan di perangkat Android Go! Kemajuan hilang setiap kali 1GB RAM atau kurang berada di bawah tekanan

@Saketme : seperti yang dikatakan @mravn-google, Flutter tidak terikat dengan perubahan konfigurasi seperti Aktivitas Android.
Satu-satunya manajemen negara yang diperlukan adalah ketika aplikasi dimatikan oleh OS saat berada di latar belakang.

@LouisCAD Tentu menyimpan & memulihkan keadaan itu penting.
Pengembang Flutter sudah dapat mengimplementasikan sendiri sebagian besar logika ini, dan Flutter mungkin harus menyediakan cara untuk membuatnya lebih sedikit boilerplate. Namun mekanisme yang ditawarkan oleh Android cacat dan kami dapat mengambil kesempatan dengan Flutter untuk merancang sesuatu yang lebih baik, daripada meretas Aktivitas onSaveInstanceState / onRestoreInstanceState .

Bagaimana status masalah ini saat ini?

Pertimbangkan kasus aplikasi obrolan, tempat saya mengetik pesan panjang di TextField .
Tiba-tiba saya menerima panggilan telepon dan setelah beberapa waktu, OS mematikan aplikasi obrolan karena memori rendah.
Setelah panggilan telepon, saya kembali ke aplikasi obrolan. Akankah Flutter secara otomatis bertahan dan membuat ulang pesan yang saya ketik? atau Aplikasi harus bertahan dan membuatnya ulang secara manual?

Selain posisi dan rute gulir, apa lagi "status" yang biasanya tidak dikelola oleh pengembang?

@nsreenath Aplikasi obrolan apa pun harus

Saya benar-benar tidur dengan ini dan saya menyadari bahwa sebenarnya cukup mudah bagi pengembang Flutter untuk mendeteksi apakah aplikasi kembali dari proses kematian atau benar-benar contoh baru.

Sangat sederhana, selama mereka memeriksa savedInstanceState == null (dengan asumsi mereka memasukkan setidaknya 1 item ke dalam onSaveInstanceState(bundle) ). Jika flag boolean statis isFirstInit == false dan savedInstanceState != null , maka itu setelah proses mati.

Kemudian mereka dapat membuat segala jenis panggilan balik serialisasi/deserialisasi untuk keadaan kompleks yang dapat dihapus secara otomatis ketika mereka mendeteksi savedInstanceState == null . Dengan begitu mereka hanya perlu mempertahankan beberapa boolean acak, tetapi dapat mengelola sistem ketekunan mereka sendiri.

Sangat sederhana, selama mereka memeriksa saveInstanceState == null (dengan asumsi mereka menempatkan setidaknya 1 item di onSaveInstanceState(bundel)).

Tidak ada savedInstanceState dan onSaveInstanceState di iOS. Tapi aplikasi masih bisa dimatikan oleh OS.

Pertimbangkan alur kerja yang khas:

  • Seorang pengguna pergi ke layar tertentu di dalam aplikasi
  • Kemudian pengguna beralih ke aplikasi lain
  • Tiba-tiba OS yang bijaksana memutuskan untuk mematikan aplikasi sebelumnya untuk mempertahankan memori
  • Pengguna kembali ke aplikasi sebelumnya tetapi dia melihat layar beranda. Bukan layar yang terbuka saat dia memutuskan untuk mengganti aplikasi

Jadi, apa cara yang direkomendasikan saat ini untuk menyimpan riwayat layar di Flutter?

PS Tentu saja, ini tidak penting jika aplikasi Anda hanya memiliki satu layar :smirk:

@Zhuinden Saya gagal melihat banyak skenario di mana itu diperlukan.

a) Contoh obrolan:
Selalu simpan dan pulihkan konten sebelumnya yang ditulis di bidang teks yang tidak terkirim.

b) Contoh bentuk panjang:

  1. Selalu simpan konten
  2. Ketika pengguna kembali ke layar formulir, beri tahu pengguna bahwa ada versi draf yang disimpan tetapi tidak dikirimkan, menanyakan apakah dia ingin memulihkannya. Tidak ada bedanya jika pengguna keluar dari formulir secara manual (mungkin menanyakan apakah pengguna ingin menyimpan draf) atau jika aplikasi dimatikan saat berada di latar belakang.

Mungkin ada beberapa kasus di mana pengetahuan khusus tentang "layar baru segar" vs "layar baru setelah kematian proses" berguna, tetapi itu mungkin tidak begitu umum ketika Anda mendesain konten aplikasi agar selalu didukung oleh database.

@storix Anda harus secara manual mempertahankan rute saat ini dan memulihkannya saat aplikasi diluncurkan lagi. Terlalu banyak boilerplate untuk kasus penggunaan umum, saya setuju. Tapi masih bisa dilakukan.
Juga: Banyak pengembang Android akan terkejut dengan betapa sedikitnya aplikasi/pengembang iOS yang repot dengan pemulihan status. Sebagian besar pengembang yang saya kenal bahkan tidak menyadari bahwa itu adalah masalah. Kira itu kemewahan bekerja pada perangkat kelas atas saja.

@Zhuinden panggilan balik siklus hidup dari https://github.com/flutter/flutter/issues/6827#issuecomment -335160555 berfungsi untuk aplikasi obrolan saya. Saya hanya menyimpan beberapa status setiap kali aplikasi dialihkan ke latar belakang. Lihat juga https://docs.flutter.io/flutter/dart-ui/AppLifecycleState-class.html

Saya ingin mendesak tim Flutter untuk tidak mengekspos metode siklus hidup Android/iOS sebagai cara kanonik untuk mempertahankan/memulihkan status UI. Bahkan di aplikasi asli, mereka sulit untuk grok dan kode dengan benar, dan garis antara UI dan status aplikasi sering kabur, dengan mekanisme yang berbeda disediakan untuk masing-masing. Akan sangat disayangkan untuk menyebarluaskan semua API yang berbeda ini ke Flutter (terutama jika platform tambahan ditargetkan di masa mendatang).

Widget stateless Flutter telah _sudah_ memigrasikan tanggung jawab untuk status UI keluar dari widget dan ke dalam aplikasi, sehingga masalah mempertahankan/memulihkan status UI semakin ditutupi dengan mempertahankan/memulihkan status _app_ sebagai gantinya. Saya melihat ini sebagai hal yang baik, dibandingkan dengan memperlakukan UI dan status Aplikasi secara berbeda, seperti di iOS dan Android.

Saya perhatikan bahwa beberapa widget Flutter, seperti ScrollView dan TextField, menyediakan kelas "Controller" yang merangkum status dinamis widget. Dalam beberapa hal mereka bertindak sebagai objek "kenang-kenangan" -- snapshot status widget -- yang dapat diberikan kembali ke widget di lain waktu untuk memulihkan statusnya ke beberapa konfigurasi sebelumnya. Kedengarannya akrab. Jika "kenang-kenangan" ini dibuat mudah untuk disimpan/dipulihkan, maka aplikasi dapat mengambil tanggung jawab untuk mereka sebagai bagian dari mengelola statusnya sendiri. Ini akan mengakhiri dikotomi status aplikasi/UI, akan ikut serta, dan akan meniadakan kebutuhan kerangka kerja Flutter untuk menyediakan kait pelestarian status UI (lebih sedikit lebih banyak!)

Dengan kata lain, buatlah itu menjadi tanggung jawab pengembang, tetapi cobalah untuk membuatnya semudah mungkin. Jika seorang pengembang berpikir bahwa formulir yang setengah selesai cukup penting bagi Flutter untuk bertahan di seluruh pemanggilan, maka orang dapat berargumen bahwa itu mungkin cukup penting untuk dianggap sebagai status aplikasi. Saya menyadari bahwa mempertahankan status objek menimbulkan masalah versi, tetapi saya pikir ini adalah masalah yang lebih mudah dikelola.

Apakah ada kemajuan dalam hal ini? Saya pikir ada kebutuhan nyata untuk membedakan antara status proses 'jalankan pertama' dan 'dipulihkan'. Bagaimana lagi Anda dapat memprogram untuk menampilkan layar yang tepat ketika pengguna menavigasi kembali ke proses yang dimatikan?

@lukaspili Anda menulis: "Satu-satunya manajemen negara yang diperlukan adalah ketika aplikasi dimatikan oleh OS saat berada di latar belakang."

Okey, lalu bagaimana dengan router stack, juga simpan ke disk? Saya juga tidak t find proper solution to determine that application was restored. Every time when app goes to background I need to save router stack, app data, state to disc? What if I don menggunakan Redux?

Saya pikir sebagai langkah pertama, kita harus memberi plugin kemungkinan untuk menyimpan dan memulihkan status. Ini proposal saya: #22328

Apakah ada pembaruan tentang masalah ini?

Saya menghadapi masalah yang sama. Saya telah mendapatkan solusi untuk menyimpan input teks di sini melalui database realtime Firebase. Tetapi metode ini cukup kasar dan seringkali tidak memiliki opsi skalabilitas karena setiap pengguna akan diarahkan ke database yang sama. Oleh karena itu, data saya tersedia untuk semua orang yang memiliki akses ke akun gmail. Saya telah mencoba menggunakan Redis, tetapi meskipun saya dapat membuat daftar terpisah dalam database yang sama untuk pengguna lain, itu selalu dilakukan melalui metode POST. Itu berarti tampilan harus di-refresh setiap kali data baru disimpan. Yang berarti tidak ada visual waktu nyata. Sebuah kesalahan besar memang.

Ada masalah yang lebih besar di sini. Saya telah bolak-balik dengan @matthew-carroll dua bulan lalu dan saya cukup yakin ini sedang berlangsung.

Saya sudah mulai membuat perpustakaan yang menangani hal ini . Saya sebenarnya tidak akan merekomendasikan menggunakannya tetapi ini adalah satu ide dari implementasi Dart saja. #6225 membuat segalanya sedikit rumit.

Saya perhatikan bahwa aplikasi Google Ads, yang dilaporkan dibuat dengan Flutter, mempertahankan status navigasi setelah aktivitas dimulai ulang. Ini akan menjadi referensi yang bagus untuk mengetahui bagaimana penerapannya, bahkan jika itu adalah solusi.

@aletorrado Apakah aplikasi Google Ads mempertahankan status navigasi jika pengguna telah membuang aplikasi dari “pengelola tugas” Android/IOS dan kemudian meluncurkannya lagi ?

@gitspeak Tidak, itu hanya mempertahankan status jika aktivitas dimatikan secara otomatis oleh OS (mungkin menggunakan kait onSaveInstanceState).

@aletorrado Jadi ya itu mungkin satu-satunya cara...

Saya sebenarnya mencoba masuk ke aplikasi Google Ads tetapi Anda harus memiliki akun aktif untuk membuka layar apa pun di luar yang pertama: Saya tidak dapat menguji apakah itu benar-benar berfungsi atau tidak

Itu benar. Terjamin. Itu juga mempertahankan status lain seperti posisi gulir.

Untuk memperjelas beberapa kebingungan:

  • Ya, aplikasi Google Ads ditulis dengan Flutter.
  • Menjadi salah satu pengadopsi awal, mereka harus melakukan sesuatu yang tidak biasa untuk eksekusi latar belakang. Mereka membuat dan memelihara isolat utama mereka saat aplikasi dimulai (bukan aktivitas). Kemudian, mereka meneruskan isolat ini ke tampilan Flutter saat dibuat.
  • Apa yang Anda amati sebagai "pemeliharaan negara" adalah efek samping dari metodologi yang dijelaskan di atas. Saat Anda mematikan aktivitas di Google Ads, "UI" Flutter tidak mati. Itu disimpan dalam memori terkait dengan konteks aplikasi. Saat aktivitas dimulai ulang, ia memuat isolat dan biola yang sama, Anda mendapatkan kembali UI Anda.

Kami tidak merekomendasikan pendekatan ini untuk memecahkan masalah ini karena tidak menyelesaikan masalah sama sekali. Jika proses aplikasi mati, seluruh status masih hilang. Anda dapat mencoba ini dengan Google Ads dengan membuka dan menghentikan proses dari Setelan > Aplikasi. UI akan hilang.

Mungkin ada cara untuk menyelamatkan status isolasi secara lokal tetapi perlu penyelidikan lebih lanjut karena kami tidak yakin seberapa layak/amannya.

Untuk saat ini, solusi yang dijelaskan di atas adalah yang terbaik: Hubungkan ke metode asli dan simpan status aplikasi Anda secara selektif sehingga Anda dapat memulihkannya nanti. Kerangka kerja yang didukung solusi otomatis pada iOS dan Android masih jauh.

Anda dapat mencoba ini dengan Google Ads dengan membuka dan menghentikan proses dari Setelan > Aplikasi. UI akan hilang.

tidak itu benar-benar ok. Menghentikan Paksa aplikasi harus kehilangan status.

Dihentikan oleh kondisi memori rendah seharusnya tidak.

Either way, jika mereka menyimpan status dalam aplikasi sebagai memori tunggal tanpa mempertahankan apa pun ke disk/bundel, maka itu AKAN hilang.

Sayang sekali saya tidak dapat menguji aplikasi tanpa akun, karena saya tidak akan membuat akun Google Ads hanya untuk mengotak-atik aplikasi itu sendiri. :thinking_face:

Hubungkan ke metode asli dan simpan status aplikasi Anda secara selektif sehingga Anda dapat memulihkannya nanti.

Memang solusi terbaik adalah mempertahankan status aktif ke disk, dan mendeteksi kematian proses di sisi asli Android; jika Anda mendeteksi kematian proses, muat ulang dari disk, jika tidak, hapus dan mulai ulang dari awal.

Saya tidak cukup tahu tentang navigator Flutter untuk memberi tahu Anda apakah Anda dapat merekonstruksi riwayat navigasi dengan semacam metode setHistory atau setBackstack seperti yang Anda lakukan dengan backstack atau Konduktor atau semacamnya.

Aktivitas/Fragmen menangani hal ini secara internal dengan ActivityRecord/FragmentRecord, sehingga tidak disebutkan lebih dari itu.

Terima kasih atas semua wawasan itu @mehmetf. Sangat menyedihkan mendengar bahwa dukungan untuk menyimpan/memulihkan keadaan masih jauh dari implementasi.

Mungkin flutter_redux dapat membantu membuat serial beberapa status aplikasi.

Nah, jika Anda menyimpan gumpalan toko Redux ke disk. Berhati-hatilah dengan masalah "dialog pemuatan tak terbatas setelah kematian proses". :thinking_face:

Untuk memperjelas beberapa kebingungan:

Sebenarnya saya agak lebih bingung..

Menjadi salah satu pengadopsi awal, mereka harus melakukan sesuatu yang tidak biasa untuk eksekusi latar belakang. Mereka membuat dan memelihara isolat utamanya saat aplikasi dimulai (bukan aktivitas). Kemudian, mereka meneruskan isolat ini ke tampilan Flutter saat dibuat.

Bagaimana hal itu berhubungan dengan masalah yang dihadapi? Apakah isolat Dart berjalan dalam proses terpisah?

Apa yang Anda amati sebagai "pemeliharaan negara" adalah efek samping dari metodologi yang dijelaskan di atas. Saat Anda mematikan aktivitas di Google Ads, "UI" Flutter tidak mati. Itu disimpan dalam memori terkait dengan konteks aplikasi. Saat aktivitas dimulai ulang, ia memuat isolat dan biola yang sama, Anda mendapatkan kembali UI Anda.

Pemahaman saya adalah bahwa @aletorrado mengamati "penjagaan status" setelah proses dihentikan, mungkin ketika aplikasi dipindahkan ke latar belakang (seperti menekan tombol beranda).

FYI: ketika "Aktivitas Mati" pada saat runtime akan SELALU diikuti dengan menghentikan proses.

Kami tidak merekomendasikan pendekatan ini untuk memecahkan masalah ini karena tidak menyelesaikan masalah sama sekali. Jika proses aplikasi mati, seluruh status masih hilang. Anda dapat mencoba ini dengan Google Ads dengan membuka dan menghentikan proses dari Setelan > Aplikasi. UI akan hilang.

Seperti yang disebutkan @Zhuinden, ini bukan skenario "penyimpanan status" yang valid, sehingga membuang aplikasi dari "pengelola tugas"

Mungkin ada cara untuk menyelamatkan status isolasi secara lokal tetapi perlu penyelidikan lebih lanjut karena kami tidak yakin seberapa layak/amannya.

Itu bukan cara untuk melakukannya. Untuk Android, Anda harus terhubung ke mekanisme onSaveInstanceState karena dibuat secara eksplisit untuk menyimpan status Aktivitas untuk skenario di mana "penyimpanan status" diperlukan setelah penghentian proses. Saya tidak terbiasa dengan siklus hidup aplikasi iOS.

Untuk saat ini, solusi yang dijelaskan di atas adalah yang terbaik: Hubungkan ke metode asli dan simpan status aplikasi Anda secara selektif sehingga Anda dapat memulihkannya nanti. Kerangka kerja yang didukung solusi otomatis pada iOS dan Android masih jauh.

Tidak ada solusi yang dijelaskan selain mengakui fasilitas Android yang sesuai untuk mempertahankan status untuk kasus-kasus di mana run-time memutuskan untuk menghentikan proses.

Tidak ada solusi yang dijelaskan selain mengakui fasilitas Android yang sesuai untuk mempertahankan status untuk kasus-kasus di mana run-time memutuskan untuk menghentikan proses.

Saya agak menjelaskan apa yang dapat kami lakukan tetapi saya tidak cukup tahu tentang Flutter untuk memberi tahu Anda apakah itu mendukung apa yang diperlukan untuk itu (kemampuan untuk mengatur riwayat navigasi arbitrer pada waktu tertentu )

@Zhuinden ada "

dan hanya untuk menegaskan kembali posisi saya: Tugas/Aktivitas Android, Layanan, Siklus Hidup Aplikasi (termasuk onSaveInstanceState) semuanya adalah bagian dari Kontrak Pemrograman Proses Aplikasi dan karena kode Flutter hidup dalam Proses Aplikasi Android, tidak bijaksana bagi Flutter untuk mengabaikan batasan ini.

Pengembang Android yang terhormat,

terima kasih atas semangat Anda dalam memberikan pengalaman pengguna terbaik kepada pengguna Anda dengan menyimpan status tidak hanya di seluruh perubahan konfigurasi tetapi juga selama proses mati. Anda mungkin telah membaca dalam 200 komentar di atas bahwa Flutter sedang berjuang untuk menyelamatkan status aplikasi. Saya ingin memberi tahu Anda mengapa dan bagaimana menyelesaikannya .

Mengapa saya tidak bisa menggunakan onSavedInstanceState ?

onSavedInstanceState mengharuskan pengembang untuk menyimpan status aplikasi secara sinkron. Anda harus segera kembali. Tetapi komunikasi ke Flutter tidak sinkron secara default ( Future ). Oleh karena itu, Anda tidak dapat meminta status tersimpan dari aplikasi Flutter saat Android memintanya.
_Tetapi Anda dapat mengembalikan status tersimpan jika Anda telah membuatnya sebelumnya, bukan?_

Masalahnya bukan onSavedInstanceState tidak dapat diakses, masalahnya adalah waktunya. Saat Android meminta untuk menyimpan status, Anda tidak perlu data.

Solusi pemulihan A: File status instans Anda sendiri yang disimpan

Anda dapat mendengarkan onSavedInstanceState dan memberi tahu aplikasi flutter Anda tentang acara ini. Kemudian simpan status aplikasi Anda dan tulis dalam file . Saat aplikasi Anda pulih dari kematian proses (Anda dapat mendeteksi ini melalui savedInstanceState di sisi Android) muat file dari sistem file, parsing, dan pulihkan status Anda. (Petunjuk: Gunakan saluran platform, itu lebih mudah dari yang Anda kira).

Anda bahkan dapat memulihkan status saat pengguna mematikan aplikasi, atau mengabaikan status tersimpan jika terlalu lama!

Solusi pemulihan B: Terus simpan status

Kami dapat menjalankan pengatur waktu dan terus menyimpan status aplikasi di sisi platform dalam memori. Katakanlah setiap 3 detik. Saat Android kemudian memanggil onSavedInstanceState kita dapat mengembalikan status yang disimpan sebelumnya. (Ya, kami mungkin kehilangan perubahan yang terjadi dalam 3 detik terakhir)

Solusi pemulihan C: Simpan status pada poin-poin penting

Seperti solusi B, status yang disimpan akan disimpan dalam memori di sisi platform. Namun alih-alih menyimpan status setiap beberapa detik, Anda meminta secara manual untuk menyimpan status setelah pengguna melakukan beberapa tindakan. (membuka laci, memasukkan teks dalam TextField , sebut saja).


Semua 3 solusi dapat digabungkan, gunakan yang paling sesuai dengan aplikasi Anda.

Tetapi bagaimana saya bisa menyimpan status aplikasi saya?

Tidak seperti Android View s, Widget s tidak memiliki onSaveInstanceState callback. Pohon Widget tidak disimpan secara otomatis. Dan itu hal yang bagus! UI dan status akhirnya dipisahkan dan tidak ada keajaiban refleksi saat memulihkan status Tampilan atau membuat instance Aktivitas dan Fragmen.

Untuk menyimpan status, Anda harus mengulangi setiap layar di aplikasi Anda dan menyimpan semua informasi yang diperlukan untuk memulihkannya. Saya suka melakukan ini di json tetapi Anda dapat memilih protobuf untuk kinerja yang lebih baik (Ukur sebelum Anda mengoptimalkan!).

Melihat aplikasi penghitung sampel tercinta kami, ini bisa sangat mudah:

// complete app state of sample application
{
  "counter": 24,
}

Jika Anda memiliki beberapa layar atau dialog, Anda mungkin berakhir dengan json status aplikasi berikut:

{
    "currentRoute": "/todo-detail/241",
    "routes": {
        "todo-detail/241": {
            "edited": "true",
            "title": "Buy bananas",
            "picture": "http://....",
            "show_confirm_delete_dialog": "false"
        },
        "todo-list": {
            "current_scroll_position": "365",
            "filter": "Filter.TODAY"
        }
    }
}

Implementasi sebenarnya sangat bergantung pada arsitektur aplikasi Anda.

  • Jika Anda menggunakan Redux, Anda dapat menggunakan middleware untuk mempertahankan status aplikasi global dengan cepat.
  • Jika Anda menggunakan scoped_model mungkin cukup untuk menyimpan model. Membuat ulang mereka pada dasarnya berbeda dari membuat ulang status Redux.

Kiat terbaik yang bisa saya berikan kepada Anda:

  • Jauhkan pemisahan yang ketat antara UI dan model Anda.
  • Lihatlah setiap layar, dan tanyakan pada diri Anda apa yang benar-benar Anda butuhkan untuk memulihkan keadaan. Sebagian besar waktu itu benar-benar tidak banyak.
  • Bicaralah dengan iOS Anda colleagues dan mereka akan memberi tahu Anda bahwa mereka belum menerapkan pemulihan status melalui encodeRestorableState selama bertahun-tahun. Android 1 jt+ vs iOS 974

Pascal

Aktifkan setelan "Jangan Simpan Aktivitas".

Tidak, ini tidak menguji dengan benar terhadap kematian proses. Ini tidak pernah terjadi kecuali Anda secara eksplisit mengaktifkan pengaturan itu. 😞

Jika Anda memiliki beberapa layar atau dialog, Anda mungkin berakhir dengan json status aplikasi berikut:

{
  "currentRoute": "/todo-detail/241",
  "routes": {
      "todo-detail/241": {
          "edited": "true",
          "title": "Buy bananas",
          "picture": "http://....",
          "show_confirm_delete_dialog": "false"
      },
      "todo-list": {
          "current_scroll_position": "365",
          "filter": "Filter.TODAY"
      }
  }
 }

Astaga, kita mencapai "status navigasi adalah bagian dari status aplikasi sebagai status mesin status terbatas" agar ini berfungsi, tapi saya suka tampilannya

Ya ampun, kami mencapai "status navigasi adalah bagian dari status aplikasi sebagai status mesin status terbatas" agar ini berfungsi

😂 jangan mulai aku.

Status aplikasi lainnya harus disimpan dalam database/di server. Satu-satunya status sementara yang layak disimpan di onSaveInstanceState adalah tumpukan navigasi dan beberapa input pengguna. Itu juga yang kebanyakan orang minta di utas ini.

Status aplikasi lainnya harus disimpan dalam database/di server. Satu-satunya status sementara yang layak disimpan di onSaveInstanceState adalah tumpukan navigasi dan beberapa input pengguna. Itu juga yang kebanyakan orang minta di utas ini.

dan itu KUNCI! (serta dukungan "Layanan Android")

@gitspeak , pertukaran terakhir kami tidak ada hubungannya dengan masalah ini. Saya akan menyembunyikan keduanya. Jika Anda ingin melanjutkan diskusi, silakan PM saya menggunakan pegangan github saya @ gmail.com. Jelas ada kesalahpahaman di sini yang berada di luar cakupan masalah ini.

Sebagai salah satu pengembang yang mengerjakan antarmuka antara Flutter dan Android, saya ingin mengucapkan terima kasih kepada @passsy karena telah memberikan ikhtisar yang bagus tentang masalah yang terkait dengan status penyimpanan.

Saya juga ingin menambahkan bahwa saya tertarik untuk memahami setiap kasus penggunaan khusus yang ditangani oleh pengembang di utas ini. Menyimpan negara adalah konsep yang kemungkinan mencakup banyak tujuan yang berbeda. Apa yang disebut oleh satu aplikasi, yang lain tidak. Satu aplikasi benar-benar harus menjamin status tersimpan, sementara yang lain lebih memilih untuk menyimpan beberapa status tetapi tidak akan rusak tanpanya. Beberapa aplikasi sepenuhnya dibuat dengan Flutter, aplikasi lain harus menyinkronkan informasi antara UI Android/iOS tradisional dan Flutter.

Jika ada di antara Anda yang ingin menjelaskan kasus penggunaan tertentu yang perlu didukung aplikasi Anda terkait dengan status penyimpanan, jangan ragu untuk mengirim email kepada saya di [email protected] - Saya aktif merakit kasus penggunaan.

@passsy Tolong pertimbangkan komentar saya di bawah ini sebagai upaya tulus untuk memberikan umpan balik yang membangun

Solusi pemulihan A: File status instans Anda sendiri yang disimpan
Anda dapat mendengarkan onSavedInstanceState dan memberi tahu aplikasi flutter Anda tentang acara ini. Kemudian simpan status aplikasi Anda dan tulis dalam file. Saat aplikasi Anda pulih dari kematian proses (Anda dapat mendeteksi ini melalui saveInstanceState di sisi Android) muat file dari sistem file, parsing, dan pulihkan status Anda. (Petunjuk: Gunakan saluran platform, itu lebih mudah dari yang Anda kira).
Anda bahkan dapat memulihkan status saat pengguna mematikan aplikasi, atau mengabaikan status tersimpan jika terlalu lama!

Beberapa masalah:

1) Ada kondisi ras yang melekat dengan pendekatan ini. Utas "status bertahan" mungkin lebih lama dari utas Utama aplikasi Android yang memanggil peristiwa siklus hidup, dalam hal ini, proses dapat dihentikan saat file sedang ditulis. Anda dapat mencoba mengoordinasikan dua utas dengan memblokir dalam metode siklus hidup pada sinyal penyelesaian dari utas "penghematan status" tetapi itu, selain memperkenalkan kompleksitas sinkronisasi tambahan, tidak dapat memastikan kebenaran karena penundaan yang cukup lama dapat menyebabkan ANR. (https://developer.android.com/topic/performance/vitals/anr)
Anda mungkin berpendapat bahwa "penyimpanan status" ke file sangat cepat sehingga ini tidak mungkin terjadi, tetapi pertimbangkan penundaan aktual dalam penjadwalan utas dan latensi penyimpanan saat proses lain sedang berjalan seperti unduhan latar belakang, pembaruan play store, pemutar media, dll ... Saya tidak memperdebatkan hal ini, tetapi pengalaman pribadi saya (dibagikan oleh orang lain) adalah bahwa tampaknya perangkat Android tidak kebal terhadap "flu" yang sama yang menginfeksi PC Windows - mereka juga menjadi lebih lambat dari waktu ke waktu. Saya baru saja menghapus Nexus 7 saya dan tidak hanya butuh JAM JAM untuk mengunduh dan menginstal semua aplikasi melalui Wifi, tetapi respons perangkat sekarang menjadi lelucon lengkap untuk menunjukkan di mana saya ragu memiliki kesabaran menggunakannya bahkan untuk mencari resep.

2) Anda tidak dapat memiliki definisi yang dapat diandalkan tentang "terlalu tua". Jika saya membuang Aplikasi dari pengelola tugas, saya berharap semua status segera hilang, sementara jika saya beralih ke aplikasi lain, saya berharap status dipertahankan tanpa batas waktu selama ponsel menyala. Intinya, Android tidak memberi Anda cara untuk membedakan antara kedua status ini.

3) Tidak semua kontrol UI mengekspos statusnya melalui API yang memungkinkan pengguna mengonfigurasi kontrol untuk status tertentu. API tersebut tidak diragukan lagi akan menimbulkan upaya pengembangan tambahan atas nama pengembang kontrol. Pada Android, kontrol UI yang berperilaku baik diharapkan untuk mengabstraksi masalah "pemeliharaan status" dari pengembang dengan menangani notifikasi onSave/Restore InstanceState secara internal sehingga mengontrol status apa jika ada yang dipertahankan. Jika saya ingat dengan benar, kontrol AutoComplete mempertahankan istilah kueri tetapi bukan daftar hasil.

Solusi pemulihan B: Terus simpan status
Kami dapat menjalankan pengatur waktu dan terus menyimpan status aplikasi di sisi platform dalam memori. Katakanlah setiap 3 detik. Saat Android kemudian memanggil onSavedInstanceState, kami dapat mengembalikan status yang disimpan sebelumnya. (Ya, kami mungkin kehilangan perubahan yang terjadi dalam 3 detik terakhir)

Sangat tidak setuju, selain memiliki masalah yang sama seputar "Solusi A", itu menambah risiko baru membuka sekaleng worm duplikasi data.

Solusi pemulihan C: Simpan status pada poin-poin penting
Seperti solusi B, status yang disimpan akan disimpan dalam memori di sisi platform. Namun alih-alih menyimpan status setiap beberapa detik, Anda meminta secara manual untuk menyimpan status setelah pengguna melakukan beberapa tindakan. (membuka laci, memasukkan teks di TextField, sebut saja).

Memiliki masalah yang sama seputar "Solusi A".

Pohon Widget tidak disimpan secara otomatis. Dan itu hal yang bagus! UI dan status akhirnya dipisahkan dan tidak ada keajaiban refleksi saat memulihkan status Tampilan atau membuat instance Aktivitas dan Fragmen.

Tidak setuju. Retensi otomatis Pohon Widget Android dalam kasus di mana statusnya dapat

"Solusi A" tampaknya sangat masuk akal bagi saya saat menggunakan kunci yang sesuai, dan tidak ada ANR yang harus dipicu jika jumlah pekerjaan masuk akal. Juga, dengan merangkul mekanisme notifikasi platform, ia menghindari melakukan pekerjaan serialisasi yang tidak perlu sepanjang waktu, yang mungkin mahal untuk dilakukan untuk setiap penekanan tombol tunggal.

Tentang kemungkinan untuk mengotomatiskan perilaku ini, tampak jelas bagi saya bahwa tidak memiliki mekanisme refleksi di runtime Dart, dan tidak memiliki cara terstruktur untuk menyimpan status dalam memori di Flutter, itu tidak mungkin dengan cara apa pun. Saat ini, pendekatan paling sederhana yang dapat saya pikirkan untuk melakukan ini adalah dengan memilih untuk mempertahankan status dalam satu objek dinamis (seperti this.props dan this.state pada Bereaksi).

@aletorrado

"Solusi A" tampaknya sangat masuk akal bagi saya saat menggunakan kunci yang sesuai, dan tidak ada ANR yang harus dipicu jika jumlah pekerjaan masuk akal.

Saya setuju. ketika ... Di mana Model Android jauh lebih sederhana dan kuat karena tidak memerlukan pengembang untuk menangani kunci, itu tidak mempertahankan status sementara ke disk dan mempertahankan status hanya untuk kasus-kasus di mana itu relevan dengan sesuatu yang Anda (berkibar) tidak dapat melakukannya tanpa menghubungkan ke peristiwa siklus hidup Android dan fasilitas onSaveInstance - untuk memulai, jadi apa gunanya?

Juga, dengan merangkul mekanisme notifikasi platform, ia menghindari melakukan pekerjaan serialisasi yang tidak perlu sepanjang waktu, yang mungkin mahal untuk dilakukan untuk setiap penekanan tombol tunggal.

Tidak ada persyaratan untuk mempertahankan setiap pukulan kunci melainkan snapshot yang terdiri dari status yang dipilih

Data hanya harus disimpan dalam Bundel sebagai ByteArray untuk menghindari
memicu ANR atau lag yang disebabkan oleh I/O pada utas utama.

Pada Rabu, 19 Des 2018, 18:08 Alejandro Torrado [email protected]
menulis:

"Solusi A" tampaknya sangat masuk akal bagi saya saat menggunakan yang sesuai
kunci, dan ANR tidak boleh dipicu jika jumlah pekerjaan masuk akal.
Juga, dengan merangkul mekanisme notifikasi platform, ia menghindari melakukan
serialisasi yang tidak perlu bekerja sepanjang waktu, yang mungkin mahal untuk dilakukan
untuk setiap penekanan tombol tunggal.

Tentang kemungkinan untuk mengotomatiskan perilaku ini, tampak jelas bagi saya bahwa
tidak memiliki mekanisme refleksi di runtime Dart, dan tidak memiliki
cara terstruktur untuk menyimpan status dalam memori di Flutter, itu tidak mungkin
dengan cara apapun. Saat ini, pendekatan paling sederhana yang bisa saya lakukan
ini akan memilih untuk mempertahankan status dalam satu objek dinamis (seperti
this.props dan this.state pada Bereaksi).


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448671264 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AGpvBUQk17YOsjLMHTWcdJHL9rR71u6lks5u6nKGgaJpZM4KwSuX
.

Tipuan sebenarnya adalah membangun ByteArray ini yang Anda bicarakan :slightly_smiling_face:

Saya lebih suka mengatakan bahwa tipu daya sebenarnya adalah untuk dapat mengembalikan ini
ByteArray dengan benar.

Pada Rabu, 19 Des 2018, 18:41 Gabor Varadi [email protected] menulis:

Tipuan sebenarnya adalah membangun ByteArray yang Anda bicarakan ini


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448682229 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AGpvBep8AW2xkKECt6uyTjqRGwri9Pmoks5u6npKgaJpZM4KwSuX
.

Tidak. Setelah Anda memiliki array byte, itu sangat mudah. Cukup tunjukkan indikator pemuatan saat Anda memuatnya (dengan asumsi Anda telah memberi tahu diri sendiri dengan saluran bahwa Anda perlu memuatnya).

Masalah sebenarnya adalah integrasi Flutter, yang saya sebut sebagai bagian pemulihan.
Navigasi harus disimpan secara otomatis, dan input pengguna juga, seperti di
aplikasi Android asli.

Karena masalah ini, aplikasi Flutter tidak dapat berintegrasi dengan benar di Android karena
mereka tidak berperilaku dengan benar ketika ada tekanan RAM dan pengguna beralih
aplikasi dalam kondisi ini sebelum kembali.

Pada Kamis, 20 Desember 2018, 2:42 Gabor Varadi [email protected] menulis:

Tidak. Setelah Anda memiliki array byte, itu sangat mudah. Cukup tunjukkan pemuatan
indikator saat Anda memuatnya (dengan asumsi Anda telah memberi tahu diri sendiri dengan
saluran yang Anda butuhkan untuk memuatnya).


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448827537 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AGpvBR4rmSxelodAjZvcQ6kOwPd7yxKPks5u6ushgaJpZM4KwSuX
.

Banyak dari kami membahas ini sedikit lebih banyak di sini: https://github.com/mehmetf/flutter/issues/1. Poin yang relevan untuk masalah ini:

  • Untuk memperbaikinya (https://github.com/flutter/flutter/issues/6827#issuecomment-447488177): Google Ads diuji dengan mengaktifkan "Jangan Pertahankan Aktivitas". Proses aplikasi tidak mati dalam kasus itu, jadi penjelasan saya di (https://github.com/flutter/flutter/issues/6827#issuecomment-447955562) berlaku. Seperti yang ditunjukkan oleh @Zhuinden , ini bukan cara yang baik untuk menguji skenario yang kami cegah karena tidak berfungsi seperti itu dalam praktiknya.

  • Setiap kali aplikasi dilatar belakangi, View.onSaveInstanceState dipanggil pada utas UI untuk setiap Tampilan dalam hierarki yang telah mengaktifkan penyimpanan. Jadi, solusi yang baik dari sudut pandang pengembang adalah FlutterView untuk mengimplementasikan ini. Untuk mencegah kondisi balapan dengan utas UI (yang tidak dapat/tidak boleh Anda blokir), metode ini tidak boleh menjangkau aplikasi Flutter karena tidak ada cara sinkron untuk melakukannya.

  • Apa pun yang disimpan onSaveInstanceState tidak boleh lebih besar dari 1 MB sehingga menyimpan seluruh status isolasi tidak perlu dipertanyakan lagi. Sebagai gantinya, kita mungkin dapat menemukan cara untuk membagi kode, aset, dan data yang terkandung dalam memori isolasi dan membuang hanya bagian data yang diserialisasi ke dalam paket.

  • Saat tampilan dipulihkan nanti, FlutterView membuat isolat dan membongkar bagian data yang disimpan sebelumnya jika ada.

  • Saat aplikasi berada di latar belakang, status mungkin harus berhenti berubah di sisi Flutter. Akan selalu ada kasus sudut sehingga membuat daftar praktik terbaik untuk aplikasi Flutter akan menyenangkan untuk menyertai solusi ini (mempertimbangkan siklus hidup dan tekanan memori ).

  • Saya membayangkan ini akan menjadi masalah yang jauh lebih mendesak ketika datang ke pasar negara berkembang seperti India di mana Flutter diharapkan berjalan pada ponsel kelas bawah. Android Go juga dikabarkan memiliki strategi manajemen memori yang jauh lebih agresif.

  • Mereka membuat dan memelihara isolat utamanya saat _application_ dimulai (bukan aktivitas). Kemudian, mereka meneruskan isolat ini ke tampilan Flutter saat dibuat.

dapatkah Anda memberikan contoh kode ini?

Mereka membuat dan memelihara isolat utamanya saat aplikasi dimulai (bukan aktivitas). Kemudian, mereka meneruskan isolat ini ke tampilan Flutter saat dibuat.

dapatkah Anda memberikan contoh kode ini?

Saya tidak dapat memberikan contoh jenis salin/tempel, tetapi inilah yang terjadi:

Aplikasi

  • Di Application.onCreate , panggil fungsi inisialisasi untuk FlutterMain . ( startInitialization dan ensureInitializationComplete ). Anda dapat menemukan referensi untuk ini di repo flutter/engine.
  • Buat objek FlutterNativeView baru, teruskan instance Aplikasi sebagai konteks.
  • Jalankan bundel Anda dalam tampilan asli ini (Cari contoh runFromBundle ).
  • Menerapkan antarmuka dalam Aplikasi ini yang memungkinkan Anda mengembalikan tampilan asli ini (seperti FlutterNativeViewProvider ).

Aktivitas

  • Perluas FlutterFragmentActivity dan timpa dua metode ini:
  <strong i="26">@Override</strong>
  public FlutterNativeView createFlutterNativeView() {
    FlutterNativeViewProvider provider = (FlutterNativeViewProvider) getApplication();
    return provider.getFlutterNativeView();
  }

  <strong i="27">@Override</strong>
  public boolean retainFlutterNativeView() {
    return true;
  }

Sesuatu di sepanjang garis ini akan membuat Anda maju. Perhatikan bahwa aplikasi Flutter Anda akan mulai berjalan sebelum Aktivitas (dan dengan demikian jendela) tersedia. Oleh karena itu Anda tidak boleh memanggil runApp() di fungsi utama Anda sampai Anda menerima sinyal dari Aktivitas bahwa itu sudah siap. Anda dapat melakukannya melalui PlatformChannels dan satu tempat untuk memberi sinyal yang ada di createFlutterNativeView().

Penafian

  1. Seperti yang saya sebutkan di atas, kami tidak cukup memaafkan pola ini karena tidak benar-benar menyelesaikan masalah khusus ini dan cukup berantakan.

  2. Perhatikan bahwa banyak dari API ini dijadwalkan untuk berubah. Karena kasus penggunaan add2app masih berkembang, API penyematan Android dan iOS belum cukup stabil. Kami akan mengumumkan perubahan yang melanggar pada flutter-dev@ seperti biasa.

@passsy , @gitspeak

Solusi pemulihan A: File status instans Anda sendiri yang disimpan

Untuk memastikan bahwa Anda memiliki file status yang valid, Anda dapat menulis ke file .tmp dan kemudian mengganti nama saat penulisan selesai. Jika Anda juga menyimpan file status sebelumnya, Anda akan selalu berakhir dengan status yang valid. Juga ketika proses dimatikan saat menyimpan status.

@jsroest

Anda akan selalu berakhir dengan status yang valid.

Tetapi belum tentu keadaan yang benar, yang merupakan inti dari keseluruhan..

Juga tidak ada persyaratan untuk mempertahankan status ke memori non-volatile untuk memulai dengan ..

@gitspeak

Anda akan selalu berakhir dengan status yang valid.

Tetapi belum tentu keadaan yang benar, yang merupakan inti dari keseluruhan..

Juga tidak ada persyaratan untuk mempertahankan status ke memori non-volatile untuk memulai dengan ..

Dalam banyak situasi, memiliki status yang konsisten lebih penting daripada memiliki 'sebagian besar nilai terbaru'. Database relasional yang digunakan dengan transaksi adalah contohnya. Yang menurut saya juga merupakan tempat yang bagus untuk menyimpan negara.

Bertahan pada memori non-volatile memiliki beberapa keuntungan; Prosesnya juga dimatikan pada 'telepon jatuh', perubahan baterai, kegagalan perangkat keras, pembaruan os, reboot secara umum, dll, sehingga statusnya tetap ada saat disimpan ke memori yang tidak mudah menguap.

@jsroest Argumen Anda valid tetapi IMHO tidak berlaku untuk masalah khusus ini karena "status" yang disinggung di sini adalah status UI sementara (mis. Posisi gulir, pilihan aktif, input teks sementara, dll ...). Perlu diingat bahwa memulihkan jenis data ini hanya diperlukan dalam situasi di mana pengguna belum secara eksplisit membuang "Layar" tetapi masukan pengguna masih bisa hilang, seperti saat pengguna beralih ke aplikasi lain. Tidak ada harapan pengguna untuk memulihkan keadaan sementara pada kehilangan daya seperti halnya tidak ada harapan pengguna bahwa browser akan menggulir halaman web yang terakhir ditampilkan ke offset yang tepat sebelum kehilangan daya sistem. Intinya, sifat data yang dianggap sebagai "keadaan sementara" adalah penting dan diskusi di sini (khususnya fasilitas Android onSaveInsatnceState) adalah tentang penanganan jenis data tersebut.

@gitspeak
Saya menulis program untuk perusahaan. Misalnya program yang digunakan di gudang oleh pemetik pesanan, atau contoh lain dalam bisnis ritel, di mana pemilik toko memindai barang yang akan dipesan dari pemasoknya. Pelanggan saya mengharapkan aplikasi untuk memulai di mana mereka meninggalkannya, apa pun yang terjadi. Itu termasuk status sementara, tetapi juga tumpukan navigasi. Bisakah Anda menjelaskan mengapa ini tidak cocok untuk fasilitas OnSaveInstanceState seperti yang dibahas di sini? Mungkin harapan pengguna di pasar konsumen berbeda, tetapi logika yang sama untuk menyimpan dan memulihkan status dapat digunakan IMHO.

@jsroest

Pelanggan saya mengharapkan aplikasi untuk memulai di mana mereka meninggalkannya, apa pun yang terjadi.

Maaf, saya tidak tahu cara membuat aplikasi untuk pelanggan seperti itu, mungkin orang lain dapat membantu ...

@jsroest Itu pertanyaan yang bagus tetapi tidak dalam lingkup masalah ini. Flutter dibatasi oleh apa yang bisa dilakukan oleh platform itu sendiri dan, seperti yang disebutkan @gitspeak , onSaveInstanceState Android adalah tentang menyimpan status UI sementara di mana siklus hidup data berbeda dari yang Anda inginkan. Penyimpanan lokal atau penyimpanan jarak jauh (jika Anda ingin keadaan bertahan di seluruh pencopotan pemasangan dan perangkat) akan memberikan apa yang Anda inginkan tetapi memiliki peringatan, yang sekali lagi berada di luar cakupan masalah ini.

Bacaan yang direkomendasikan lebih lanjut:

https://developer.android.com/topic/libraries/architecture/saving-states [Catatan misalnya bagaimana artikel ini memisahkan penyimpanan lokal dari onSaveInstanceState]
https://developer.android.com/guide/topics/data/data-storage
https://firebase.google.com/docs/firestore/

@jsroest jika pelanggan Anda mengharapkan aplikasi untuk kembali ke tempat semula meskipun dihentikan paksa, dihapus tugas, atau OS dimulai ulang , maka itu adalah sesuatu yang sepenuhnya khusus, dan bukan yang dibicarakan orang di sini.

onSaveInstanceState sedang menyimpan status tugas untuk tugas tertentu, tetapi status tersebut dihapus saat tugas dihapus atau dihentikan paksa atau boot ulang OS.

@mehmetf Saya pikir masalah ini memang telah mencapai tingkat verbositas yang cukup menantang untuk grok. Dengan restu @LouisCAD , saya sarankan Anda menutup masalah ini dan membuka yang baru yang dimulai dengan ringkasan terbaru Anda (mungkin agak disesuaikan dengan penekanan pernyataan masalah dan ruang lingkup) bersama dengan referensi kembali ke diskusi sebelumnya.

Saya setuju tetapi saya akan menyerahkannya kepada @matthew-carroll; dia kemungkinan akan memiliki masalah ini.

Terima kasih untuk umpan baliknya.

Saya tidak ingin memaksakan sesuatu, tetapi saya pikir saya tidak cukup jelas menggambarkan apa yang saya cari. Saya tidak setara dengan semua terminologi yang digunakan di sini di utas ini. Saya melihat banyak kesamaan antara apa yang ingin saya lihat di Flutter dan apa yang dijelaskan oleh @passsy .

Program saya biasanya terdiri dari bagian-bagian berikut:

  • Backend untuk mendorong data dari kondisi mapan saat pengguna selesai dengan pekerjaan.
  • Basis data sqlite lokal untuk kondisi mapan
  • Dalam struktur data memori disebut 'variabel' untuk keadaan transien yang dapat diserialisasikan ke memori non-volatile sehingga dapat hidup lebih lama dari proses.

Struktur data 'variabel' hanya memiliki kelas sederhana dengan properti yang dapat diserialisasi. Bagian penting dari struktur data 'variabel' ini adalah screenstack. (Cukup Daftar). Layar yang ada di atas, adalah layar terakhir yang berinteraksi dengan pengguna. Yang di bawah ini adalah yang dinavigasi pengguna saat menekan 'kembali'.

Saya dapat membayangkan bahwa OnSaveInstanceState dapat digunakan pada platform Android untuk memicu serialisasi struktur data ini. Sesuatu yang serupa harus ditemukan di platform lain (iOS, masa depan: win, mac, web), tetapi seperti yang disarankan @passy, poin-poin penting lainnya juga dapat memicu ini dan itu mungkin cukup baik.

Ketika program dimulai, ia akan memeriksa apakah struktur data serial tersedia dan apakah versi program saat ini sama dengan versi yang membuat serial itu. Ini dapat dilakukan hanya dengan membandingkan nomor versi. Kami tidak ingin memuat struktur data yang tidak kompatibel.

Jika ini semua bertambah maka struktur data dideserialisasi dan tersedia di memori. Program memuat layar yang ada di atas tumpukan. Layar itu sendiri memuat status transiennya dari struktur data. Sekarang kami memiliki program yang bertahan dari kematian proses. (Atau apakah saya melewatkan sesuatu? Ini pasti berfungsi pada proyek saya sebelumnya di windows mobile, formulir Xamarin, dan asp.net. Saya pikir itu juga harus berfungsi di Flutter).

Contoh aplikasi, di mana SXXX adalah singkatan dari layar dengan nomor. Nomor tersebut hanya digunakan untuk mengingatkan pemrogram layar mana yang kurang lebih memiliki satu kesatuan.

S100 Main menu
  S200 Order pick
    S210 Select order
      S220 Confirm pick location
        S230 Pick nr of pieces
          S240 Report shortage
        S250 Scan dropoff location
    S300 Cycle count
      S210 XXXXXX
        S220 XXXXXX
      S230 XXXXXX
    S300 Reprint labels
      S310 XXXXXX

Contoh struktur data variabel

Class variables {
    Class AmbientVariables {
        ….
    }

    Class ScreenStack{
        List<string> ScreenStack;
    }

    Class Orderpick {
        int selected_order;
        string comfirmed_location;
        string nr_picked;
        ….
    }

    Class CycleCount {
        ….
    }

    Class ReprintLabels {
        ….
    }
}

Semua yang ingin saya lihat di flutter mungkin sudah ada di sana, kecuali pembuatan ulang screenstack alias navigationstack. Saya tidak tahu cara membuat ulang pohon objek ini di memori. Saya juga tidak yakin apakah saya ingin membuatnya kembali. Saya juga dapat membuat tumpukan navigasi saya sendiri dan mengimplementasikannya dalam flutter sebagai aplikasi satu halaman. Sama seperti yang saya lakukan di proyek-proyek sebelumnya. Tapi kemudian saya akan kehilangan banyak barang bawaan dari kelas MaterialApp dan Scaffold, di mana panah/tombol kembali adalah yang paling jelas.

Saya menyadari bahwa ini tidak secara otomatis menyimpan semua status sementara. Misalnya, teks yang dipilih, posisi daftar, posisi animasi tertentu. Tetapi sebagai programmer, Anda dapat memutuskan per layar apa yang perlu disimpan. Meskipun itulah yang coba dicegah oleh

Haruskah saya membuat masalah baru atau apakah ini cocok dengan utas ini?

Sekali lagi terima kasih atas umpan baliknya, saya sangat menghargainya.

@jsroest Terima kasih atas penjelasan detailnya. Silakan posting ini di StackOverflow dengan tag flutter. Anda pada dasarnya meminta rekomendasi tentang cara menyusun dan membangun rute Anda dan cara menyimpan rute yang terakhir dikunjungi di penyimpanan persisten.

Anda mungkin berpikir bahwa memecahkan masalah khusus ini memecahkan masalah Anda juga, tetapi itu tidak benar. Anda dapat PM saya di github saya menangani @ gmail.com jika Anda tidak mengerti mengapa.

Ada kemajuan dalam masalah ini?

@vanlooverenkoen secara teoritis jika Anda mempertahankan daftar rute navigator DAN hierarki kunci ke file (dan status widget stateful), dan menghapusnya ketika di sisi asli Android Anda mendapatkan savedInstanceState == null , maka Anda berpotensi membuatnya bertahan dan dapat dipulihkan, hanya saja tidak semudah itu.

@Zhuinden Sebenarnya jauh lebih kompleks yang hanya menyimpan rute dan kunci. Komunikasi antar layar dilakukan dengan menggunakan panggilan balik atau dengan menunggu hasil. Anda entah bagaimana harus mengembalikan status ini juga. Kalau tidak, Anda akhirnya memiliki sesuatu yang terlihat benar tetapi tidak bertindak seperti itu. Anda dapat menyiasati pemulihan callback dan semacamnya dengan menggunakan bentuk komunikasi menggunakan bundel data seperti yang Anda lakukan menggunakan Android asli (dengan maksud), tetapi itu akan menghilangkan banyak kemudahan penggunaan Flutter. Selama tidak ada solusi nyata untuk masalah ini, Flutter tidak dapat digunakan untuk banyak aplikasi, terutama yang digunakan untuk mengumpulkan data berbasis formulir yang besar dan kompleks. Saya heran mengapa masalah ini tidak ditangani secara terbuka oleh tim Flutter, karena mereka pasti harus menyadarinya. Mungkin itu akan mengungkapkan kelemahan serius dalam arsitektur Flutter dan menunjukkan bahwa, di luar presentasi mencolok untuk mengesankan manajemen, sistem bukanlah pilihan yang baik untuk aplikasi kehidupan nyata.

@jsroest @mehmetf @claudiofelber , apakah jawaban untuk pertanyaan StackOverflow ini mengatasi masalah ini? https://stackoverflow.com/questions/54015775/what-is-the-best-way-to-pass-data-or-arguments-between-flutter-app-screens/54015932

Saya telah mengikuti ini dari awal, tetapi harus membaca ulang banyak pesan karena saya kehilangan konteks.

Jika tim Flutter yakin harus ada masalah baru yang segar, dengan lebih sedikit banjir dan rencana yang lebih jelas, saya tidak punya masalah dengan itu dan sebagai hasilnya masalah ini ditutup.

Saya telah membuat plugin native_state untuk memanfaatkan mekanisme Android dan iOS untuk menyimpan status aplikasi saat proses dimatikan.

Navigator ternyata sudah dapat memulihkan rute aplikasi melalui properti initialRoute , _jika_ Anda mengikuti beberapa aturan khusus, yang telah saya dokumentasikan dan tambahkan contoh, dan Anda mungkin selalu ingin menggunakan plugin dalam kombinasi dengan memulihkan rute (baik dengan menggunakan SavedStateRouteObserver disediakan atau mekanisme lain).

Tao baru saja menunjukkan kepada saya bahwa ini adalah masalah ke-4 yang paling banyak diminta dalam survei kami musim gugur yang lalu. Saya pikir ada campuran keinginan pengguna yang diungkapkan di sini (di lebih dari 100 komentar!) Dan kita perlu duduk di beberapa titik dan menggoda mereka menjadi item terpisah yang dapat kita ambil tindakan.

@eseidelGoogle Saya tidak setuju dengan penilaian Anda. IMHO, tidak ada "campuran keinginan pengguna yang diungkapkan" melainkan kurangnya dukungan platform Android yang memunculkan tantangan pemrograman berbeda yang dihasilkan dari masalah inti yang sama.

Aplikasi Android memiliki dua tema sentral:

1) Mesin Status Proses (alias peristiwa Siklus Hidup).
2) perangkat UI.

Flutter melakukan (2) tetapi tidak (1). Itu perlu untuk mengatasi keduanya.

3 tahun...

Pembaruan cepat: Saat ini saya sedang aktif mencari solusi untuk masalah ini.

@goderbauer Hanya untuk memastikan bahwa ini bukan hanya masalah Android, iOS memiliki mekanisme yang sama, meskipun dampaknya pada aplikasi iOS mungkin lebih kecil. Saya telah membuat plugin yang memungkinkan untuk menyimpan status melalui mekanisme os yang mungkin juga perlu dilihat: https://github.com/littlerobots/flutter-native-state

@hvisser bagaimana Anda merekomendasikan menyimpan status navigasi? Navigator dari Flutter tidak memiliki metode getRoutes .

@hvisser Ya, saya melihat ini untuk kedua platform (dan itu harus cukup fleksibel untuk dihubungkan ke platform lain juga). Saya menghapus label platform-android untuk memperjelas bahwa ini bukan masalah khusus Android.

@hvisser bagaimana Anda merekomendasikan menyimpan status navigasi? Navigator dari Flutter tidak memiliki metode getRoutes .

@Zhuinden Navigator sudah memiliki dukungan untuk itu (walaupun saya rasa itu tidak terkenal). Saya punya contoh cara mengaturnya di proyek negara bagian asli, tetapi TL;DR

  • Anda harus menggunakan rute bernama
  • Rute harus hierarkis, misalnya /screen1/screen2 mengikuti /screen1 mengikuti /
  • Rute didorong ke tumpukan navigasi saat Anda menyetel properti initialRoutes dan rute Anda diatur seperti itu
  • Saya _think_ Anda juga memerlukan GlobalKey untuk navigatorKey pada MaterialApp tetapi itu mungkin khusus untuk kasus saya.

Jadi jika Anda menyetel initialRoutes ke /screen1/screen2 dan Anda telah mengatur rute dengan benar, maka memulihkan navigasi juga akan berfungsi.

Satu kasus lagi pada perangkat lama dengan 2 kartu sim saat Anda mengaktifkan dan menonaktifkan mode pesawat dari youtube asli dan aplikasi flutter akan memulai ulang #49057
Video terlampir di edisi saya

Coba nonaktifkan mode debug flutter. jika mode debug diaktifkan, aplikasi selalu memulai ulang.

debugShowCheckedModeBanner: salah,

Saya telah bekerja di flutter sejak 2018, Juni/Juli mungkin dan mengetahui ini adalah masalah setelah 2-3 bulan. Saya tidak tahu itu menjadi masalah besar. Saya sedang membuat aplikasi pemutar musik dan jika saya menekan tombol kembali, bergetar digunakan untuk menghancurkan setiap keadaan sementara musik terus diputar di latar belakang. Untuk mengatasi ini saat itu saya membuat perpustakaan system_shortcuts ini.
Tambahkan plugin ke proyek Anda di pubspec.yaml _[ Bagi yang pemula :) ]_

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  system_shortcuts:

Saya selalu menggunakan solusi yang berfungsi dengan baik untuk saya tetapi ada beberapa masalah di dalamnya, disebutkan di bawah dalam komentar ini tetapi itu tidak terlalu memengaruhi saya.

Penggunaan -

Widget apa pun yang Anda gunakan dan tekan tombol kembali, Anda tahu bahwa aplikasi akan ditutup (yaitu pengguna meninggalkan aplikasi) tambahkan WidgetsBindingObserver Mixin di kelas status seperti ini

class _MusicPlayerState extends State<MusicPlayer>
    with WidgetsBindingObserver{
...
}

Setelah melakukan itu, timpa metode didPopRoute dan tambahkan kode berikut dalam metode:

<strong i="21">@override</strong>
  Future<bool> didPopRoute() async {
    await SystemShortcuts.home();
  return true;
  }

_Mari Pahami cara kerjanya_ :
Aplikasi sedang berjalan >> Pengguna menekan tombol kembali >> Beranda ditekan dan tombol kembali tidak >> Aplikasi pergi ke latar belakang dan statusnya tidak hancur APAPUN

Contoh Kerja -

_ EDIT _
_Aplikasi ini ditulis menggunakan manajemen status penyedia tetapi tidak ada kode yang digunakan untuk mengelola status apa pun setelah menutup aplikasi dan membuka kembali dari yang terbaru. Ini hanya kode yang kami tambahkan berfungsi di sini._

ezgif com-video-to-gif

ezgif com-video-to-gif (1)

Masalah -

Jika Anda datang ke aplikasi Anda dari aplikasi lain, setiap kali Anda kembali dari widget rumah Anda, Anda akan selalu pergi ke layar awal dan bukan ke aplikasi sebelumnya dari tempat Anda datang.
Contoh :-
Pengguna ada di WhatsApp (atau aplikasi lain) >> Mereka mendapat notifikasi dari aplikasi Anda >> Mereka membuka aplikasi Anda dari notifikasi >> Sekarang mereka menekan tombol kembali >> Mereka akan kembali ke layar beranda ponsel dan tidak WhatsApp (yang seharusnya menjadi skenario default)
_Masalah ini belum memberi saya masalah besar sejauh ini dan saya pikir semua pengguna dan pengembang normal akan baik-baik saja dengan itu karena dalam kasus yang jarang terjadi Anda datang ke aplikasi Anda dari aplikasi lain. Saya tidak mengabaikan bahwa itu bisa terjadi dengan tenang beberapa kali tetapi itu berhasil untuk saya dan ini hanya solusi sampai mendapat bantuan aktual dari tim inti flutter._

Fakta bahwa masalah ini hampir berusia 3 setengah tahun dan masih belum ada yang begitu menjengkelkan.

Rupanya, ini akan dikerjakan pada Mei 2020 dilihat dari tonggak yang ditetapkan saat ini… tepat waktu untuk Google I/O?

hvisser di atas telah memecahkan masalah, sekarang orang hanya perlu menulis kode yang membuatnya berfungsi

Saya membuat kemajuan dalam memberikan solusi untuk ini. Untuk pratinjau awal tentang bagaimana aplikasi dapat memulihkan status instans setelah ini selesai, lihat PR ini: https://github.com/flutter/samples/pull/433.

Sayangnya, itu akan memakan waktu lebih lama sampai semuanya selesai.

Dokumen desain dengan detail tentang cara melakukan pemulihan status di Flutter tersedia di sini: http://flutter.dev/go/state-restoration-design

PR dengan kerangka pemulihan keadaan umum sebagaimana diuraikan dalam desain di atas telah diposting: https://github.com/flutter/flutter/pull/60375. Saya masih bekerja untuk menambahkan beberapa cakupan tes lagi.

Kerangka kerja pemulihan keadaan umum (https://github.com/flutter/flutter/pull/60375) telah dikirimkan. Saya sekarang bekerja untuk mengintegrasikannya di widget kami. Mulai dengan scrollable, bidang teks, dan Navigator.

Anda dapat menerapkan ini sendiri dengan menyimpan data yang ingin Anda pulihkan di dalam database sqlite . Itu adalah teknik yang digunakan saat menulis server.

Itu memiliki manfaat memulihkan aplikasi bahkan jika baterai mati di telepon atau ada kepanikan kernel atau apa pun. Saya sarankan membuat pembungkus OO di sekitar panggilan SQLite Anda. Dart tidak memiliki sesuatu seperti SQLite.Net yang sayangnya melakukannya secara otomatis untuk Anda.

edit: Ini juga sesuatu yang kami lakukan di hari-hari awal pengembangan seluler karena memori sangat terbatas dan sqlite adalah cara yang bagus untuk menukar hal-hal antara memori dan disk.

Saya menutup masalah ini karena kerangka pemulihan keadaan umum (dan penyematan Android) telah mendarat. Pekerjaan yang tersisa dilacak dalam edisi terpisah:

Sudah selesai dilakukan dengan baik!!!!! Terima kasih bergetar!!!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat