此篇可随意转载!Update 2020.10.27

前言

经过上一章的学习,大家对flutter plugin开发有了基本认知。但是大家要清醒的认识到自定义插件开发涉及基础知识较多,因此本节我帮助大家有针对性的补充涉及到的基础知识。

Flutter架构

我们从上往下看,找找我们需要补全的知识。

1. Framework/dart层

我们在这一层经常使用async和await来实现异步UI/数据流,比如上一章的这段代码:

  Future<void> initPlatformState() async {
    Map<String, dynamic> deviceData;

    try {
      if (Platform.isAndroid) {
        deviceData = _readAndroidBuildData(await deviceInfoPlugin.androidInfo);
      } else if (Platform.isIOS) {
        deviceData = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo);
      }
    } on PlatformException {
      deviceData = <String, dynamic>{
        'Error:': 'Failed to get platform version.'
      };
    }

    if (!mounted) return;

    setState(() {
      _deviceData = deviceData;
    });
  }

注意: 这里的async、await是dart语法糖,符合ES6标准写法。可以跟Future,Promise配合使用。但是它在dart语言内部是用协程实现的,通过CPU中断–>保留上下文–>在新上下文中执行代码–>返回保留上下文 完成异步功能。重要的是:它并没有新建线程,这点和Java是很不同的。

2. Flutter Engine

上面我们知道了在flutter中做异步UI/数据流的一般方法。假如有个需求要定时和Android通讯,那应该怎么弄呢?

在Android和iOS中,Embedder层中会为每个app实例创建UI、GPU、IO 三个Runner。而所有实例共享Platform Runner。这些Runner的引用会被FlutterEngine对象保存起来。

我们的代码默认运行在DartVM 的Root isolate,它通过Engine内的引用跟UI、GPU、IO、platform四个底层Runner交互。当我们需要定时去跟系统通讯的时候,考虑到阻塞问题,不能直接在Root isolate中操作,应该在DartVM中建立自己的isolate。比如下面这个例子:

import 'dart:async';
import 'dart:isolate';

main(List<String> args) {
  start();

  print("start");
}

Isolate isolate;

int i = 1;

void start() async {
  //接收消息的主Isolate的端口
  final receive = ReceivePort();

  isolate = await Isolate.spawn(runTimer, receive.sendPort);

  receive.listen((data) {
    print("Reveived : $data ; i :$i");
  });
}

void runTimer(SendPort port) {
  int counter = 0;
  Timer.periodic(const Duration(seconds: 1), (_) {
    counter++;
    i++;
    final msg = "Notification $counter";
    print("Send :$msg ;i :$i");
    port.send(msg);
  });
}

3. Flutter的启动过程

flutter的启动过程主要有2大步骤(以目前新版1.12.13为例):

1.FlutterApplication启动,这一步对应AndroidManifest.xml里面的application标签。可以在Android Studio里面一步步跟踪它的启动过程。它的主要作用是加载flutter.so、加载Assets资源。

2. FlutterActivity启动,对应AndroidManifest.xml里面的activity标签。它主要作用是启动View,启动Flutter Engine,启动dart VM并关联底层Runner。

Flutter升级(1.12+)

注意:务必详细阅读此文档,因为你看到的教程可能在使用旧的实现。需要仔细看这篇升级的issue讨论,flutter开发者阐述了一些flutter升级的可能的问题和解决思路。

Flutter Null Safety升级

Flutter新的类架构图

可以看到Null类型已经不属于其他类型了。所以,给一个List对象传递一个Null是会报类型错误的。看看引入了Null Safety约束后编码有什么不同:

class A {
  String? firstName; //可空的成员变量
  int getNameLen(String? lastName /*可空的参数*/) {
    int firstLen = firstName?.length ?? 0;
    int lastLen = lastName?.length ?? 0;
    return firstLen + lastLen;
  }
}

在变量声明,形参,访问的时候都需要带上”?”,表示这个变量是可以为空的。

如果要让变量不为空,要像如下声明:

class B {
  List names=[];//定义时初始化
  final List colors;//在构造方法中初始化
  late List urls;//延时初始化
  CommonModel(this.colors);
  ...

定义时要么直接初始化,要么在构造函数初始化,要么声明为“late”。比如flutter的初始化函数“initState”。

解决Flutter依赖国外maven仓库问题

网上普遍说换掉google()、jcenter() 改为国内镜像仓库,如下:

    repositories {
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
    }

但是,如果你的项目用了第三方plugins,它们内部会依赖的国外的maven仓库。因此我们只能使用proxy去访问海外仓库,在项目根目录gradle.properties中增加以下配置:

org.gradle.jvmargs=-Xmx1536M -DsocksProxyHost=xx.xx.xx.xx -DsocksProxyPort=1080 
systemProp.socks.proxyHost=xx.xx.xx.xx
systemProp.socks.proxyPort=1080 

解决Gradle版本号问题

一共有2种方法

  1. 对齐android/build.gradle 和android/gradle/wrapper/gradle-wrapper.properties 中的gradle版本和插件版本号
  2. 重新建一个项目,Android Studio 安装后它们的版本是默认对齐的。
  3. 最好升级到最新的版本,避免warning。

Flutter提速的本地修改

git: M	packages/flutter_tools/gradle/flutter.gradle
git: M	packages/flutter_tools/gradle/resolve_dependencies.gradle
git: M	packages/flutter_tools/lib/src/http_host_validator.dart