Flutter开发Flutter与原生OC、Java的交互通信-1(47)

我的文章讲过:(0085)iOS开发之OC与JS交互高级用法(JavaScriptCore)

前言:我们知道OC 在UIWebView 加载 H5中,常常需要OC 与 H5 之间进行交互调用。采取的方法有:

  1. WebViewJavascriptBridge
  2. JavaScriptCore 框架
  3. 拦截URL

特征:都通过一种中间人进行桥接实现相互调用的。

今天讲的:Flutter与原生OC、Java的交互通信 也是一种桥接的方式实现的。类似WebViewJavascriptBridge、JavaScriptCore的方式。Flutter 提供了 platform channels ,来和管理你的 Flutter view 的 ViewController 通信和交互数据。平台管道本质上是一个异步通信机制,桥接了 Dart 代码和宿主 ViewController,以及它运行于的 iOS 框架。你可以用平台管道来执行一个原生的函数,或者是从设备的传感器中获取数据。

本文翻译自官方文档:Writing custom platform-specific code

platform channel:平台通道
host;主机(这里指iOS 安卓端代码)
client:客户端(这里指Flutter端代码)

译文:

编写自定义特定平台代码(自定义平台:这里指iOS、安卓)

本指南介绍如何编写特定于平台的自定义代码。某些特定平台的功能可以通过现有的软件包获得;请参见using packages。

Flutter使用一个灵活的系统,允许您在Android平台上使用Java或Kotlin代码调用平台特定API,或者在Objective-C或Swift代码上调用iOS。

Flutter平台特定的API支持不依赖代码生成,而是依赖灵活的消息传递风格:

  • 应用程序的flutter部分通过平台通道向其host(这个应用程序的iOS、Android部分)发送消息。
  • host通过平台通道监听并接收消息。然后,它使用native programming language调用任意数量特定平台的API,同时向client(应用程序的flutter部分)发送响应。

⚠️:如果您需要在Java/Kotlin/Objective-C or Swift中使用对应平台的APIs or libraries,本指南将使用平台通道机制来说明。但您也可以通过查看defaultTargetPlatform属性,在你的Flutter app中编写特定平台的Dart代码。Platform adaptations 在这个框架中Flutter自动帮我们做了一些特定平台的适配。

体系结构概述:平台通道

消息通过平台通道在the client (UI) and host (platform) 之间传递,如图所示:
Flutter开发Flutter与原生OC、Java的交互通信-1(47)_第1张图片

消息和响应是异步传递的,以确保用户界面保持响应。

⚠️注意:即使flutter异步地向Dart发送消息和从Dart接受消息,每当调用通道方法时,必须在平台的主线程上调用该方法。有关更多信息,请参阅线程部分。

On the client side, MethodChannel (API) 能发送message 并能响应方法回调,On the platform side,在Android 的(API)MethodChannel 和iOS的(API) FlutterMethodChannel能接收到方法调用并返回一个结果。这些类允许您用很少的“样板”代码开发平台插件。

补充:如果需要,方法调用也可以反向发送消息,用平台充当客户端在Dart中实现方法。快速动作插件quick_actions就是一个具体的例子。

平台通道数据类型支持和编解码器

标准平台通道使用标准消息编解码器,该编解码器支持对类似于JSON的简单值(booleans, numbers, Strings, byte buffers, and List and Maps)进行有效的二进制序列化(有关详细信息,请参阅StandardMessageCodec编解码器)。当发送和接收值时,这些值与消息之间的序列化和反序列化将自动进行。

下表显示了如何在平台侧接收Dart值,反之亦然:
Flutter开发Flutter与原生OC、Java的交互通信-1(47)_第2张图片

示例:使用平台通道调用特定平台iOS和Android的代码

下面代码演示如何调用特定平台的API来取回并显示当前电池电量。它通过平台消息的一个消息getBatteryLevel()来使用Android BatteryManager API和ios device.batteryLevel API实现的。

该示例将特定平台的代码添加到主应用程序本身中。如果您想为多个应用程序重用特定平台的代码,那么项目创建步骤略有不同(请参见开发包),但是平台通道代码仍然是以相同的方式编写的。

注意:这个例子的完整的可运行源代码(Android with Java and iOS with Objective-C)可以在/examples/platform_channel/找到。For iOS with Swift, see /examples/platform_channel_swift/。

Step 1: 创建一个新工程

这里我已经有一个工程hello,跳过此步。

Step 2:创建Flutter客户端平台
  • 首先,使用一个带有返回电池电量的a single platform method 来构造通道。

通道的客户端和主机端通过通道构造函数中传递的通道名称连接。单个应用程序中使用的所有频道名称必须是唯一的;在频道名称前面加上唯一的“域前缀”,例如:samples.flutter.dev/battery

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class MethodChannelTestViewState extends State {
  static const platform = const MethodChannel('samples.flutter.dev/battery');

  // Get battery level.
}
  • 接着,封装一个方法_getBatteryLevel(),在这个方法中用上面的通道对象platform调用invokeMethod来调用一个字符串标识符为getBatteryLevel一个具体方法。这个方法在特定平台实现。调用可能会失败,例如,如果平台不支持platform API(例如在模拟器中运行时),因此请将invokeMethod调用包装在try catch语句中。

