Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
172 views
in Technique[技术] by (71.8m points)

Flutter module add-to-app调用Android原生抛MissingPluginException?

问题描述

创建的Flutter module按照撰写双端平台代码添加到host App中,并且通过MethodChannel调用host App 原生方法失败,抛MissingPluginException异常:

No implementation found for method testMethod on channel flutter.bridge.call_platform

问题出现的环境背景及自己尝试过哪些方法

  • 为了排查问题,我特别使用现有flutter版本(1.14.6)新建了hello world的flutter module,也新建了一个helloworld Android app,再按add-to-app步骤添加依赖也一样存在此问题。因此可能不是1.12之后的plugin兼容问题(Supporting the new Android plugins APIs)。Flutter版本信息:

    $ flutter --version
    Flutter 1.14.6 ? channel beta ? https://github.com/flutter/flutter.git
    Framework ? revision fabeb2a16f (10 weeks ago) ? 2020-01-28 07:56:51 -0800
    Engine ? revision c4229bfbba
    Tools ? Dart 2.8.0 (build 2.8.0-dev.5.0 fc3af737c7)
  • 在full-flutter测试项目中同样的写法可正常调起
  • 在创建的Flutter module中,.android下写平台原生方法也是可正常调起
  • 但按"将 Flutter module 集成到 Android 项目",无论是使用aar,还是直接源码依赖,均会有这个问题。
  • 大致调用顺序:host App的点击事件,使用以下方式打开flutter模块,然后,在Flutter触发调用回host app的方法:

    startActivity(FlutterActivity.createDefaultIntent(this));

相关代码

  • host App的点击事件中打开flutter模块
package com.peter.myapplication;
import io.flutter.embedding.android.FlutterActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.tv_test).setOnClickListener((view)->
                startActivity(FlutterActivity.createDefaultIntent(this)));
    }
}
  • 在Flutter模块中点击回调中添加host app方法调用
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ///通过平台通道调用平台原生方法的通道名
  static const bridgeNameToNative = 'flutter.bridge.call_platform';
  static const MethodChannel _toPlatform = MethodChannel(bridgeNameToNative);

  String _callRst = '点击按钮触发获取';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Fluuter:测试页面'),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(16),
          child: Column(
            children: <Widget>[
              RaisedButton(
                child: Text('call platform'),
                onPressed: () {
                 ////////////////////////////
                 //点击回调中添加触发调用平台原生方法 
                 ////////////////////////////
                 _toPlatform.invokeMethod('testMethod').then((value) {
                    print('---> testMethod = $value');
                    setState(() => _callRst = value);
                  }).catchError((e) {
                    print('---> ' + e.message);
                    setState(() => _callRst = e.message);
                  });
                },
              ),
              Text('call platform rst:
' + _callRst)
            ],
          ),
        ),
      ),
    );
  }
}
  • 在host App中处理此请求的原生代码如下:
package com.peter.myapplication;

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

public class BridgeActivity extends FlutterActivity {
    private static final String CHANNEL_NAME = "flutter.bridge.call_platform";

    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL_NAME)
        .setMethodCallHandler((call, result) -> {
            switch (call.method){
                case "testMethod":
                    result.success("从平台原生方法返回的值");
                    break;
                default:
                    result.notImplemented();
                    break;
            }
        });
    }
}
  • host App中AndroidManifest.xml的相关配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.peter.myapplication">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="io.flutter.embedding.android.FlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:theme="@style/AppTheme"
            android:windowSoftInputMode="adjustResize" />
        <activity
            android:name=".BridgeActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:theme="@style/AppTheme"
            android:windowSoftInputMode="adjustResize" />
    </application>

</manifest>

你期待的结果是什么?实际看到的错误信息又是什么?

理论上,应该在host app中onClick事件跳转到Flutter模块时,能打印出由Flutter模块发起、从host app返回的文本信息"从平台原生方法返回的值",但实际上却如开头所述,此时抛MissingPluginException异常。


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
2020-4-6:人肉顶贴,期待各位大佬

是不是我使用的方式不符合最佳实践?为什么这个问题似乎没有人遇到?add-to-app按理应该还是有很广的应用场景的……

后来看了下FlutterActivity内部实现逻辑,算是搞清楚为什么了,应该是Flutter sdk的bug:
需要在

FlutterActivity.createDefaultIntent(this))

创建出来的intent中重新指向到子类BridgeActivity,具体的整理了篇分析文章,感兴趣的伙伴欢迎查阅讨论:
Add-to-app的MethodCallHandler不生效抛MissingPluginException解决


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...