Open10
FlutterのPluginをほかのPlugin のMethodCallで使用した場合にMissingPluginExceptionなってしまう(Android Only)
Androidのshared_preferencesに値を入れようとしたときに MissingPluginExceptionになってしまった
2.2 2.3はだめらしい
2.0.6とかで試してみる
2.0.6もだめ
releaseビルドだと動かないみたいな状態もあったっぽいけど、今回はDebugビルドなので、それではなさそう…
Releaseビルドの場合に動かなかった件は、proguardの問題だったっぽい。
background processesでmethod callした場合、その中で他のPluginを使うとMissingPluginExceptionが出てしまう
普通はGeneratedPluginRegistrant.registerで解決できる用になったはずなのに…
確認する
Alarm ManagerのReadmeにもAndroid Embedding V2 (Flutter Version >= 1.12)であれはAndroidManifestに meta追加するだけで行けるって書いてあるんだよな…
どこが原因かいろいろ調査するため、alarm_managerでもテストしてみた
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
void printHello() {
final DateTime now = DateTime.now();
final int isolateId = Isolate.current.hashCode;
print("[$now] Hello, world! isolate=${isolateId} function='$printHello'");
SharedPreferences prefs = await SharedPreferences.getInstance();
}
main() async {
final int helloAlarmID = 0;
await AndroidAlarmManager.initialize();
runApp(...);
await AndroidAlarmManager.periodic(const Duration(minutes: 1), helloAlarmID, printHello);
}
いまの自分のコードにこれを足すとだめ
alarm_manager にあるexampleに追加してみたらMissingPluginにならなかった!
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:isolate';
import 'dart:math';
import 'dart:ui';
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// The [SharedPreferences] key to access the alarm fire count.
const String countKey = 'count';
/// The name associated with the UI isolate's [SendPort].
const String isolateName = 'isolate';
/// A port used to communicate from a background isolate to the UI isolate.
final ReceivePort port = ReceivePort();
/// Global [SharedPreferences] object.
SharedPreferences prefs;
Future<void> main() async {
// TODO(bkonyi): uncomment
WidgetsFlutterBinding.ensureInitialized();
// Register the UI isolate's SendPort to allow for communication from the
// background isolate.
IsolateNameServer.registerPortWithName(
port.sendPort,
isolateName,
);
prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey(countKey)) {
await prefs.setInt(countKey, 0);
}
await runZonedGuarded(() async {
runApp(AlarmManagerExampleApp());
await AndroidAlarmManager.initialize();
await AndroidAlarmManager.periodic(
Duration(seconds: 15), 1000, alarmHandler);
}, (error, stackTrace) {
print(error.toString());
});
}
void alarmHandler() async {
print('alarmHandler');
final pref = await SharedPreferences.getInstance();
final count = pref.getInt(countKey);
print('count: $count');
}
/// Example app for Espresso plugin.
class AlarmManagerExampleApp extends StatelessWidget {
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: _AlarmHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class _AlarmHomePage extends StatefulWidget {
_AlarmHomePage({Key key, this.title}) : super(key: key);
final String title;
_AlarmHomePageState createState() => _AlarmHomePageState();
}
class _AlarmHomePageState extends State<_AlarmHomePage> {
int _counter = 0;
void initState() {
super.initState();
AndroidAlarmManager.initialize();
// Register for events from the background isolate. These messages will
// always coincide with an alarm firing.
port.listen((_) async => await _incrementCounter());
}
Future<void> _incrementCounter() async {
print('Increment counter!');
// Ensure we've loaded the updated count from the background isolate.
await prefs.reload();
setState(() {
_counter++;
});
}
// The background
static SendPort uiSendPort;
// The callback for our alarm
static Future<void> callback() async {
print('Alarm fired!');
// Get the previous cached count and increment it.
final prefs = await SharedPreferences.getInstance();
final currentCount = prefs.getInt(countKey);
await prefs.setInt(countKey, currentCount + 1);
// This will be null if we're running in the background.
uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
uiSendPort?.send(null);
}
Widget build(BuildContext context) {
final textStyle = Theme.of(context).textTheme.headline4;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Alarm fired $_counter times',
style: textStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Total alarms fired: ',
style: textStyle,
),
Text(
prefs.getInt(countKey).toString(),
key: ValueKey('BackgroundCountText'),
style: textStyle,
),
],
),
ElevatedButton(
key: ValueKey('RegisterOneShotAlarm'),
onPressed: () async {
await AndroidAlarmManager.oneShot(
const Duration(seconds: 5),
// Ensure we have a unique alarm ID.
Random().nextInt(pow(2, 31)),
callback,
exact: true,
wakeup: true,
);
},
child: Text(
'Schedule OneShot Alarm',
),
),
],
),
),
);
}
}
GeneratedPluginRegistrant.registerWithで実行されているPluginの中でエラーを吐いているプラグインがあり、それのせいでregisterができなかったのが原因だった
あえて下記の処理を追加して、この処理でエラーを吐いているプラグインを特定、そのプラグインを使わないようにしたらMissingPluginExceptionも出なくなったし、処理ができるようになった。
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine) // add this line
}
}