用返回的结果来更新setstate内电池级别的用户界面状态。

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

  Future _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;
    });
  }
  • 最后,替换模板中的build方法,以包含一个以字符串形式显示电池状态的
    用户界面,以及一个用于刷新值的按钮。
@override
Widget build(BuildContext context) {
  return Material(
    child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          RaisedButton(
            child: Text('Get Battery Level'),
            onPressed: _getBatteryLevel,
          ),
          Text(_batteryLevel),
        ],
      ),
    ),
  );
}

Step 3a 使用Objective-C添加iOS特定平台的实现

注意:以下步骤使用Objective-C。如果您喜欢使用Swift,请跳到步骤4b。
首先在Xcode中打开flutter应用程序的iOS主机部分:

  1. 找到Flutter项目文件夹hello->ios->Runner.xcworkspace 用Xcode打开。
  2. 打开 AppDelegate.m
  • [1] copy 下面代码并替换AppDelegate.m对应代码
#import 
#import "GeneratedPluginRegistrant.h"

@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
                                          methodChannelWithName:@"samples.flutter.dev/battery"
                                          binaryMessenger:controller];

  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
    // Note: this method is invoked on the UI thread.
    // TODO
  }];

  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
  • [2] 接下来,添加使用iOS电池API检索电池电量的OC代码。此代码与您在本机iOS应用程序中编写的代码完全相同。
    AppDelegate类中@end之前添加以下方法:
- (int)getBatteryLevel {
  UIDevice* device = UIDevice.currentDevice;
  device.batteryMonitoringEnabled = YES;
  if (device.batteryState == UIDeviceBatteryStateUnknown) {
    return -1;
  } else {
    return (int)(device.batteryLevel * 100);
  }
}
  • [3] 最后来完善前面添加的setMethodCallHandler()方法。您需要处理调用getBatteryLevel()以此来测试返回的值,并使用result参数返回成功和错误情况的响应。如果调用了未知的方法,请报告该方法:unknown method is called
__weak typeof(self) weakSelf = self
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
  // Note: this method is invoked on the UI thread.
  if ([@"getBatteryLevel" isEqualToString:call.method]) {
    int batteryLevel = [weakSelf getBatteryLevel];

    if (batteryLevel == -1) {
      result([FlutterError errorWithCode:@"UNAVAILABLE"
                                 message:@"Battery info unavailable"
                                 details:nil]);
    } else {
      result(@(batteryLevel));
    }
  } else {
    result(FlutterMethodNotImplemented);
  }
}];

完成:您现在应该可以在iOS上运行该应用程序了。如果使用iOS模拟器,请注意它不支持电池API,并且应用程序显示“电池信息不可用”。

效果如下:这样就实现了Flutter与原生OC的通信。为了更好的验证。我还增加了一个方法getFromOCClientMessage

最后附上flutter 、OC端的完整代码

flutter:

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


class MethodChannelTest extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MethodChannelTestView();
  }
}

class MethodChannelTestView extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return MethodChannelTestViewState();
  }
}

class MethodChannelTestViewState extends State  {

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

  final textColorStyle = TextStyle(color: Colors.deepOrange);

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

  // 测试从OC 端传过来的字符串
  String _messageFromOC = '默认Flutter';

  Future _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}'.";
    }

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

  // 测试Flutter 与OC 端通信
  Future _getFromOCClientMessage () async {
    String message;
    try {
      final String result = await platform.invokeMethod('getFromOCClientMessage');
      message = 'Message 来自 $result % .';
    } on PlatformException catch (e) {
      message = "Failed to get Message: '${e.message}'.";
    }

    print('我获取到从OC 传过来的值 :$message');
    setState(() {
      _messageFromOC = message;
    });
  }


  void _pushSaved() {
    print('你哈后!');
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('我的收藏列表'),
        actions: [
          new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            RaisedButton(
              child: Text('Get Battery Level'),
              onPressed: _getBatteryLevel,
            ),
            Text(_batteryLevel,style: textColorStyle),

            SizedBox(
              height: 40,
            ),

            RaisedButton(
              child: Text('Get Message From OC'),
              onPressed: _getFromOCClientMessage,
            ),
            Text(_messageFromOC,style: textColorStyle,),

          ],
        ),
      ),
    );
  }
}

