Pemberitahuan & Alarm di Flutter
Bekerja dengan Flutter Plugin, Android_Alarm_Manager.
Ketahuilah ini! Plugin ini hanya berfungsi untuk platform Android ! Saya pribadi tidak tahu apa pun yang setara dengan iOS. Atau, sebulan setelah menerbitkan artikel ini, saya menemukan plugin yang memberikan pemberitahuan pada platform Android dan iOS. Saya, tentu saja, menulis artikel dan itu juga. Lihat di bawah.
Ketahuilah bahwa jika Anda ingin menggunakan plugin yang dijelaskan di sini, Anda harus mengikuti file readme -nya secara eksplisit untuk mengatur semuanya dengan benar. Anda AndroidManfest.xml setidaknya harus terlihat seperti ini di bawah ini:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="NAME OF YOUR APPLICATION STARTING WITH COM.">
<!-- The INTERNET permission access.-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- android_alarm_manager -->
<!-- Start an Alarm When the Device Boots if past due -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- application needs to have the device stay on -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="code_samples"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- android_alarm_manager -->
<service
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
# https://pub.dev/packages/android_alarm_manager
android_alarm_manager: ^0.4.0
Dalam hal ini, ini untuk mematikan alarm di aplikasi Anda! Dalam kasus saya, saya menemukan plugin Flutter, android_alarm_manager , memenuhi kebutuhan aplikasi terbaru yang sedang saya kerjakan, jadi… saya membuat rutinitas agar mudah bekerja dengannya. Jika Anda menginginkannya, ambil salinannya , buatlah milik Anda sendiri dan bagikan perbaikan yang Anda buat. Keren?
Sekarang jika Anda punya waktu, silakan lanjutkan membaca apa yang menurut saya harus saya lakukan untuk membuat plugin ini bekerja hampir 'sangat mudah' sehingga dengan mudah dan cepat mengatur alarm atau operasi lain untuk dilakukan 'di latar belakang' di beberapa titik di masa mendatang saat aplikasi berjalan. Itu membuat Hidup sedikit lebih mudah, dan itu hal yang baik. Baik?
Screenshot Saja. Klik Untuk Intinya.
Seperti biasa, saya lebih suka menggunakan tangkapan layar daripada inti untuk menampilkan kode di artikel saya. Saya merasa mereka lebih mudah untuk dikerjakan, dan lebih mudah dibaca. Namun, Anda dapat mengklik / mengetuknya untuk melihat kode di intinya atau di Github. Ironisnya, lebih baik membaca artikel ini tentang pengembangan seluler di komputer Anda daripada di ponsel Anda. Selain itu, kami memprogram sebagian besar di komputer kami; bukan di ponsel kita. Untuk sekarang.
Mari kita mulai.
Pendekatan saya di sini adalah pertama-tama menyajikan kelas utilitas ini dalam sebuah contoh dan mendemonstrasikan cara menggunakannya - sehingga memanfaatkan plugin Flutter, android_alarm_manager . Faktanya, saya akan menggunakan contoh yang sama yang tercantum di halaman contoh plugin itu sendiri . Namun, contoh ini telah dimodifikasi untuk menggunakan file pustaka yang disajikan di sini. Salinan contoh ini tersedia untuk Anda sebagai intinya, android_alarm_manager . Setelah contoh, saya akan membahas bagian-bagian dari kelas utilitas itu sendiri yang menjelaskan pada waktu apa yang perlu dilakukan untuk memungkinkan kelas seperti itu digunakan oleh massa yang pantang menyerah yaitu masyarakat umum.
Pertahankan Statis
Dalam contoh yang sangat sederhana ini, Anda harus menekan tombol yang ditampilkan, dan 5 detik kemudian, dua angka nol seperti yang digambarkan pada gambar di bawah akan berubah menjadi satu. Wheeee! Yang sangat menarik, tentu saja, adalah apa yang ada di balik terpal dalam kode.
Catatan, saya mengubah kode dari aslinya untuk mengakomodasi operasi asinkron yang harus dilakukan sebelum aplikasi benar-benar dapat mulai berjalan. Saya telah menggunakan widget FutureBuilder untuk mencapai ini. Dalam melakukannya, saya mendefinisikan fungsi baru yang disebut, initSettings () , untuk menginisialisasi rutin 'Preferensi Bersama' yang digunakan untuk 'mengingat' jumlah total alarm yang diaktifkan seperti yang terlihat pada gambar di atas.
Anda juga bisa melihat dalam fungsi initSettings () yang ditampilkan di bawah, ini mengatur 'jumlah total' ke nol jika ini adalah pertama kalinya aplikasi dijalankan. Namun, pada awalnya, rutinitas pustaka, AlarmManager , diinisialisasi untuk menyiapkan 'Layanan Alarm' tertentu yang diperlukan untuk melakukan pemberitahuan pada ponsel Android.
Mari melangkah lebih jauh ke kode contoh dan melihat apa yang membentuk tombol kecil itu. Catatan, rutinitas library menggunakan nama yang sama untuk parameter dan fungsi yang membentuk plugin Flutter yang menggarisbawahi, Android_Alarm_Manager . Lebih baik konsisten dengan hal-hal seperti itu. Lebih lanjut, seperti fungsi oneShot () milik plugin , versi pustaka ini, setelah dipanggil, akan 'menunggu' selama durasi yang ditentukan sebelum mengaktifkan rutinitas callback yang ditentukan. Dalam kasus contoh yang dimodifikasi ini, rutinitas panggilan balik adalah fungsi anonim yang akan berjalan setelah durasi 5 detik. Pada tangkapan layar di bawah ini, aplikasi itu, sebenarnya, dimulai lagi tombol ditekan lagi yang menunjukkan, berkat Preferensi Bersama, bahwa tombol telah ditekan dua kali sejak dijalankan untuk pertama kalinya. Wheeee.
Melihat lebih dekat pada fungsi anonim di bawah ini, dan kami melihatnya diteruskan satu parameter tipe, integer. Anda dapat menebak bahwa itu adalah nilai id yang sama yang diteruskan ke parameter kedua sebagai nomor acak menggunakan fungsi Dart, Random (). NextInt (pow (2,31)) . Sekarang mengapa repot-repot meneruskan nilai itu ketika sudah dilewatkan ke parameter tepat di sebelahnya? Kami akan membahasnya.
Untuk saat ini, Anda dapat melihat lebih jauh di bawah bahwa fungsi callback, pada gilirannya, memanggil fungsi, _incrementCounter (). Di sana, 'jumlah total' penekanan tombol diambil dari rutinitas 'Preferensi Bersama'. Ini kemudian diperbarui oleh satu orang untuk menjelaskan tekan tombol saat ini dan disimpan kembali ke Preferensi Bersama. Layar aplikasi kemudian diperbarui dengan variabel inkrementasi, _counter , menggunakan fungsi, setState (). Apakah Anda mengikuti semua itu sejauh ini?
Menjaga agar Tetap Statis
Tidak seperti contoh aslinya (lihat di bawah), contoh ini diizinkan untuk menggunakan fungsi anonim dan tidak perlu menggunakan fungsi statis secara khusus. Tentu saja, Anda juga diizinkan untuk menggunakan fungsi statis dalam contoh ini - selama fungsi tersebut masih menerima nilai integer tunggal itu. Namun, dalam contoh aslinya, ini harus berupa fungsi statis atau fungsi tingkat tinggi yang ditentukan di luar kelas apa pun dalam file Dart library tersebut - salah satunya, Anda lihat, merupakan persyaratan saat bekerja langsung dengan plugin Flutter, Android_Alarm_Manager .
Namun, jika Anda telah mengikuti artikel saya, Anda tahu saya suka opsi. Ini semua tentang memiliki pilihan dengan saya. Saya telah menulis kelas utilitas ini agar lebih menampung - untuk memungkinkan fungsi anonim misalnya. Oleh karena itu, dengan pengaturan seperti itu, meneruskan nilai integer id tersebut sebagai parameter memang memungkinkan fungsi tersebut menjadi fungsi Statis, fungsi tingkat tinggi, atau fungsi anonim. Pilihan! Sekali lagi, tangkapan layar dari contoh asli mengikuti di bawah ini:
Mari beralih ke kelas utilitas, AlarmManager , itu sendiri. Sekali lagi, ini dirancang untuk bekerja dengan plugin. Dan lagi, banyak parameter yang merupakan parameter yang sama yang digunakan oleh plugin dan diteruskan ke plugin itu — tetapi tidak sebelum beberapa pengujian parameter ekstensif untuk nilai yang valid. Ciri lain yang diperlukan dari kelas utilitas semacam itu. Itu melakukan semua pekerjaan jadi Anda tidak perlu melakukannya. Baik?
Pada gambar di bawah, adalah bagian pertama dari kelas utilitas ini. Dalam fungsi statisnya, init (), kita melihat plugin memang diinisialisasi. Catatan, setiap kesalahan yang tidak menguntungkan yang mungkin terjadi dalam upaya untuk menginisialisasi akan terperangkap dalam pernyataan coba-tangkap . Kelas utilitas juga perlu melakukannya. Selanjutnya di fungsi init (), ada kelas 'helper', _Callback , yang dipanggil untuk menginisialisasi sarana yang diperlukan aplikasi untuk berkomunikasi dengan Isolate terpisah yang digunakan oleh Layanan Alarm di latar belakang.
Terakhir, Anda dapat melihat di bawah ini bahwa setiap dan semua nilai parameter yang bukan null ditetapkan ke properti spesifik dan juga Statis yang membentuk kelas utilitas, AlarmManager . Banyak hal statis yang terjadi di sekitar sini tidak ada.
Anda akan menemukan banyak properti dan fungsi yang membentuk kelas ini adalah Statis. Pilihan untuk menggunakan anggota Statis di kelas haruslah bertemperamen tinggi. Misalnya, karena fungsi init () adalah Statis, itu berarti dapat dipanggil di mana saja, kapan saja dan berapa kali. Fakta itu membutuhkan pertimbangan tambahan. Dalam contoh ini, pernyataan satu baris pertama pada tangkapan layar di atas adalah pernyataan if: 'i f (_init) return _init;'. Itu diperlukan saat menulis fungsi init () ini. Dengan itu, Anda sekarang dapat memanggil fungsi itu sesering yang Anda suka. Terlepas dari itu, layanan dan plugin yang diperlukan hanya diinisialisasi dengan panggilan pertama. Jadi, dalam tim pengembang, misalnya, jika panggilan ke fungsi init () salah dilakukan lebih dari sekali, tidak ada kerusakan yang terjadi. Karakteristik lain yang diinginkan dari kelas utilitas. Lihat apa yang saya lakukan di sini? Menjadikannya agak 'sangat mudah.' Baik?
Buka Pengaturan Anda
Ngomong-ngomong, dengan parameter tersebut diteruskan ke variabel statis tersebut, itu berarti Anda memiliki lebih banyak opsi dalam contoh kami. Ketika fungsi init () dipanggil, pengaturan bisa saja ditentukan saat itu juga. Melakukan hal itu akan memungkinkan setiap dan semua panggilan berikutnya ke fungsi, oneShot (), oneShotAt (), dan periodic (), untuk menggunakan pengaturan tersebut jika tidak secara eksplisit menyediakan pengaturannya sendiri. Saya telah menunjukkan ini di bawah. Anda dapat melihat perbedaan yang dibuat dalam kode contoh jika parameter tambahan dalam fungsi init () yang digunakan. Itu hanya menyisakan panggilan fungsi 'oneShot' dengan durasinya, idnya, dan fungsi panggilan balik yang diperlukan. Membuat kode sedikit lebih bersih. Pilihan!
D Jangan tempatkan init () dalam fungsi build () ! Akan muncul fungsi init () milik plugin Flutter yang disebut, AndroidAlarmManager. initialize () ;, cenderung menyebabkan efek samping atau masalah. Dalam beberapa kasus, ini akan memulai pembangunan kembali (seperti memanggil fungsi setState ()). Itulah mengapa kelas utilitas saya memiliki fungsi init () terpisah . Lebih disukai jika dipanggil di dekat awal aplikasi Anda - di widget FutureBuilder dengan MaterialApp misalnya. Lihat sendiri dan, dalam contoh modifikasi Anda, coba komentari fungsi AlarmManger.init () dari initSettings () dan letakkan sebagai gantinya tepat sebelum fungsi oneShot () (lihat di bawah.) Contoh Anda kemudian akan mulai mengalami kesalahan.
Ambil oneShot Anda
ok, kembali ke kelas utilitas. Dalam fungsi oneShot (), tiga nilai parameter 'diperlukan' pertama sedang menguji validitas dan diteruskan ke fungsi oneShot () milik Plugin . Semua kecuali parameter 'Fungsi', callback . Sebaliknya, itu ditambahkan ke objek Peta statis yang secara unik diidentifikasi oleh id integer yang disediakan menggunakan perintah berikut, _Callback.oneShots [id] = callback . Kami akan segera kembali ke sana. Terakhir, Anda melihat ada panggilan ke fungsi statis, oneShot (), juga ditemukan di kelas helper, _Callback . Itu berdiri sebagai 'fungsi statis' yang diperlukan untuk digunakan oleh plugin. Ini meninggalkan sisa nilai parameter untuk mengambil banyak variabel Statis menggunakan operator penggabung-nol, ?? . Operator digunakan sehingga ketika parameter eksplisit tidak diteruskan, nilai dalam variabel Statis tersebut digunakan sebagai gantinya. Mendapatkan? Variabel statis tersebut semuanya diinisialisasi, dengan nilai default sehingga tidak ada nilai null yang akhirnya diteruskan ke plugin itu sendiri. Bagus.
Jangan Ambil Kesempatan
Catatan, panggilan ke plugin itu sendiri juga disertakan dalam pernyataan coba-tangkap . Itu karena ini adalah program pihak ketiga. Kami tidak tahu apa yang bisa terjadi, dan karena ini adalah kelas utilitas, kami tidak ingin membuat aplikasi Anda mogok, tetapi menangkap pengecualian apa pun yang mungkin terjadi.
Lebih lanjut, seperti kelas utilitas yang baik, kelas ini memiliki sarana bagi pengembang untuk 'menguji' apakah operasi berhasil atau tidak. Jika tidak, Pengecualian apa pun yang mungkin terjadi dicatat sehingga pengembang dapat menanganinya. Ini ditunjukkan di bawah dalam contoh yang dimodifikasi dengan pernyataan if yang baru disisipkan .
Lebih Statis
Lebih jauh di kelas utilitas, AlarmManager . Kami melihat fungsi oneShotAt (). Sekali lagi, karena semua fungsi ini adalah fungsi Statis, beberapa pengamanan perlu dimasukkan ke dalam kode. Dalam keadaan yang tidak menguntungkan, misalnya, plugin mungkin belum diinisialisasi saat fungsi onShotAt () ini dipanggil. Dengan kata lain, fungsi init () tidak dipanggil terlebih dahulu. Itu bisa terjadi bila digunakan oleh masyarakat umum. Anda dapat melihat pada gambar di bawah, situasi seperti itu diuji dengan fungsi assert (). Ini dengan harapan pengembang akan menangkap kesalahan seperti itu saat mereka dalam pengembangan. Dalam produksi, itu ditangkap oleh pernyataan if yang mengikuti fungsi assert ().
Catatan, ini oneShotAt () fungsi memiliki objek Peta sendiri untuk menyimpan disahkan pada fungsi 'callback', dan memiliki fungsinya sendiri statis, _Callback.onShatAt (), untuk diteruskan ke sendiri plugin oneShotAt () fungsi. Ini semua menyiratkan, Anda dapat memanggil fungsi-fungsi ini berapa kali pun dalam aplikasi Anda yang menjadwalkan sejumlah operasi yang akan terjadi di masa mendatang. Tentu saja, masing-masing harus diberi nilai id uniknya sendiri. Jika tidak, operasi apa pun yang sudah dijadwalkan akan ditimpa dengan yang baru jika kebetulan menggunakan nilai id yang sama. Itulah intinya saat menggunakan id unik. Baik?
Namun, semua ini juga menyiratkan Anda dapat menggunakan id yang sama, tetapi di antara tiga fungsi yang berbeda, oneShot (), oneShotAt () dan periodic () secara terpisah. Ingat, mereka memiliki objek Map dan fungsi Statis tersendiri. Fakta ini sangat membantu saya dalam proyek terbaru saya di mana id yang digunakan adalah nilai-nilai yang ditemukan di bidang utama database residen. Pilihan, sayang! Suka!
Mengintip Plugin
Mengintip sekilas sekarang pada fungsi oneShot () dan oneShotAt () milik Flutter plugin , dan Anda dapat melihat bahwa fungsi oneShot (), pada kenyataannya, hanya meneruskan parameternya bersama ke mitra oneShotAt (). Catatan, objek 'CallbackHandle' yang Anda lihat di screenshot di bawah ini berasal dari fungsi, _getCallbackHandle (), yang selanjutnya disebut fungsi framework Flutter, PluginUtilities.getCallbackHandle (callback) . Operasi ini 'menyobek' salinan fungsi panggilan balik sehingga memiliki akses ke sana dan memanggil fungsi seperti itu di Isolate yang berjalan di latar belakang. Saya akan kembali ke itu juga.
Operasi Callback
Mari lanjutkan untuk saat ini dan lihat 'kelas helper' , _Callback , di file perpustakaan . Anda dapat melihat di bawah ini objek Peta yang ditambahkan dalam objek fungsi Callback didefinisikan di kelas ini sebagai properti Statis. Kelas ini juga memiliki fungsi init () dan dipanggil dalam fungsi init () AlarmManger sendiri . Di dalam fungsi init () ini di mana 'port komunikasi' terdaftar dengan tiga pengenal nama tertentu. Porta digunakan oleh Isolate latar belakang untuk berkomunikasi dengan Isolate latar depan dengan meneruskan pesan ke port tersebut. Tebak apa nilai pengenal nama-nama itu? Mereka muncul di tangkapan layar di bawah ini disimpan di variabel, _oneShot , _oneShotAt , dan _periodic .
Sesuai dengan namanya, isolates, adalah segmen memori terpisah
yang didesain, yah,… terisolasi. Tidak ada berbagi memori. Hanya ada penyampaian pesan antara Isolat. Isi pesan semacam itu bisa berupa nilai primitif (null, num, bool, double, String), instance objek SendPort , objek List, atau objek Map dengan salah satu nilai primitif yang disebutkan pertama kali.
Dengarkan Di Latar Belakang
Port selanjutnya diberi 'pendengar' untuk bereaksi jika dan ketika pesan diterima, dalam hal ini, dengan latar belakang Isolate yang menjalankan Layanan Alarm. Pendengar dalam bentuk fungsi anonim yang mengambil objek Map sebagai parameternya. Anda dapat melihat di bawah objek Map berisi nilai integer (yang kebetulan adalah id) dan String yang menyimpan salah satu 'pengenal nama' tersebut. The kasus pernyataan menentukan maka yang 'jenis' fungsi adalah untuk api. Lihat cara kerjanya? Tentu saja, sebagai kelas utilitas, semuanya disertakan dalam pernyataan coba-tangkap . Misalnya, kami tidak tahu apa yang akan terjadi ketika fungsi yang dipilih dijalankan. Kami ingin menangkap Pengecualian yang mungkin terjadi. Baik?
Kirim pesan
Jadi, bagaimana pesan itu dikirim ke aplikasi yang berjalan di latar depan? Nah, setelah pengenal nama tersebut terdaftar di atas, (lihat di bawah) salah satu dari tiga fungsi kelas utilitas, AlarmManager . oneShot (), AlarmManager . oneShotAt () , dan AlarmManager . periodic (), akan meneruskan tiga fungsi Statis yang sesuai, _Callback . onShot (), _Callback . onShotAt () dan _Callback . periodic (), langsung ke plugin Flutter. Melakukannya akan memungkinkan latar belakang Isolate kemudian meneruskan pesan kembali ke aplikasi yang berjalan di latar depan Isolate. Ketiga jenis panggilan tersebut tercantum di bawah ini.
Soalnya, ini adalah tiga fungsi Statis yang sesuai, _Callback . onShot (), _Callback . onShotAt () dan _Callback . periodic (), yaitu 'jembatan' dari latar belakang Isolate ke latar depan Isolate. Ketika tiba waktunya untuk mematikan alarm, misalnya, Layanan Alarm akan memanggil salah satu dari tiga fungsi Statis ini. Perhatikan, saat itu terjadi, ini bukan fungsi sebenarnya yang ditentukan di latar depan Isolate, tetapi 'salinan robek' darinya. Anda akan melihat beberapa perilaku kontra-intuitif karena fakta ini. Misalnya, variabel Statis apa pun dalam fungsi itu yang biasanya ditentukan di latar depan Isolate akan menjadi null di latar belakang Isolate. Anda bisa menguji fenomena ini sendiri.
Misalnya, dalam contoh modifikasi kami, jika saya menekan tombol untuk ketiga kalinya, kita tahu objek Map, oneShots , memiliki satu objek Fungsi di dalamnya untuk menjalankan dan memperbarui layar setelah lima detik, dan itu melakukannya di tangkapan layar dari rutinitas 'Pendengar' di bawah ini. Namun, dalam prosesnya, objek Map itu kosong jika diakses di latar belakang Isolate ?! Bagaimana mungkin? Ini mungkin karena ini adalah salinan dari objek Map dan bukan yang ada di latar depan Isolate. Sekali lagi, Isolate hanya bisa menyampaikan 'pesan' satu sama lain. Mereka tidak berbagi memori.
Sekali lagi, ketiga fungsi Statis ini ada di kelas helper, _Callback , terdaftar satu per satu. Mereka ditampilkan pada gambar di bawah. Di masing-masing, Anda dapat melihat Isolate latar depan direferensikan menggunakan 'pengidentifikasi nama' dan diteruskan objek Map dari latar belakang Isolate. Perhatikan, operator akses anggota bersyarat, ?. , digunakan jika operasi pencarian mengembalikan null. Ini akan dilakukan jika namanya tidak ada, misalnya. Ini tidak mungkin terjadi karena ini semua adalah kode yang diinternalisasi, tetapi sebagai kelas utilitas, kami tidak mengambil risiko. Baik?
Semua ini ada di akhir file perpustakaan, dan di sinilah kita akhirnya melihat nilai 'pengidentifikasi nama' itu di variabel, _oneShot , _oneShotAt , dan _periodic . Masing-masing diberi nama menurut jenis fungsinya yang sesuai. Tidak terlalu imajinatif, tapi masuk akal. Kami juga melihat mereka adalah variabel tingkat tinggi yang ditentukan di luar kelas atau fungsi tingkat tinggi. Faktanya, mereka adalah variabel konstan dengan kata kunci, const . Seperti variabel final, variabel const diinisialisasi hanya sekali dan kemudian tidak dapat diubah. Tidak seperti variabel final, variabel tersebut ditentukan saat aplikasi dikompilasi. Oleh karena itu, untuk kebutuhan kita di sini, mereka tersedia bahkan untuk isolasi latar belakang. Bagus.
Jika Anda tidak memiliki pegangan pada semua hal Isolate ini. Jangan khawatir tentang itu sekarang. Itulah kegunaan kelas utilitas. Tidak seperti kode contoh plugin asli, Anda tidak perlu khawatir tentang pengaturan 'port komunikasi' atau kapan menggunakan Statis atau fungsi atau variabel tingkat tinggi. Itu sebabnya saya menulis kelas ini sejak awal - jadi saya tidak perlu khawatir tentang semua itu juga. Itulah mengapa kelas utilitas seperti itu ditulis sama sekali - sehingga dapat digunakan berulang kali oleh banyak aplikasi yang akan kita tulis di masa mendatang. Baik?
Bersulang.