조용한 담장

Flutter : Platform-specific code 본문

Flutter

Flutter : Platform-specific code

iosroid 2019. 8. 6. 17:39

Flitter Platform-specific code

Flutter Docs 따라가기

Flutter Docs 의 Writing custom platform-specific code 을 따라가 보며 Android/iOS 의 Java, Kotlin, Objective-C, Swift 와 Flutter 가 어떻게 연동되는지 알아보자.

Platform-specific code 를 사용할 수 있다는 것은 이미 존재하는 많은 코드들을 재사용 할 수도 있고, 좀 더 성능에 유리하게 개발 할 수도 있을 것이다.

또 Package 로 만들어서 flutter 내에서 재사용 하는 것도 가능하다.

플랫폼에 상관없이 코드 구조가 비슷해서 Android 와 Java 만 따라가 보겠다.

Flutter 는 Android 는 Java, iOS 는 Objective-C 를 기본으로 사용하므로 Kotlin, Swift 를 사용하려면 아래와 같이 프로젝트를 생성한다.

$ flutter create -i swift -a kotlin batterylevel

Platform channels

코드 구조가 비슷한 이유는 Flutter 와 Platform 이 messeging 방식으로 연동되기 때문이다.

Flutter가 Platform API 에 직접 접근하는 것이 아니라 MethodChannel 이라는 것을 통해 platform API 를 실행하도록 메세지를 보내고 결과를 메세지로 받는 방식이다.

platform_channel

Messages and responses are passed asynchronously, to ensure the user interface remains responsive.

Flutter app (client)

platform 과 통신하기 위한 channel 을 생성한다. 인자로 넘겨주는 이름이 다른 channel 과 구분을 지어주는 이름이니 되니 유일한 문자값을 쓴다.


The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique.


static const platform = const MethodChannel('samples.flutter.dev/battery');

생성된 method channel 의 method 통하여 결과값(배터리 레벨)을 얻어야 한다. 이때 method 도 문자열로 정의하여 사용된다. 아래 예제에는 platform 에 'getBatteryLevel' 문자열로 정의된 method가 있다.


invokeMethod() Invokes a method on this channel with the specified arguments.


  // Get battery level.
  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

Flutter app 의 나머지 코드에서는 결과값 _batteryLevel 을 화면에 그리는 동작만 하면 된다.

Platform host

Android 에서 동작될 method 를 Java 로 구현한다. 파일은 android/app/src/main/java/com/example/batterylevel/MainActivity.java 이다.

MethodChannel 의 이름이 Flutter app 의 MethodChannel 과 동일한것을 확인하자.

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "samples.flutter.dev/battery";

method channel 의 이름이 'getBatteryLevel' 인 method 의 handler 를 구현하면 된다.

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);

    new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
      new MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall call, Result result) {
          // Note: this method is invoked on the main thread.
          if (call.method.equals("getBatteryLevel")) {
            int batteryLevel = getBatteryLevel();
            if (batteryLevel != -1) {
              result.success(batteryLevel);
            } else {
              result.error("UNAVAILABLE", "Battery level not available.", null);
            }
          } else {
            result.notImplemented();
          }
        }
      }
    );
  }

'getBatteryLevel' 의 handler 에서 실제로 배터리 레벨값을 읽어오는 getBatteryLevel() 를 작성해주면 된다.

private int getBatteryLevel() {
    int batteryLevel = -1;
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
        BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
        batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
    } else {
        Intent intent = new ContextWrapper(getApplicationContext()).
                registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
                intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    }

    return batteryLevel;
}

리턴값 batteryLevel 이 method 의 결과값으로 Flutter App 에 전달되어 UI 에 그려질 것이다.

more

battery package

Flutter Docs 의 예제코드를 보았지만 실제 거의 유사한 코드가 battery 값을 가져오는 package 로 구현되어 있다. 코드를 살펴보면 좋은 참고가 될 것이다.

battery

section on threading

[You must invoke a channel method on the platform’s main thread. See the section on threading for more information.]

quick_actions

If desired, method calls can also be sent in the reverse direction, with the platform acting as client to methods implemented in Dart. A concrete example of this is the quick_actions plugin.

Platform channel data types support and codecs

The standard platform channels use a standard message codec that supports efficient binary serialization of simple JSON-like values, such as booleans, numbers, Strings, byte buffers, and List and Maps of these (see StandardMessageCodec) for details).

Reference

https://flutter.dev/docs/development/platform-integration/platform-channels

'Flutter' 카테고리의 다른 글

ListView  (0) 2019.08.08
url_launcher  (0) 2019.08.07
Drawer  (0) 2019.08.02
AnimatedSwitcher  (0) 2019.07.31
BottomNavigationBar, BottomAppBar  (0) 2019.07.29
Comments