当前位置:   article > 正文

2024最新Flutter面试题分享(含答案解析)_flutter 面试题

flutter 面试题

Dart部分

String扩展一个方法
  1. 使用关键字extension ... on为String定义一个扩展类
  2. 在为扩展类添加一个 新的方法
  3. String类型对象调用这个扩展的方法
extension StringExt on String{
  // 2. 扩展方法
  int add(int x, int y){
    return x + y;
  }
}

void mian(){
  String str = 'hello';
  // 3. 使用扩展类方法
  int result = str.add(3, 7);
  debugPrint(result.toString());
}

// 单元测试
import 'package:flutter_start/extTest.dart';
import 'package:flutter_test/flutter_test.dart';
void main(){
  test('StringExt', (){
    String ext = 'ext';
    expect(ext.add(3, 7), 10);
  });
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

dart是单继承还是多继承

单继承

dart如何达到多继承的效果?

Dart中使用Mixins,可以达到多继承的效果

mixin混入有什么特点
  1. 作为mixins的类只能继承自Object,不能继承其他类
  2. 作为mixins的类不能有构造函数
  3. 一个类可以mixins多个mixins类
  4. mixins绝不是继承,也不是接口,而是一种全新的特性
// 类D 继承A和B   关键字 with
class D  extends A with B{

}

// mixin 的使用
class A {
  String info="this is A";
  void printA(){
    print("A");
  }
  void run(){
    print("A Run");
  }
}
class B {  
  void printB(){
    print("B");
  }
  void run(){
    print("B Run");
  }
}
class C extends Person with B,A{
  C(String name, num age) : super(name, age);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
混入相同方法的多个混入,最终会执行哪一个?

后面的类中的方法将前面的类中相同的方法覆盖

dart运行机制是什么样的?

消息循环机制

  1. 两个队列,微任务队列和事件队列。
  2. microtask queue 的优先级高于event queue。
  3. 在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。
如何向事件队列插入任务?

Future就是将任务插入到事件队列

向微任务队列插入任务

Future.microtask()
scheduleMicrotask()
Stream中的执行异步的模式就是scheduleMicrotask。因为microtask的优先级又高于event。所以,如果 microtask 太多就可能会对触摸、绘制等外部事件造成阻塞卡顿

Future和Stream有什么区别?
  1. Future中的任务会加入下一轮事件循环,而Stream中的任务则是加入微任务队列
  2. Future 用于表示单个运算的结果,而 Stream 则表示多个结果的序列。

Stream 有同步流和异步流之分。它们的区别在于同步流会在执行 add,addError 或 close 方法时立即向流的监听器 StreamSubscription 发送事件,而异步流总是在事件队列中的代码执行完成后在发送事件。`

Stream订阅模式有哪几种?

Stream分为Single Subscription和Broadcast两种类型, 前者只允许订阅(listen)一次,后者允许多次订阅。

单订阅在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。
广播订阅可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在。

Stream单订阅,多次订阅会出现什么结果?

会报错,单订阅只能有一次订阅.
即使取消了第一个监听器,也不允许在单订阅流上设置其他的监听器。

Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。
通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

dart是单线程还是多线程?

Dart是单线程模型

Dart是如何实现多任务并行的

主要依赖dart的并发编程(Isolate)、异步和事件驱动机制

Isolate
  • 在dart中,一个Isolate对象其实就是一个Isolate执行环境的引用,一般来说我们都是通过当前的Isolate去控制其他的Isolate完成彼此之间的交互,而当我们想要创建一个新的Isolate可以使用Isolate.spawn方法获取一个新的Isolate对象,两个Isolate之间使用SendPort相互发送消息,而Isolate中也存在了一个与之对应ReceivePort接收消息用来处理,但是我们需要注意的是SendPortReceivePort在每一个Isolate都有一对,只有同一个Isolate中的ReceivePort才能接受当前类的SendPort发送的消息并且处理。
  • Isolate可以把它理解为Dart中的线程。但它又不同于线程,更恰当的说应该是微线程。它与线程最大的区别就是不能共享内存,因此也不存在锁竞争问题,两个Isolate完全是两条独立的执行线,且每个Isolate都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。

  void initState() async {
    super.initState();
    // 主Isolate的ReceivePort
    ReceivePort receivePort = ReceivePort();
    SendPort? otherSendPort;
    // 主Isolate接收到子Isolate中由主Isolate的SendPort发送过来的消息
    receivePort.listen((message) {
      if(message is SendPort){
        otherSendPort = message;
      }else{
        // 处理消息
        // ......
        
        // 子Isolate的SendPort在主Isolate中向子Isolate发送消息
       otherSendPort?.send('我是来自主Isolate的消息');
      }
    });

    // 创建子Isolate
    Isolate isolate = await Isolate.spawn((message) {
      // message 是主Isolate的SendPort

      // 在子Isolate中创建一个新的ReceivePort
      ReceivePort recPort = ReceivePort();
      // 主Isolate的SendPort
      SendPort? mainSendPort;
      
      // 运用主Isolate的SendPort将子Isolate的SendPort发送给主Isolate
      message.send(recPort.sendPort);
      // 子Isolate监听接收到主Isolate那边发送的消息 谁发送?子Isolate的SendPort
      recPort.listen((msg) {
        if(msg is SendPort){
          mainSendPort = msg;
        }else{
          // 主Isolate的SendPort向主Isolate发送消息
            mainSendPort?.send('我是来自子Isolate的消息');
        }
      });
    }, receivePort.sendPort);// 参数二 将主Isolate的SendPort传递给子Isolate
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
await for如何使用?

await for是用来不断获取stream流中的数据,然后执行循环体中的操作。它一般用在直到stream什么时候完成,并且必须等待传递完成后才能使用,不然会阻塞。

Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3','4']);
main() async{
    await for(String s in stream){
    print(s);
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
如何实现websocket稳定连接?
  • 定期发送心跳包,一般1秒一次
  • 捕获关闭连接事件并重连websocket
  • 实现断线重连

Flutter部分

A、B两个组件在setState前修改背景颜色,是否会修改成功?

Flutter 渲染流程是什么?(GPU)

Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。

Widget、Element、RenderObject三者关系是什么?
简述

widget是用于描述Element配置信息的,flutter中一切都是widget,尺寸、颜色、组件等都是widget
element是widget树上特定位置的实例
renderobject是渲染上的一个对象

依赖关系:Element树依赖Widget树,渲染树依赖Element树,最终的UI树是由独立的Element节点构成。

一个widget会创建一个element
一个element持有一个widget和render object,element
会对比widget的变化,将那写需要更新和重建的widget,同步到render object树,以最小的开销来渲染

一、它们是什么?
  • Widget:对一个Element配置的描述,刷新的过程中随时会重建。(不参与真正的渲染,widget的属性是不可以改变的,要想改变只能重新创建一个widget对象)
  • Element:表示一个Widget树中特定位置的实例,用于对比widget,找出需要更新和重建的widget,更新Element树和RenderObject树。
  • RenderObject:渲染树上的一个对象,用于界面的布局和绘制,负责真正的渲染,实例化一个 RenderObject 是非常耗能。
二、关系
  • 一个Widget会创建一个Element对象,是通过createElement()创建的。
  • 一个Element持有一个RenderObject和一个Widget。Widget 、Element都不负责最后的渲染,真正负责的渲染只有 RenderObject。
  • Widget 具有不可变性,但 Element 却是可变的。Element 树将 Widget 树的变化做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。
flutter生命周期,setstate会执行哪些生命周期?

setState()执行后,会执行build()

  • app的状态:AppLifecycleState
    inactive:活跃可见
    paused:关闭或者切换到后台时,不可见的状态
    hidden:后台运行状态
    resumed:切回到前台可见状态
    detached:关闭状态

  • Flutter SDK 3.13 之前的方式: with WidgetsBindingObserver
    initState()中注册 WidgetsBinding.instance.addObserver(this);
    dispose()移除 WidgetsBinding.instance.removeObserver(this);
    didChangeAppLifecycleState()回调中,检测app的状态

  • Flutter SDK 3.13 之后的方式:AppLifecycleListener

late final AppLifecycleListener _listener;

  void initState() {
    super.initState();
    // Initialize the AppLifecycleListener class and pass callbacks
    _listener = AppLifecycleListener(
      onStateChange: _onStateChanged,
    );
  }
  
  void dispose() {
    // Do not forget to dispose the listener
    _listener.dispose();
    super.dispose();
  }
  // Listen to the app lifecycle state changes
  void _onStateChanged(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.detached:
        _onDetached();
      case AppLifecycleState.resumed:
        _onResumed();
      case AppLifecycleState.inactive:
        _onInactive();
      case AppLifecycleState.hidden:
        _onHidden();
      case AppLifecycleState.paused:
        _onPaused();
    }
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
StatefulWidget 的生命周期方法

createState:可以调用多次

State 对象的生命周期方法
class _MyWidgetState extends State<MyWidget> {
  
  void initState() {
    super.initState();
    // 在 State 对象被插入树中时调用,这个方法只会被调用一次。
  }
  
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 在 initState 之后调用,表示 State 对象的依赖关系发生变化。
  }
  
  Widget build(BuildContext context) {
    // 在此构建 Widget 树
    return Container();
  }
  
  void didUpdateWidget(MyWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当父 Widget 重建时调用。
    // 如果父 Widget 重建时需要重新配置子 Widget,则会调用此方法。
  }
  
  void deactivate() {
    // 在此处理 State 对象从树中被移除的操作
    super.deactivate();
  }
  
  void activate() { 
      // 在此处理 State 对象被重新插入树里
    super.activate();
  }
  
  void dispose() {
    // 当 State 对象被永久从树中移除时调用
    super.dispose();
  }
}

// 打开页面执行:initState、didChangeDependencies
// 关闭页面执行:deactivate、dispose


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
StatelessWidget 的生命周期方法
class MyWidget extends StatefulWidget {
  
  Widget build(BuildContext context) {
    return Widget;
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
setState()执行做了什么事?

setState()过程主要工作是记录所有的脏元素,会引起build函数执行,更新widget树、更新Element树和RenderObject树,最后重新渲染。

flutter中的key?
  • 作用:比较两个Widget是不是同一个Widget
  • 分类:LocaleKey、GlobalKey
  • LocaleKey:ValueKey、ObjectKey、UniqueKey。
statelesswidget和statefullwidget有什么区别?

StatelessWidget 没有要管理的内部状态.
无状态widget的build方法通常只会在以下三种情况调用:

  • 将widget插入树中时
  • 当widget的父级更改其配置时
  • 当它依赖的InheritedWidget发生变化时

StatefullWidget是可变状态的widget。 使用setState方法管理StatefulWidget的状态的改变。调用setState告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法,以便应用程序可以应用最新状态。

在有状态类中编写一个按钮调用初始化生命周期(initState)方法,会发生什么?

报错,但不影响布局;会报生命周期创建错误;

如何获取控件的大小和位置?
  1. 使用Key拿到上下文取得findRenderObject拿内容的尺寸数据;
  2. 使用context取得findRenderObject拿内容的尺寸数据;
Flutter 是如何与原生Android、iOS进行通信的?

PlatformChannel
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。

// 1. 创建java类。  实现FlutterPlugin和MethodCallHandler
 public class MsaOaidPlugin implements FlutterPlugin, MethodCallHandler{
   
  public void onAttachedToEngine( FlutterPluginBinding flutterPluginBinding) {
    Log.e("---------","==========onAttachedToEngine");
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "msa_oaid");
    channel.setMethodCallHandler(this);
    this.context = flutterPluginBinding.getApplicationContext();
    System.loadLibrary("msaoaidsec");

  }
  
  public void onMethodCall( MethodCall call,  Result result) {
    Log.e("---------","==========onMethodCall");
    if(call.method.equals("isSupport")){
      new DemoHelper().getDeviceIds(context, new IIdentifierListener() {
        
        public void onSupport(IdSupplier idSupplier) {
          result.success(idSupplier.isSupported());
        }
      });
    }else if(call.method.equals("getOaid")){
      new DemoHelper().getDeviceIds(context, new IIdentifierListener() {
        
        public void onSupport(IdSupplier idSupplier) {
          result.success(idSupplier.getOAID());
        }
      });

    }else{
      result.notImplemented();
    }

  }

  
  public void onDetachedFromEngine( FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
    Log.e("---------","==========onDetachedFromEngine");
  }
 }


// 2. flutter lib包下创建dart类

class MsaOaid {
  static const MethodChannel _channel = MethodChannel('msa_oaid');

  static Future<bool>  isSupport() async {
    final bool support = await _channel.invokeMethod('isSupport');
    return support;
  }

  static Future<String?> getOaid() async {
    final String? oaid = await _channel.invokeMethod('getOaid');
    return oaid;
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

最后

我整理了一套Android面试题合集,除了以上面试题,还包含【Java 基础、集合、多线程、虚拟机、反射、泛型、并发编程、Android四大组件、异步任务和消息机制、UI绘制、性能调优、SDN、第三方框架、设计模式、Kotlin、计算机网络、系统启动流程、Dart、Flutter、算法和数据结构、NDK、H.264、H.265.音频编解码、FFmpeg、OpenMax、OpenCV、OpenGL ES
在这里插入图片描述

有需要的朋友可以扫描下方二维码,免费领取全部面试题+答案解析!!!

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号