OC 端代码:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
    
    FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
    FlutterMethodChannel *batteryChannel = [FlutterMethodChannel
                                            methodChannelWithName:@"samples.flutter.dev/battery"
                                            binaryMessenger:controller];
    
    __weak typeof(self) weakSelf = self;
    [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        // Note: this method is invoked on the UI thread.
        if ([@"getBatteryLevel" isEqualToString:call.method])
        {
            int batteryLevel = [weakSelf getBatteryLevel];
            if (batteryLevel == -1) {
                result([FlutterError errorWithCode:@"UNAVAILABLE" message:@"Battery info unavailable" details:nil]);
            } else {
                result(@(batteryLevel));
            }
            
        } else if ([@"getFromOCClientMessage" isEqualToString:call.method])  {
            NSString *testMethod = [weakSelf getFromOCClientMessage];
            result(testMethod);
            
        } else {
            result(FlutterMethodNotImplemented);
        }
    }];
    [GeneratedPluginRegistrant registerWithRegistry:self];
    
    
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (int)getBatteryLevel {
    UIDevice* device = UIDevice.currentDevice;
    device.batteryMonitoringEnabled = YES;
    if (device.batteryState == UIDeviceBatteryStateUnknown) {
        return -1;
    } else {
        return (int)(device.batteryLevel * 100);
    }
}

- (NSString *)getFromOCClientMessage {
    return @"我来自OC client!!!";
}

@end

Step 3b 使用swift添加iOS平台特定的实现

不再翻译和Step 4a 相同。只是改用swift语法实现即可。

Step 4a 使用Java添加Android平台特定的实现

同理:

  1. 找到Flutter项目文件夹hello->Android 用Android Studio打开。
  2. 打开 MainActivity.java
  • [1] copy 下面代码并替换MainActivity.java对应代码
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

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

    @Override
    public 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.
                        // TODO
                    }
                });
    }
}
  • [2] 接下来,添加使用Android电池API检索电池电量的java代码。此代码与您在本机Android应用程序中编写的代码完全相同。
    首先,在文件顶部添加所需的导入:
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
  • [3] 然后将以下内容作为新方法添加到Activity类中onCreate()方法下面:
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;
}
  • [4]最后来完善前面添加的setMethodCallHandler()方法。您需要处理调用getBatteryLevel()以此来测试返回的值,并使用result参数返回成功和错误情况的响应。如果调用了未知的方法,请报告该方法:unknown method is called
@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();
    }
}

最后附上flutter 、Java端的完整代码

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


class MethodChannelTest extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MethodChannelTestView();
  }
}

class MethodChannelTestView extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return MethodChannelTestViewState();
  }
}

class MethodChannelTestViewState extends State  {

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

  final textColorStyle = TextStyle(color: Colors.deepOrange);

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

  // 测试从OC 端传过来的字符串
  String _messageFromOC = '默认Flutter';

  // 测试从AD 端传过来的字符串
  String _messageFromAD = '默认Flutter';


  Future _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}'.";
    }

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

  // 测试Flutter 与OC 端通信
  Future _getFromOCClientMessage () async {
    String message;
    try {
      final String result = await platform.invokeMethod('getFromOCClientMessage');
      message = 'Message 来自 $result % .';
    } on PlatformException catch (e) {
      message = "Failed to get Message: '${e.message}'.";
    }

    print('我获取到从OC 传过来的值 :$message');
    setState(() {
      _messageFromOC = message;
    });
  }


  // 测试Flutter 与AD 端通信
  Future _getFromADClientMessage () async {
    String message;
    try {
      final String result = await platform.invokeMethod('getFromADClientMessage');
      message = 'Message 来自 $result % .';
    } on PlatformException catch (e) {
      message = "Failed to get Message: '${e.message}'.";
    }

    print('我获取到从AD 传过来的值 :$message');
    setState(() {
      _messageFromAD = message;
    });
  }

  void _pushSaved() {
    print('你哈后!');
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('我的收藏列表'),
        actions: [
          new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            RaisedButton(
              child: Text('Get Battery Level'),
              onPressed: _getBatteryLevel,
            ),
            Text(_batteryLevel,style: textColorStyle),

            SizedBox(
              height: 40,
            ),

            RaisedButton(
              child: Text('Get Message From OC'),
              onPressed: _getFromOCClientMessage,
            ),
            Text(_messageFromOC,style: textColorStyle,),


            SizedBox(
              height: 40,
            ),

            RaisedButton(
              child: Text('Get Message From Andriod'),
              onPressed: _getFromADClientMessage,
            ),
            Text(_messageFromAD,style: textColorStyle,),

          ],
        ),
      ),
    );
  }
}

Java 端

package com.example.hello;

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.Log;

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

  @Override
  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

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

            new MethodCallHandler() {
                public void onMethodCall(MethodCall call, Result result) {
                  String method = call.method;
                  //Log.d(method, "The onStart() event");
                  if (call.method.equals("getBatteryLevel")) {
                      int batteryLevel = getBatteryLevel();

                      if (batteryLevel != -1) {
                          result.success(batteryLevel);
                      } else {
                          result.error("UNAVAILABLE", "Battery level not available.", null);
                      }
                  } else if (call.method.equals("getFromADClientMessage"))  {

                      String message = getFromADClientMessage();
                      result.success(message) ;

                  } else {
                      result.notImplemented();
                  }
              }
            });

      GeneratedPluginRegistrant.registerWith(this);

  }

    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;
    }

    private String getFromADClientMessage() {
        return "我来自Android";
    }
}

效果如图:

Flutter与原生OC、Java的交互通信大结局

完成了Flutter与原生OC、Java的交互通信!???!

你可能感兴趣的:(Flutter开发教程)