Android (Java) のネイティブメソッドを呼び出す方法 - メソッドチャネル

Flutter では Android または iOS のネイティブコードのメソッドを呼ぶための、メソッドチャネル (MethodChannel) という仕組みがあります。

ここでは Android の Java で書いたメソッドにパラメータを渡して、データを返し、Flutter で受け取れることを確認します。

次の画面のプログラムでは数字の計算 (ここでは単に掛け算) を行ったり、デバイス名を取得したりする作業を Java のコードで行います。 それを Flutter (Dart) で受け取り、画面に表示しています。

Flutter のメソッドチャネル

これはどのように実装すれば良いでしょうか。

Flutter 側

Flutter 側は次のステップが必要になります。

  1. MethodChannel を作成。このときチャネルの識別子を渡す。
  2. MethodChannel オブジェクトの invokeMethod メソッドに、ネイティブコード側のメソッド名 (文字列) とパラメータ (を詰め込んだディクショナリ) を渡して呼び出します。
  3. ネイティブコードからの戻り値は invokeMethod の戻り値として取得できます。

main.dart は次の通りです。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(
      MaterialApp(
        debugShowCheckedModeBanner: false,
        home: MyApp(),
      ),
    );

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _State();
  }
}

class _State extends State<MyApp> {
  static const _platform = const MethodChannel("com.keicode.flutter/test1");

  String _label1 = '';

  void _getDataFromPlatform() async {
    var paramMap = <String, dynamic>{
      'a': 7,
      'b': 8,
    };
    var resMap = <dynamic, dynamic>{};

    try {
      resMap = await _platform.invokeMethod(
        "Func1",
        paramMap,
      );
      var calcResult = resMap["calcResult"];
      var deviceName = resMap["deviceName"];

      setState(() {
        _label1 = "$calcResult ($deviceName)";
      });
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Channel - Java'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Container(
              padding: EdgeInsets.all(20.0),
              child: RaisedButton(
                child: Text('Invoke Method'),
                onPressed: () => _getDataFromPlatform(),
              ),
            ),
            Container(
              padding: EdgeInsets.all(16),
              child: Text(
                _label1,
                style: TextStyle(fontSize: 24),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

MethodChannel の作成時に渡す文字列がチャネルの識別子です。Java 側でチャネルを識別するのに使います。

_getDataFromPlatform() というメソッドを作成しています。それをボタンをタップした時に呼び出しています。

Java 側の実装

Java 側、すなわち今回メソッドを呼び出される側は次のようにします。

  1. MethodChannel を作成 (このときチャネルの識別文字列を渡す。Flutter 側で渡した文字列)
  2. メソッドチャネルにメソッドコール・ハンドラを作成 MethodChannel.MethodCallHandler()
  3. MethodCallHandler の onMethodCall メソッドを実装
    • パラメータは onMethodCall に渡される methodCall オブジェクトを経由して取れる
    • 戻り値は result.success に渡す。
    • エラーの場合は result.error を呼ぶ。

ファイルは android/app/src/main/java/<パッケージ名>/<プロジェクト名>/MainActivity.java

package com.example.test1;

import android.os.Bundle;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {

    private static final String CHANNEL = "com.keicode.flutter/test1";

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

        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {

            @Override
            public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                final Map<String, Object> args = methodCall.arguments();
                Dictionary<String, String> results = new Hashtable<String, String>();

                results.put("calcResult", "");
                results.put("deviceName", android.os.Build.MODEL);

                // Get Parameters
                int a, b, c;

                try {
                    a = (int) args.get("a");
                    b = (int) args.get("b");
                    c = a * b;
                } catch (Exception ex) {
                    c = -1;
                }

                // Return Results
                if (methodCall.method.equals("Func1")) {
                    if (c == -1) {
                        results.put("calcResult", "Error n/a");
                        result.success(results);
                    } else {
                        results.put("calcResult", "a * b = " + c);
                        result.success(results);
                    }
                }
            }
        });
    }
}

onMethodCall に渡される MethodCall オブジェクトに、 メソッド呼び出しの情報が詰まっています。

メソッド呼び出しのパラメータは、zMethodCall オブジェクトのarguments() から取得できます。 Flutter 側でマップ (ディクショナリ) でパラメータを渡しているので、Java 側でもやはりマップとして取得できています。

メソッド名は method プロパティで取得できています。これによって、同じチャネルを使って、 複数の種類の呼び出しが可能になります。

メソッド呼び出しの結果は onMethodCall に渡される MethodChannel.Result オブジェクトを使って返します。

今回の例では success として返していますが、エラー発生時には error を呼び出します。

以上、メソッドチャネルを使って、Flutter (Dart) 側から Java のメソッドを呼ぶ方法を紹介しました。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Flutter 入門