友盟崩溃日志报了这样一个问题,说是扫描蓝牙设备回调时 MethodChannel 为空:
java.lang.NullPointerException: Attempt to invoke virtual method 'void io.flutter.plugin.common.MethodChannel.invokeMethod(java.lang.String, java.lang.Object)' on a null object reference
at com.pauldemarco.flutter_blue.FlutterBluePlugin$5.run(SourceFile:1)
at android.app.Activity.runOnUiThread(Activity.java:7152)
at com.pauldemarco.flutter_blue.FlutterBluePlugin.invokeMethodUIThread(SourceFile:1)
at com.pauldemarco.flutter_blue.FlutterBluePlugin.access$400(SourceFile:1)
at com.pauldemarco.flutter_blue.FlutterBluePlugin$2.onScanResult(SourceFile:6)
at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1.run(BluetoothLeScanner.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7896)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
理了一遍,发现 flutter_blue 的 MethodChannel 会在 MainActivity 的 onCreate 中初始化,这个时候还没有开启蓝牙扫描,这就奇怪了:
[ com.pauldemarco.flutter_blue.FlutterBluePlugin ]
private void invokeMethodUIThread(final String name, final byte[] byteArray)
{
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
channel.invokeMethod(name, byteArray);
}
});
}
private void setup(
final BinaryMessenger messenger,
final Application application,
final Activity activity,
final PluginRegistry.Registrar registrar,
final ActivityPluginBinding activityBinding) {
synchronized (initializationLock) {
Log.i(TAG, "setup");
this.activity = activity;
this.application = application;
this.context = application;
channel = new MethodChannel(messenger, NAMESPACE + "/methods");
channel.setMethodCallHandler(this);
stateChannel = new EventChannel(messenger, NAMESPACE + "/state");
stateChannel.setStreamHandler(stateHandler);
mBluetoothManager = (BluetoothManager) application.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (registrar != null) {
// V1 embedding setup for activity listeners.
registrar.addRequestPermissionsResultListener(this);
} else {
// V2 embedding setup for activity listeners.
activityBinding.addRequestPermissionsResultListener(this);
}
}
}
@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
activityBinding = binding;
setup(
pluginBinding.getBinaryMessenger(),
(Application) pluginBinding.getApplicationContext(),
activityBinding.getActivity(),
null,
activityBinding);
}
所以找了一下官网 issues:https://github.com/pauldemarco/flutter_blue/issues/649
按照上面答友的方案试了一下,果然不会了:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class MainPage extends StatelessWidget {
Future<bool> _onWillPop() async {
// A little workaround for issue https://github.com/pauldemarco/flutter_blue/issues/649
if (Platform.isAndroid) {
if (await FlutterBlue.instance.isScanning.first) {
// Also close any open connections you may have.
await FlutterBlue.instance.stopScan();
// not sure if 200 milliseconds is enough on every device. Slower devices may need more time.
await Future.delayed(Duration(milliseconds: 200));
}
}
return true;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: // Your body goes here
);
}
}
通过对事件日志的分析,发现在退出应用后,再马上进来很容易复现这个问题,所以做了以下猜想:
是否有这样的可能:当第一次进入后,MethodChannel 初始化完成调用系统蓝牙扫描,这时退出,再马上进来,蓝牙还在扫描,而这一次 MethodChannel 还没初始化,就报这个错误?
补充:
线上观测后,这个问题还是会出现,特别是在华为手机,崩溃率特别高。
原先以为是升级版本带来的问题(0.7.x),那就恢复到旧版本(0.6.x),还是会有问题。
既然官方迟迟没有修复,那就自己改源码:直接修改 flutter_blue 的 Android 模块代码,在 FlutterBluePlugin.java 中发生空指针异常的地方,加上非空判断,why not ~