2023年3月15日 星期三

Flutter 開發筆記 - 撰寫平台相依性功能,以取得 Android Device 資料為例


研究了一下 Flutter 如何寫平台相依性的程式碼,看一眼也是常見的 Channel or Message 等溝通機制,也滿直觀的。以 Android 平台和 Kotlin 為例,先找到 MainActivity.kt ,接著,在 Code -> Override Methods 可以找到 configureFlutterEngine 可以添加

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
    }
}

接著就可以在透過 MethodChannel 建立綁定溝通管道:

import android.os.Build
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val myCHANNEL = "samples.flutter.dev/helper"
    
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, myCHANNEL).setMethodCallHandler {
                call, result ->
            // This method is invoked on the main thread.
            // TODO

            if (call.method == "getBatteryLevel") {
                val batteryLevel = 0; // getBatteryLevel()
                if (batteryLevel != -1) {
                    result.success(batteryLevel)
                } else {
                    result.error("UNAVAILABLE", "Battery level not available.", null)
                }
            } else if (call.method == "getDeviceInfo") {
                result.success(getDeviceInfo())
            } else {
                result.notImplemented()
            }
        }
    }

    private fun getDeviceInfo(): HashMap<String, String> {
        val deviceInfo:HashMap<String, String> = HashMap<String, String>()
        deviceInfo.run {
            put("MODEL", Build.MODEL)
            put("MANUFACTURER", Build.MANUFACTURER)
        }
        return deviceInfo
    }
}

如此在 Flutter Dart 端,就可以呼叫:

class _MyHomePageState extends State<MyHomePage> {
  static const platform = MethodChannel('samples.flutter.dev/helper');

  String _deviceInfo = "system info unknown";
  Future<void> _getDeviceInfo() async {
    Map<String, String> deviceInfo = {};
    try {
      final Map<Object?, Object?> result = await platform.invokeMethod('getDeviceInfo');
      deviceInfo.clear();
      result.forEach((key, value) {
        if (key.runtimeType == String && value.runtimeType == String) {
          deviceInfo[key.toString()] = value.toString();
        }
      });
    } on PlatformException catch (e) {
      deviceInfo.clear();
    } on Exception catch (e) {
      //print("Exception catch: $e ");
    }

    setState(() {
      _deviceInfo = json.encode(deviceInfo);
    });
  }
...

後續就只是完善細節,強烈建議直接觀看官方文件,有完整的細流程資訊和各平台的範例: docs.flutter.dev/development/platform-integration/platform-channels 

Flutter 開發筆記 - 排除 Could not open settings generic class cache for settings file @ macOS 13.2.1, openjdk 19.0.2, Android Studio Electric Eel | 2022.1.1 Patch 2


最近工作上關係,來回顧一下 Flutter app 的開發,預計寫一款非常簡單的 App 試試水溫。結果在 macOS 13.2.1, openjdk 19.0.2, Android Studio Electric Eel | 2022.1.1 Patch 2 環境,預設弄個專案出來卻不能編譯:

Launching lib/main.dart on sdk gphone64 arm64 in debug mode...
Running Gradle task 'assembleDebug'...

FAILURE: Build failed with an exception.

* What went wrong:
Could not open settings generic class cache for settings file '/Users/UserID/AndroidStudio/usb_displayport_helper/android/settings.gradle' (/Users/UserID/.gradle/caches/7.5/scripts/XXXXXX).
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 63

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 24s
Exception: Gradle task assembleDebug failed with exit code 1

研究了一下,主因是 JDK 與 Gradle 版本問題,需要多做一些事就能排除了。記錄一下。

此時環境:

% java --version
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment (build 19.0.2+7-44)
OpenJDK 64-Bit Server VM (build 19.0.2+7-44, mixed mode, sharing)

這時,在既有專案的左邊上方的 Project 小視窗,挑選 android 目錄,點選右鍵 -> Flutter -> Open Android module in Android Studio -> New Window ,在新的 Android Studio 還沒按 Build 就看到訊息:

Unsupported Java. 
Your build is currently configured to use Java 19.0.2 and Gradle 7.5.

Possible solution:
 - Open Gradle wrapper settings, change `distributionUrl` property to use compatible Gradle version and reload the project

這時就來更換預設的環境: File -> Project Structure -> Project

- Android Gradle Plugin Version: 7.4.2
- Gradle Version: 7.6.1

按下 OK 就會開始抓資料,最後也順利編譯後在模擬器上跑了。