Method Channel เป็นกลไกการสื่อสารสองทางระหว่างโค้ด Dart และฝั่งดั้งเดิมของ iOS และ Android ใน Flutter ตาม Flutter Documentation, 2026 Method Channel ช่วยให้การส่งข้อความแบบกำหนดชนิดระหว่าง Dart และแพลตฟอร์มโฮสต์เป็นไปได้ หากไม่มีกลไกนี้ การเข้าถึงความสามารถด้านฮาร์ดแวร์ของอุปกรณ์ SDK ดั้งเดิม และการเรียกระบบจากโค้ดแอปพลิเคชันจะเป็นไปไม่ได้
ประเด็นสำคัญ
Method Channel เป็นองค์ประกอบหลักของชั้นแพลตฟอร์ม Flutter ซึ่งไอโซเลต Dart แลกเปลี่ยนข้อความกับแอปพลิเคชันโฮสต์บน iOS หรือ Android งานหลักของช่องทางคือการซ่อนความแตกต่างในโปรโตคอลการส่งข้อมูลระหว่างสองแพลตฟอร์มและจัดเตรียม API ที่เป็นหนึ่งเดียวสำหรับนักพัฒนา
เมื่อแอปพลิเคชัน Flutter ต้องการเข้าถึงกล้อง Bluetooth เซ็นเซอร์ หรือ API ดั้งเดิมอื่น ๆ การเรียกโดยตรงจาก Dart เป็นไปไม่ได้ Flutter ทำงานในเอนจินที่สร้างบน C++ และไม่สามารถเข้าถึงเฟรมเวิร์ก UIKit หรือ Android SDK Method Channel แก้ปัญหานี้โดยสร้างสะพานเชื่อมระหว่างโลกของ Dart และโลกของโค้ดดั้งเดิม
ตามข้อมูลจาก Google I/O 2024 กว่า 80% ของแอปพลิเคชัน Flutter ในโปรดักชันใช้ Method Channel อย่างน้อยหนึ่งช่องทางสำหรับการรวมเข้ากับบริการแพลตฟอร์ม ซึ่งยืนยันบทบาทที่สำคัญของช่องทางในสถาปัตยกรรมของโครงการสมัยใหม่
สำหรับนักพัฒนา Method Channel ดูเหมือนการเรียกฟังก์ชันอะซิงโครนัสทั่วไป เบื้องหลังเกิดการทำให้ข้อความเป็นลำดับ การส่งผ่านบัฟเฟอร์ของเอนจิน และการดำเนินการโค้ดดั้งเดิมบน เธรดหลัก ของแพลตฟอร์ม
การโต้ตอบ ผ่าน Method Channel เริ่มต้นเมื่อฝั่ง Dart ส่งข้อความที่มีชื่อเมธอดและอาร์กิวเมนต์ Flutter Engine รับข้อความนี้ แปลงเป็นรูปแบบมาตรฐาน StandardMethodCodec และส่งต่อไปยังฝั่งดั้งเดิมผ่าน BinaryMessenger
ฝั่งดั้งเดิมมีตัวจัดการ — MethodCallHandler ซึ่งรับการเรียกที่ถูกดีซีเรียลไลซ์และดำเนินการตรรกะที่เกี่ยวข้อง ผลลัพธ์จะถูกส่งกลับไปยัง Dart ในรูปแบบ Response ซึ่งประกอบด้วยผลลัพธ์ที่สำเร็จหรือ ข้อผิดพลาด พร้อมรหัสและข้อความ
วงจรการเรียกทั้งหมดผ่าน Method Channel สามารถแบ่งออกเป็นหกขั้นตอน ไอโซเลต Dart สร้างอินสแตนซ์ของช่องทางด้วยชื่อที่ไม่ซ้ำกันเพื่อระบุการเชื่อมต่อ เมื่อเรียก invokeMethod โค้ดแพลตฟอร์ม Dart จะทำให้ชื่อเมธอดและอาร์กิวเมนต์เป็นลำดับโดยใช้ MethodCodec ซึ่งแปลงเป็นบัฟเฟอร์ไบนารีผ่าน StandardMessageCodec
Flutter Engine ส่งบัฟเฟอร์นี้ผ่านซ็อกเก็ตไปยังฝั่งดั้งเดิม BinaryMessenger ดั้งเดิมอ่านข้อความ ระบุช่องทางตามชื่อ และเรียกตัวจัดการที่ลงทะเบียน โดยส่งออบเจ็กต์ FlutterMethodCall พร้อมข้อมูลที่แยกวิเคราะห์ ตัวจัดการดำเนินการโค้ดที่จำเป็นและส่งคืนผลลัพธ์ ซึ่งผ่านเส้นทางการทำให้เป็นลำดับย้อนกลับและไปถึง Dart ในรูปแบบ Future
สถาปัตยกรรม Method Channel ประกอบด้วยเอนทิตีที่เชื่อมต่อกันหลายตัว ซึ่งแต่ละตัวรับผิดชอบขั้นตอนการส่งข้อมูลของตนเอง Dart API จัดเตรียมคลาส MethodChannel ซึ่งซ่อนรายละเอียดระดับต่ำของการทำให้เป็นลำดับและการกำหนดเส้นทางจากนักพัฒนา
BinaryMessenger เป็นอินเทอร์เฟซระดับต่ำของ Flutter Engine สำหรับส่งและรับข้อความไบนารีระหว่าง Dart และแพลตฟอร์มโฮสต์ MethodChannel แต่ละช่องทางผูกกับ BinaryMessenger เฉพาะที่ให้การกำหนดเส้นทางตามชื่อช่องทาง ฝั่ง Dart ใช้คลาส BinaryMessenger บน Android — BinaryMessenger จากแพ็คเกจ io.flutter.embedding.engine บน iOS — โปรโตคอล FlutterBinaryMessenger
MethodCodec เป็นตัวเข้ารหัสที่แปลงการเรียกเมธอดและค่าที่ส่งคืนเป็นรูปแบบไบนารี Flutter มาพร้อมกับการใช้งานในตัวสองแบบ: StandardMethodCodec (ค่าเริ่มต้น) และ JSONMethodCodec (สำหรับสตริง JSON) StandardMethodCodec ใช้ StandardMessageCodec ภายใน ซึ่งทำให้ข้อมูลเป็นลำดับโดยรองรับชนิดพื้นฐานทั้งหมดของ Dart
StandardMessageCodec รองรับชุดชนิดข้อมูลที่จำกัดเพื่อให้แน่ใจว่าสามารถทำงานร่วมกันระหว่าง Dart, Kotlin และ Swift รายการประกอบด้วย: null, bool, int, double, String, Uint8List, Int32List, Int64List, Float64List, List และ Map ที่มีคีย์เป็นสตริง
ชนิดอื่นทั้งหมด — DateTime, ออบเจ็กต์ DTO หรือคลาสที่กำหนดเอง — ต้องถูกแปลงเป็นรูปแบบใดรูปแบบหนึ่งที่ระบุไว้ วิธีการที่พบบ่อยที่สุดคือการทำให้ออบเจ็กต์ที่ซับซ้อนเป็นลำดับใน Map พร้อมฟิลด์และสร้างโครงสร้างขึ้นใหม่ที่ฝั่งผู้รับจาก พจนานุกรมฟิลด์
สำหรับการส่งข้อมูลไบนารีขนาดใหญ่ เช่น ภาพจากกล้อง Flutter แนะนำให้ใช้ BasicMessageChannel กับ Uint8List เพื่อหลีกเลี่ยงการคัดลอกบัฟเฟอร์ทั้งหมดในการเรียกแต่ละครั้งผ่าน MethodChannel
| ชนิด Dart | ชนิด Kotlin | ชนิด Swift |
|---|---|---|
| null | null | nil |
| bool | Boolean | NSNumber |
| int | Int | NSNumber |
| double | Double | NSNumber |
| String | String | NSString |
| Uint8List | ByteArray | FlutterStandardTypedData |
| List | List | Array |
| Map | HashMap | Dictionary |
การตั้งค่า Method Channel ฝั่ง Android ดำเนินการในคลาสที่ใช้ FlutterPlugin หรือโดยตรงใน MainActivity วิธีแรกเป็นวิธีที่แนะนำเนื่องจากให้การจัดการวงจรชีวิตปลั๊กอินที่เหมาะสมและความเข้ากันได้กับสถานการณ์ add-to-app
หลังจากสร้างอินสแตนซ์ของช่องทางด้วยชื่อเดียวกับฝั่ง Dart จำเป็นต้องลงทะเบียน MethodCallHandler ผ่าน setMethodCallHandler ภายในตัวจัดการ นักพัฒนาจะตรวจสอบชื่อของเมธอดที่เข้ามาผ่าน when และส่งคืนผลลัพธ์ผ่าน result.success หรือข้อผิดพลาดผ่าน result.error พร้อมรหัสและข้อความ
package com.example.app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = getBatteryLevel()
if (batteryLevel != null) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery not available", null)
}
}
else -> result.notImplemented()
}
}
}
}
ในตัวอย่างนี้ ช่องทางชื่อ samples.flutter.dev/battery จัดการการเรียก getBatteryLevel รับระดับแบตเตอรี่ผ่าน Android BatteryManager และส่งคืนไปยังโค้ด Dart ชื่อช่องทางต้องตรงกันทั้งสองฝั่ง มิฉะนั้นข้อความจะไม่ถึงตัวจัดการ
สำหรับโค้ดโปรดักชัน ขอแนะนำให้แยกตรรกะ Method Channel ไว้ในคลาสแยกต่างหากที่ใช้ FlutterPlugin ซึ่งช่วยให้สามารถนำปลั๊กอินไปใช้ซ้ำระหว่างโครงการและรับประกันการทำความสะอาดทรัพยากรที่ถูกต้องเมื่อเรียก onDetachedFromEngine ปลั๊กอินถูกลงทะเบียนผ่าน registerWith และสามารถทดสอบแยกจาก Activity
Method Channel บน iOS ถูกตั้งค่าในคลาสที่ใช้โปรโตคอล FlutterPlugin หรือใน AppDelegate วิธีการที่แนะนำคือการสร้างคลาสปลั๊กอินแยกต่างหากที่ลงทะเบียนผ่าน FlutterPluginRegistrar และจัดการโดย Flutter Engine
ฝั่ง Dart ส่งการเรียก และตัวจัดการดั้งเดิมรับออบเจ็กต์ FlutterMethodCall พร้อมชื่อเมธอดและอาร์กิวเมนต์ นักพัฒนาระบุเมธอดที่ถูกเรียกผ่าน switch บน call.method และส่งคืนผลลัพธ์ผ่านคลอเจอร์ result ในการเข้าถึง API ของ iOS จะใช้ UIKit และเฟรมเวิร์กระบบอื่น ๆ
import Flutter
import UIKit
public class BatteryPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: registrar.messenger())
let instance = BatteryPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getBatteryLevel":
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
let level = Int(device.batteryLevel * 100)
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
}
วิธีการ FlutterPlugin รับประกันการลงทะเบียนและการปิดใช้งานปลั๊กอินที่ถูกต้องเมื่อ Flutter Engine ถูกทำลาย ในตัวจัดการ Swift จะใช้ switch บน call.method แต่ละเคสส่งคืนผลลัพธ์ผ่านคลอเจอร์ result อาร์กิวเมนต์สามารถเข้าถึงได้ผ่าน call.arguments พร้อมการแปลงชนิดเป็นชนิดที่เหมาะสม
เมื่อทำงานกับ Method Channel สิ่งสำคัญคือต้องปฏิบัติตามกฎสำคัญหลายข้อเพื่อให้มั่นใจถึงประสิทธิภาพและความเสถียรของแอปพลิเคชัน คำแนะนำหลักคือการลดปริมาณและขนาดของข้อมูลที่ส่ง โดยเฉพาะอย่างยิ่งในการเรียกภายในลูป แอนิเมชัน หรือด้วยความถี่สูง
ฝั่งดั้งเดิมควรจัดการข้อยกเว้นและส่งคืนข้อผิดพลาดผ่าน result.error พร้อมข้อความที่อ่านได้เสมอ ฝั่ง Dart การเรียก invokeMethod แต่ละครั้งควรถูกห่อใน try-catch เพื่อจับ PlatformException การละเว้นข้อผิดพลาดอาจทำให้แอปพลิเคชันหยุดทำงานโดยไม่คาดคิดโดยไม่มีสาเหตุชัดเจน
โดยค่าเริ่มต้น Method Channel ดำเนินการโค้ดดั้งเดิมบนเธรดหลักของแพลตฟอร์ม หากตัวจัดการดำเนินการที่หนัก ควรย้ายการดำเนินการไปยังเธรดพื้นหลังโดยใช้ Kotlin Coroutines บน Android หรือ Grand Central Dispatch บน iOS ผลลัพธ์ควรถูกส่งคืนผ่าน result หลังจากงานบนเธรดหลักเสร็จสมบูรณ์เท่านั้น
เลือกชื่อที่ไม่ซ้ำกันสำหรับช่องทางโดยใช้สัญกรณ์โดเมนย้อนกลับ — ตัวอย่างเช่น com.example.app/feature ชื่อสั้นอาจขัดแย้งกับปลั๊กอินอื่น Flutter ลงทะเบียนช่องทางทั่วโลก ดังนั้นชื่อที่เหมือนกันในปลั๊กอินต่าง ๆ จะนำไปสู่การเขียนทับตัวจัดการและการเรียกที่ใช้งานไม่ได้
คำถามที่พบบ่อย
MethodChannel ออกแบบมาสำหรับการเรียกเมธอดในรูปแบบคำขอ-ตอบกลับด้วยการเข้ารหัสผ่าน MethodCodec BasicMessageChannel ส่งข้อความตามอำเภอใจโดยไม่มีรูปแบบเมธอดและอาร์กิวเมนต์ ซึ่งสะดวกสำหรับ ข้อมูลสตรีม และอีเวนต์จากแพลตฟอร์ม
โดยตรง — ไม่ได้ StandardMessageCodec รองรับเฉพาะชนิดพื้นฐาน: ชนิดดั้งเดิม, String, Uint8List, List และ Map ออบเจ็กต์ที่กำหนดเองต้องถูกทำให้เป็นลำดับด้วยตนเองเป็น Map ก่อนส่งและสร้างใหม่ที่ฝั่งผู้รับจากพจนานุกรมฟิลด์
ฝั่งดั้งเดิม ใช้ result.error พร้อมรหัสข้อผิดพลาดและข้อความ ฝั่ง Dart ห่อ invokeMethod ใน try-catch และจับ PlatformException หากเมธอดไม่ได้ถูกใช้งานบนแพลตฟอร์ม ให้ส่งคืน result.notImplemented
การเรียกแต่ละครั้งทำการทำให้เป็นลำดับและคัดลอกข้อมูลระหว่างไอโซเลตและแพลตฟอร์ม สำหรับการเรียกที่ไม่บ่อย โอเวอร์เฮดไม่มีนัยสำคัญ เมื่อส่งข้อมูลขนาดเมกะไบต์ต่อเฟรม อาจเกิด ความล่าช้า และ FPS ลดลง สำหรับข้อมูลสตรีม ให้ใช้วิวแพลตฟอร์มหรือออบเจ็กต์เรนเดอร์พื้นผิว
ใช้ EventChannel — ออกแบบมาสำหรับการสตรีมอีเวนต์จากฝั่งดั้งเดิมไปยัง Dart แพลตฟอร์มเริ่มการส่งผ่าน EventSink และ Dart สมัครรับสตรีมโดยใช้ receiveBroadcastStream Method Channel ไม่เหมาะสำหรับสถานการณ์นี้
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