• 【Flutter】混合开发之Flutter预加载解决第一次加载页面缓慢问题


    NativeFlutter混合开发,通过FlutterFragment加载Flutter页面,但Flutter页面第一次加载时非常缓慢,可以通过Flutter预加载的方式来减少第一次加载的耗时。

    预备知识

    1. 一个Native进程只有一个DartVM
    2. 第一个FlutterEngine初始化时,会创建并初始化DartVM
    3. 一个DartVM可以有多个FlutterEngine,每个FlutterEngine都运行在自己的Isolate中,他们的内存数据不共享,需要通过Isolate事先设置的port(顶级函数)通讯。

    实现方式Flutter页面第一次调用时,会初始化Flutter相关的东西,比如FlutterEngineDartVM等等,所以可以提前初始化Flutter相关的东西来达到减少第一次启动的耗时:

    • 提前预加载
    • 全局使用同一个FlutterEngine

    App 每个进程中创建第一个 FlutterEngine 实例的时候会加载 Flutter 引擎的原生库并启动 Dart VM(VM 存活生命周期跟随进程),随后同进程中其他的 FlutterEngine 将在同一个 VM 实例上运行,而第一次启动耗时主要花费在加载 Flutter 引擎的原生库和启动 Dart VM。所以可以提前初始化一个FlutterEngine来减少第一次加载时的耗时。

    1. 提前预加载

    1.1 FlutterHelper

    FlutterHelper主要用于预加载FlutterEngine,初始化FlutterEngine,获取FlutterEngine。预加载时,它会在Handler空闲时对FlutterEngine初始化。

    /**
     * Created by : yds
     * Time: 2022-07-27 10:02
     */
    
    import android.content.Context;
    import android.os.Looper;
    import android.os.MessageQueue;
    
    import io.flutter.embedding.engine.FlutterEngine;
    import io.flutter.embedding.engine.FlutterEngineCache;
    import io.flutter.embedding.engine.dart.DartExecutor;
    import io.flutter.embedding.engine.loader.FlutterLoader;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    
    public class FlutterHelper {
    
        private FlutterHelper() {
        }
    
        //FlutterEngine缓存的key
        public static final String FLUTTER_ENGINE = "flutter_engine";
    
        //flutter初始化成功的消息
        public static final String FLUTTER_ENGINE_INIT_FINISH = "flutter_engine_init_finish";
    
        private static volatile FlutterHelper instance;
    
        public static FlutterHelper getInstance() {
            if (instance == null) {
                synchronized (FlutterHelper.class) {
                    if (instance == null) {
                        instance = new FlutterHelper();
                    }
                }
            }
            return instance;
        }
    
        public void preloadFlutterEngine(Context context) {
            Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
                @Override
                public boolean queueIdle() {
                    initFlutterEngine(context);
                    return true;
                }
            });
        }
    
        public synchronized FlutterEngine initFlutterEngine(Context context){
            if (!FlutterEngineCache.getInstance().contains(FLUTTER_ENGINE)) {
            //这里建议用FlutterEngineGroup来创建FlutterEngine
                FlutterEngine engine = new FlutterEngine(context.getApplicationContext());
                System.out.println("flutterEngine:"+engine);
                GeneratedPluginRegistrant.registerWith(engine);
                //Channel 注册要紧跟引擎初始化之后,否则会有在dart中调用 Channel 因为还未初始化完成而导致的时序问题
                //FlutterBridge用于将Channel封装,统一提供服务
                FlutterBridge.init(engine);
                engine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
                FlutterEngineCache.getInstance().put(FLUTTER_ENGINE, engine);
                return engine;
            } else {
                return getFlutterEngine();
            }
        }
    
        public FlutterEngine getFlutterEngine(){
            if (isInitFinish()) {
                return FlutterEngineCache.getInstance().get(FLUTTER_ENGINE);
            } else {
                try {
                    throw new Exception("请先初始化 FlutterEngine!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        public boolean isInitFinish() {
            return FlutterEngineCache.getInstance().get(FLUTTER_ENGINE) != null;
        }
    
    }
    
    
    • 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
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    1.2 FlutterBridge

    FlutterBridge主要用于管理与Flutter通信的Channel

    /**
     * Created by : yds
     * Time: 2022-07-27 10:13
     */
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import io.flutter.embedding.engine.FlutterEngine;
    import io.flutter.plugin.common.BasicMessageChannel;
    import io.flutter.plugin.common.MethodCall;
    import io.flutter.plugin.common.MethodChannel;
    import io.flutter.plugin.common.StandardMessageCodec;
    
    public class FlutterBridge implements  MethodChannel.MethodCallHandler, BasicMessageChannel.MessageHandler<Object> {
    
        private static final String FLUTTER_MESSAGE_CHANNEL = "message_channel";
        private static final String FLUTTER_SEARCH_DATA_CHANNEL = "searchflutterfragment/searchscandata";
    
        private static volatile FlutterBridge instance;
    
        private static List<MethodChannel> mMethodChannels = new ArrayList<>();
    
        public static BasicMessageChannel<Object> mChannel;
    
        private FlutterBridge() {
        }
    
        public static FlutterBridge getInstance() {
            if (instance == null) {
                synchronized (FlutterBridge.class) {
                    if (instance == null) {
                        instance = new FlutterBridge();
                    }
                }
            }
            return instance;
        }
    
    
        public static FlutterBridge init(FlutterEngine flutterEngine) {
            MethodChannel channel = new MethodChannel(flutterEngine.getDartExecutor(), FLUTTER_MESSAGE_CHANNEL);
            channel.setMethodCallHandler(getInstance());
            mChannel = new BasicMessageChannel<>(
                    flutterEngine.getDartExecutor().getBinaryMessenger(), FLUTTER_SEARCH_DATA_CHANNEL,
                    StandardMessageCodec.INSTANCE
            );
            mChannel.setMessageHandler(getInstance());
            mMethodChannels.add(channel);
            return getInstance();
        }
    
        public static <T> void  send(@Nullable T message,@Nullable final BasicMessageChannel.Reply<Object> callback){
            mChannel.send(message,callback);
        }
    
        /**
         * MethodChannel回调
         * 

    * 用于接收从flutter向native发送消息 * * @param call * @param result */ @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { } /** * BasicMessageChannel回调 *

    * 用于接收从flutter向native发送消息 * * @param message * @param reply */ @Override public void onMessage(@Nullable Object message, @NonNull BasicMessageChannel.Reply reply) { } }

    • 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
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    1.3 使用

    • 首先在Application中进行预加载
    public class MyApplication extends Application {
    	public void onCreate() {
    		super.onCreate();
    		FlutterHelper.getInstance().preloadFlutterEngine(this);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 全局使用同一个FlutterEngine

    • FlutterHelper预加载时会创建一个FlutterEngine,然后会将FlutterEngine存储在FlutterEngineCache中。
    • FlutterFragment通过withCachedEngine获取FlutterHelper创建的FlutterEngine,并通过build方法创建FlutterFragment
    public class SearchFragment extends Fragment  implements OnScanDataListener {
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            initFlutterEngine();
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.search_fragment_layout,container,false);
        }
    
        private void initFlutterEngine(){
            FlutterFragment flutterFragment = FlutterFragment.withCachedEngine(FlutterHelper.FLUTTER_ENGINE).build();
            getChildFragmentManager().beginTransaction().replace(R.id.container,flutterFragment).commit();
        }
    
    
        @Override
        public void onScanData(String data) {
            FlutterBridge.send(data,new BasicMessageChannel.Reply<Object>() {
                @Override
                public void reply(@Nullable Object reply) {
                    if (reply != null) {
                        long endTime = System.currentTimeMillis();
                        System.out.println("endTime:" + endTime);
                        System.out.println("onScanData:" + reply.toString());
                    }
                }
            });
        }
    }
    
    • 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
  • 相关阅读:
    【CV知识点汇总与解析】| 正则化篇
    ES6解析赋值
    Luatos Air700 改变BL0942串口波特率
    解决JSON传参报错问题
    文件操作【c语言】
    C++数据结构补充(双向链表)
    SWUST OJ#99 欧几里得博弈
    CF1201C最大中位数题解
    Nginx负载均衡详解
    这82道 Spring Boot 面试题都答不上来?如何硬钢面试官
  • 原文地址:https://blog.csdn.net/u013293125/article/details/126019202