精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Flutter 2 Router 從入門到放棄 - 實(shí)現(xiàn)原理與源碼分析(一)

開發(fā) 前端
在上一篇文章Flutter 2 Router 從入門到放棄 - 基本使用、區(qū)別&優(yōu)勢(shì)中,主要講了多引擎混合開發(fā)的基本用法以及多引擎和單引擎混合開發(fā)的區(qū)別,本文我們主要通過源碼看看多引擎復(fù)用是如何實(shí)現(xiàn)。

[[417041]]

前言

在上一篇文章Flutter 2 Router 從入門到放棄 - 基本使用、區(qū)別&優(yōu)勢(shì)中,主要講了多引擎混合開發(fā)的基本用法以及多引擎和單引擎混合開發(fā)的區(qū)別,本文我們主要通過源碼看看多引擎復(fù)用是如何實(shí)現(xiàn)。

一、Flutter 2 源碼編譯調(diào)試

工欲善其事,必先利其器,這里我們先對(duì)源碼編譯和調(diào)試步驟進(jìn)行說明:

源碼編譯

安裝 depot_tools,配置環(huán)境變量

  1. git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 
  2. export PATH=/path/to/depot_tools:$PATH 

創(chuàng)建空的 engine 目錄并在目錄中創(chuàng)建 .gclient 配置文件,在 .gclient 中配置從 flutter/engine 主工程 fork 出的 github 工程地址,.gclient 配置如下

  1. solutions = [ 
  2.   { 
  3.     "managed"False, 
  4.     "name""src/flutter", 
  5.     "url""https://github.com/Alex0605/engine.git", 
  6.     "custom_deps": {}, 
  7.     "deps_file""DEPS", 
  8.     "safesync_url""", 
  9.   }, 

在 engine 目錄中執(zhí)行 gclient sync

切換源碼。編譯前的一個(gè)重要操作是將源碼切換到本地 Flutter SDK 的 engine version 對(duì)應(yīng)的提交點(diǎn)

  1. # 查看本地 Flutter SDK 引擎版本, 這個(gè)文件中是包含對(duì)應(yīng)的 commit id 
  2. vim src/flutter/bin/internal/engine.version 
  3.  
  4. # 調(diào)整代碼 
  5. cd engine/src/flutter 
  6. git reset --hard <commit id> 
  7. gclient sync -D --with_branch_heads --with_tags 
  8.  
  9. # 準(zhǔn)備構(gòu)建文件 
  10. cd engine/src 
  11.  
  12. #Android 
  13. # 使用以下命令生成 host_debug_unopt 編譯配置 
  14. ./flutter/tools/gn --unoptimized 
  15. # android arm (armeabi-v7a) 編譯配置 
  16. ./flutter/tools/gn --android --unoptimized 
  17. # android arm64 (armeabi-v8a) 編譯配置 
  18. ./flutter/tools/gn --android --unoptimized --runtime-mode=debug --android-cpu=arm64 
  19. # 編譯 
  20. ninja -C out/host_debug_unopt -j 16 
  21. ninja -C out/android_debug_unopt -j 16 
  22. ninja -C out/android_debug_unopt_arm64 -j 16 
  23.  
  24. #iOS 
  25. # unopt-debug 
  26. ./flutter/tools/gn --unoptimized --ios --runtime-mode debug --ios-cpu arm 
  27. ./flutter/tools/gn --unoptimized --ios --runtime-mode debug --ios-cpu arm64 
  28.  
  29. ./flutter/tools/gn --unoptimized --runtime-mode debug --ios-cpu arm 
  30. ./flutter/tools/gn --unoptimized --runtime-mode debug --ios-cpu arm64 
  31.  
  32. ninja -C out/ios_debug_unopt_arm 
  33. ninja -C out/ios_debug_unopt 
  34. ninja -C out/host_debug_unopt_arm 
  35. ninja -C out/host_debug_unopt 

編譯完成后的目錄如下:

圖片

源碼運(yùn)行調(diào)試

通過命令創(chuàng)建一個(gè) flutter 工程

flutter create --org com.wedotor.flutter source_code

用 android studio 打開創(chuàng)建的 android 工程

在 gradle.properties 文件中添加 localEngineOut 屬性,配置如下:

  1. org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 
  2. android.useAndroidX=true 
  3. android.enableJitfier=true 
  4. localEngineOut=/Users/zhoujh/myproj/3-proj/flutter/engine/src/out/android_debug_unopt_arm64 

將 engine/src/flutter/shell/platform/android 工程(稱之為* Flutter 引擎工程*)導(dǎo)入到 Android Studio

使用自定義 Flutter 引擎運(yùn)行 Flutter App(稱之為 Flutter App 工程),具體如 1-3 步所述

Flutter 引擎工程 中給源碼設(shè)置斷點(diǎn)并啟動(dòng) Debugger 連接到已啟動(dòng)的 Flutter App 進(jìn)程

PS:這里 C++ 代碼我用的是 Clion 閱讀,這里配置比較簡(jiǎn)單,將上面生成的 compile_commands.json 文件復(fù)制到 src/flutter 目錄中,然后使用 Clion 打開項(xiàng)目,indexing 之后便可以跟蹤跳轉(zhuǎn)

二、Flutter 2 源碼閱讀

進(jìn)行源碼分析之前,先了解一下官方文檔中提供的核心架構(gòu)圖,它也代表著整個(gè) Flutter 架構(gòu)。

圖片

Flutter 的架構(gòu)主要分成三層:Framework,Engine 和 Embedder。

1)、Framework:Framework 使用 dart 實(shí)現(xiàn),包括 Material Design 風(fēng)格的 Widget,Cupertino(針對(duì) iOS)風(fēng)格的 Widgets,文本/圖片/按鈕等基礎(chǔ) Widgets,渲染,動(dòng)畫,手勢(shì)等。此部分的核心代碼是:flutter 倉庫下的 flutter package,以及 sky_engine 倉庫下的 io,async ,ui (dart:ui 庫提供了 Flutter 框架和引擎之間的接口)等 package。其中 dart:ui 庫是對(duì) Engine 中 Skia 庫的 C++ 接口的綁定。向上層提供了 window、text、canvas 等通用的繪制能力,通過 dart:ui 庫就能使用 Dart 代碼操作 Skia 繪制引擎。所以我們實(shí)際上可以通過實(shí)例化 dart:ui 包中的類(例如 Canvas、Paint 等)來繪制界面。然而,除了繪制,還要考慮到協(xié)調(diào)布局和響應(yīng)觸摸等情況,這一切實(shí)現(xiàn)起來都異常麻煩,這也正是 Framework 幫我們做的事。渲染層 Rendering 是在 ::dart:ui 庫之上的第一個(gè)抽象層,它為你做了所有繁重的數(shù)學(xué)工作。為了做到這一點(diǎn),它使用 RenderObject 對(duì)象,該對(duì)象是真正繪制到屏幕上的渲染對(duì)象。由這些 RenderObject 組成的樹處理真正的布局和繪制。

2)、Engine:Engine 使用 C++ 實(shí)現(xiàn),主要包括:Skia,Dart 和 Text。Skia 是開源的二維圖形庫,提供了適用于多種軟硬件平臺(tái)的通用 API。在安卓上,系統(tǒng)自帶了 Skia,在 iOS 上,則需要 APP 打包 Skia 庫,這會(huì)導(dǎo)致 Flutter 開發(fā)的 iOS 應(yīng)用安裝包體積更大。Dart 運(yùn)行時(shí)則可以以 JIT、JIT Snapshot 或者 AOT 的模式運(yùn)行 Dart 代碼。

3)、Embedder:Embedder 是一個(gè)嵌入層,即把 Flutter 嵌入到各個(gè)平臺(tái)上去,這里做的主要工作包括渲染 Surface 設(shè)置,線程設(shè)置,以及插件等。從這里可以看出,F(xiàn)lutter 的平臺(tái)相關(guān)層很低,平臺(tái)(如 iOS)只是提供一個(gè)畫布,剩余的所有渲染相關(guān)的邏輯都在 Flutter 內(nèi)部,這就使得它具有了很好的跨端一致性。

2、啟動(dòng) app 時(shí)會(huì)在 Application onCreate 方法中創(chuàng)建 FlutterEngineGroup 對(duì)象

  1. public void onCreate() { 
  2.     super.onCreate(); 
  3.     // 創(chuàng)建 FlutterEngineGroup 對(duì)象 
  4.     engineGroup = new FlutterEngineGroup(this); 

3、在創(chuàng)建 FlutterEngineGroup 時(shí),使通過該引擎組創(chuàng)建的子引擎共享資源,比單獨(dú)通 FlutterEngine 構(gòu)造函數(shù)創(chuàng)建,創(chuàng)建速度的更快、占用內(nèi)存更少,在創(chuàng)建或重新創(chuàng)建第一個(gè)引擎時(shí),行為與通過 FlutterEngine 構(gòu)造函數(shù)創(chuàng)建相同。當(dāng)創(chuàng)建后續(xù)的引擎時(shí),會(huì)重新使用現(xiàn)有的引擎中的資源。共享資源會(huì)一直保留到最后一個(gè)引擎被銷毀。刪除 FlutterEngineGroup 不會(huì)使其現(xiàn)有的已創(chuàng)建引擎失效,但它無法再創(chuàng)建更多的 FlutterEngine。

  1. //src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java 
  2. public FlutterEngineGroup(@NonNull Context context, @Nullable String[] dartVmArgs) { 
  3.   FlutterLoader loader = FlutterInjector.instance().flutterLoader(); 
  4.   if (!loader.initialized()) { 
  5.     loader.startInitialization(context.getApplicationContext()); 
  6.     loader.ensureInitializationComplete(context, dartVmArgs); 
  7.   } 

4、FlutterLoader 的 startInitialization 將加載 Flutter 引擎的本機(jī)庫 flutter.so 以啟用后續(xù)的 JNI 調(diào)用。還將查找解壓打包在 apk 中的 dart 資源,而且方法只會(huì)被調(diào)用一次。該方法具體調(diào)用步驟:

1)、settings 屬性是否賦值來確定方法是否執(zhí)行過;

2)、方法必須在主線程中執(zhí)行,否則拋異常退出;

3)、獲取 app 上下文;

4)、VsyncWaiter 是同步幀率相關(guān)的操作;

5)、記錄初始化耗時(shí)時(shí)間;

6)、從 flutter2 開始,初始化配置、初始化資源、加載 flutter.so 動(dòng)態(tài)庫,都放在后臺(tái)子線程中運(yùn)行,加快了初始化速度。

  1. //src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java 
  2. public void startInitialization(@NonNull Context applicationContext, @NonNull Settings settings) { 
  3.   //初始化方法只能運(yùn)行一次 
  4.   if (this.settings != null) { 
  5.     return
  6.   } 
  7.  //必須在主線程上調(diào)用 startInitialization 
  8.   if (Looper.myLooper() != Looper.getMainLooper()) { 
  9.     throw new IllegalStateException("startInitialization must be called on the main thread"); 
  10.   } 
  11.  
  12.   // 獲取 app 的上下文 
  13.   final Context appContext = applicationContext.getApplicationContext(); 
  14.  
  15.   this.settings = settings; 
  16.  
  17.   initStartTimestampMillis = SystemClock.uptimeMillis(); 
  18.  //獲取 app 相關(guān)信息 
  19.   flutterApplicationInfo = ApplicationInfoLoader.load(appContext); 
  20.   VsyncWaiter.getInstance((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE)) 
  21.       .init(); 
  22.  
  23.   //將后臺(tái)線程用于需要磁盤訪問的初始化任務(wù) 
  24.   Callable<InitResult> initTask = 
  25.       new Callable<InitResult>() { 
  26.         @Override 
  27.         public InitResult call() { 
  28.      //獲取配置資源 
  29.           ResourceExtractor resourceExtractor = initResources(appContext); 
  30.      //加載 fluter 本地 so 庫 
  31.           flutterJNI.loadLibrary(); 
  32.     
  33.           Executors.newSingleThreadExecutor() 
  34.               .execute
  35.                   new Runnable() { 
  36.                     @Override 
  37.                     public void run() { 
  38.            //預(yù)加載 skia 字體庫 
  39.                       flutterJNI.prefetchDefaultFontManager(); 
  40.                     } 
  41.                   }); 
  42.  
  43.           if (resourceExtractor != null) { 
  44.       //等待初始化時(shí)的資源初始化完畢后才會(huì)向下執(zhí)行,否則會(huì)一直阻塞 
  45.             resourceExtractor.waitForCompletion(); 
  46.           } 
  47.  
  48.           return new InitResult( 
  49.               PathUtils.getFilesDir(appContext), 
  50.               PathUtils.getCacheDirectory(appContext), 
  51.               PathUtils.getDataDirectory(appContext)); 
  52.         } 
  53.       }; 
  54.   initResultFuture = Executors.newSingleThreadExecutor().submit(initTask); 

5、initResources:將 apk 中的資源文件復(fù)制到應(yīng)用本地文件中,在 DEBUG 或者在 JIT_RELEASE 模式下安裝 Flutter 資源,主要由 ResourceExtractor 來異步執(zhí)行資源文件的解壓縮操作,最終會(huì)將 apk 中 assets 中的 Dart 資源 vm_snapshot_data、isolate_snapshot_data、kernel_blob.bin 文件安裝到應(yīng)用目錄 app_flutter 目錄下。

  1. private ResourceExtractor initResources(@NonNull Context applicationContext) { 
  2.   ResourceExtractor resourceExtractor = null
  3.   if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) { 
  4.   //獲取 flutter 數(shù)據(jù)存儲(chǔ)路徑 
  5.     final String dataDirPath = PathUtils.getDataDirectory(applicationContext); 
  6.   //獲取包名 
  7.     final String packageName = applicationContext.getPackageName(); 
  8.     final PackageManager packageManager = applicationContext.getPackageManager(); 
  9.     final AssetManager assetManager = applicationContext.getResources().getAssets(); 
  10.     resourceExtractor = 
  11.         new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager); 
  12.     resourceExtractor 
  13.         .addResource(fullAssetPathFrom(flutterApplicationInfo.vmSnapshotData)) 
  14.         .addResource(fullAssetPathFrom(flutterApplicationInfo.isolateSnapshotData)) 
  15.         .addResource(fullAssetPathFrom(DEFAULT_KERNEL_BLOB)); 
  16.     resourceExtractor.start(); 
  17.   } 
  18.   return resourceExtractor; 

6、在初始化 startInitialization 時(shí),也會(huì)調(diào)用 ensureInitializationComplete 方法確認(rèn)初始化是否完成,然后里面會(huì)把 so 文件的地址給到 shellArgs 里傳入 FlutterJNI,因此我們可以通過修改 flutter 生成的代碼或者使用 hook 等方式替換 ListshellArgs 的 add 方法,從而改變 so 的路徑,進(jìn)行熱修復(fù)。

  1. public void ensureInitializationComplete( 
  2.     @NonNull Context applicationContext, @Nullable String[] args) { 
  3.   if (initialized) { 
  4.     return
  5.   } 
  6.   if (Looper.myLooper() != Looper.getMainLooper()) { 
  7.     throw new IllegalStateException( 
  8.         "ensureInitializationComplete must be called on the main thread"); 
  9.   } 
  10.   if (settings == null) { 
  11.     throw new IllegalStateException( 
  12.         "ensureInitializationComplete must be called after startInitialization"); 
  13.   } 
  14.   try { 
  15.     InitResult result = initResultFuture.get(); 
  16.  
  17.     List<String> shellArgs = new ArrayList<>(); 
  18.  
  19.   // 此處省略具體參數(shù)配置代碼... 
  20.  
  21.     long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis; 
  22.  
  23.   // 初始化 JNI 
  24.     flutterJNI.init( 
  25.         applicationContext, 
  26.         shellArgs.toArray(new String[0]), 
  27.         kernelPath, 
  28.         result.appStoragePath, 
  29.         result.engineCachesPath, 
  30.         initTimeMillis); 
  31.  
  32.     initialized = true
  33.   } catch (Exception e) { 
  34.     Log.e(TAG, "Flutter initialization failed.", e); 
  35.     throw new RuntimeException(e); 
  36.   } 

FlutterJNI 初始化

  1. public void init( 
  2.       @NonNull Context context, 
  3.       @NonNull String[] args, 
  4.       @Nullable String bundlePath, 
  5.       @NonNull String appStoragePath, 
  6.       @NonNull String engineCachesPath, 
  7.       long initTimeMillis) { 
  8.     if (FlutterJNI.initCalled) { 
  9.       Log.w(TAG, "FlutterJNI.init called more than once"); 
  10.     } 
  11.   //調(diào)用 JNI 中 flutter 初始化方法 
  12.     FlutterJNI.nativeInit( 
  13.         context, args, bundlePath, appStoragePath, engineCachesPath, initTimeMillis); 
  14.     FlutterJNI.initCalled = true
  15.   } 

7、在初始化資源之后就開始加載 flutter.so,這個(gè)就是 Flutter Engine 源碼編譯后的產(chǎn)物。當(dāng)運(yùn)行時(shí),它被 Android 虛擬機(jī)加載到虛擬內(nèi)存中。(so 是一個(gè)標(biāo)準(zhǔn)的 ELF 可執(zhí)行文件,主要分為 .data 和 .text 段,分別包含了數(shù)據(jù)和指令,加載到虛擬內(nèi)存后,指令可以被 CPU 執(zhí)行) 加載了 flutter.so 之后,最先被執(zhí)行的是里面的 JNI_OnLoad 方法 ,會(huì)注冊(cè) FlutterMain 、PlatformView、VSyncWaiter 的 jni 方法。

  1. //src/flutter/shell/platform/android/library_loader.cc 
  2.  
  3. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { 
  4.   // 開始進(jìn)行 Java VM 的初始化,是保存當(dāng)前的 Java VM 對(duì)象到一個(gè)全局的變量中 
  5.   fml::jni::InitJavaVM(vm); 
  6.  
  7.  //把當(dāng)前的 thread 和 JavaVM 關(guān)聯(lián)起來 
  8.   JNIEnv* env = fml::jni::AttachCurrentThread(); 
  9.   bool result = false
  10.  
  11.   // 注冊(cè) FlutterMain,就是把 Java 層的 native 方法和 C++層的方法關(guān)聯(lián)起來 
  12.   result = flutter::FlutterMain::Register(env); 
  13.   FML_CHECK(result); 
  14.  
  15.   // 注冊(cè) PlatformView 
  16.   result = flutter::PlatformViewAndroid::Register(env); 
  17.   FML_CHECK(result); 
  18.  
  19.   // 注冊(cè) VSyncWaiter. 
  20.   result = flutter::VsyncWaiterAndroid::Register(env); 
  21.   FML_CHECK(result); 
  22.  
  23.   return JNI_VERSION_1_4; 

系統(tǒng)初始化完成之后,會(huì)調(diào)用 NativeInit 這個(gè) native方法,對(duì)應(yīng)的 FlutterMain.cc::Init 方法。這里初始化主要是根據(jù)傳入的參數(shù)生成了一個(gè) Settings 對(duì)象。

  1. // src/flutter/shell/platform/android/flutter_main.cc 
  2. void FlutterMain::Init(JNIEnv* env, 
  3.                        jclass clazz, 
  4.                        jobject context, 
  5.                        jobjectArray jargs, 
  6.                        jstring kernelPath, 
  7.                        jstring appStoragePath, 
  8.                        jstring engineCachesPath, 
  9.                        jlong initTimeMillis) { 
  10.   std::vector<std::string> args; 
  11.   args.push_back("flutter"); 
  12.   for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { 
  13.     args.push_back(std::move(arg)); 
  14.   } 
  15.   auto command_line = fml::CommandLineFromIterators(args.begin(), args.end()); 
  16.  
  17.   auto settings = SettingsFromCommandLine(command_line); 
  18.  
  19.   int64_t init_time_micros = initTimeMillis * 1000; 
  20.   settings.engine_start_timestamp = 
  21.       std::chrono::microseconds(Dart_TimelineGetMicros() - init_time_micros); 
  22.  
  23.   flutter::DartCallbackCache::SetCachePath( 
  24.       fml::jni::JavaStringToString(env, appStoragePath)); 
  25.  
  26.   fml::paths::InitializeAndroidCachesPath( 
  27.       fml::jni::JavaStringToString(env, engineCachesPath)); 
  28.  
  29.   flutter::DartCallbackCache::LoadCacheFromDisk(); 
  30.  
  31.   if (!flutter::DartVM::IsRunningPrecompiledCode() && kernelPath) { 
  32.     auto application_kernel_path = 
  33.         fml::jni::JavaStringToString(env, kernelPath); 
  34.  
  35.     if (fml::IsFile(application_kernel_path)) { 
  36.       settings.application_kernel_asset = application_kernel_path; 
  37.     } 
  38.   } 
  39.  
  40.   settings.task_observer_add = [](intptr_t key, fml::closure callback) { 
  41.     fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); 
  42.   }; 
  43.  
  44.   settings.task_observer_remove = [](intptr_t key) { 
  45.     fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); 
  46.   }; 
  47.  
  48.   settings.log_message_callback = [](const std::string& tag, 
  49.                                      const std::string& message) { 
  50.     __android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s", 
  51.                         (int)message.size(), message.c_str()); 
  52.   }; 
  53.  
  54. #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG 
  55.   auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { 
  56.     return [mapping, size]() { 
  57.       return std::make_unique<fml::NonOwnedMapping>(mapping, size); 
  58.     }; 
  59.   }; 
  60.  
  61.   settings.dart_library_sources_kernel = 
  62.       make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); 
  63. #endif   
  64.  //創(chuàng)建 Flutter 全局變量 
  65.   g_flutter_main.reset(new FlutterMain(std::move(settings))); 
  66.   g_flutter_main->SetupObservatoryUriCallback(env); 

從 args 解析出 Settings 的過程在 flutter_engine/shell/common/switches.cc,這里最重要的是 snapshot 路徑的構(gòu)建,構(gòu)建完成的路徑就是進(jìn)程初始化拷貝到本地的路徑, 最后生成了一個(gè) FlutterMain 對(duì)象保存在全局靜態(tài)變量中。

  1. if (aot_shared_library_name.size() > 0) { 
  2.     for (std::string_view name : aot_shared_library_name) { 
  3.       settings.application_library_path.emplace_back(name); 
  4.     } 
  5.   } else if (snapshot_asset_path.size() > 0) { 
  6.     settings.vm_snapshot_data_path = 
  7.         fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename}); 
  8.     settings.vm_snapshot_instr_path = fml::paths::JoinPaths( 
  9.         {snapshot_asset_path, vm_snapshot_instr_filename}); 
  10.     settings.isolate_snapshot_data_path = fml::paths::JoinPaths( 
  11.         {snapshot_asset_path, isolate_snapshot_data_filename}); 
  12.     settings.isolate_snapshot_instr_path = fml::paths::JoinPaths( 
  13.         {snapshot_asset_path, isolate_snapshot_instr_filename}); 
  14.   } 

后記

以上主要是 Flutter 2 FlutterEngineGroup 初始化的過程,下一節(jié)我們開始學(xué)習(xí)通過 FlutterEngineGroup創(chuàng)建 FlutterEngine 并綁定的 UI 頁面的流程。

 

責(zé)任編輯:姜華 來源: 微醫(yī)大前端技術(shù)
相關(guān)推薦

2021-08-02 06:49:46

Flutter Router安全

2020-07-07 10:50:19

Python丄則表達(dá)文本

2019-07-02 14:17:18

API網(wǎng)關(guān)網(wǎng)關(guān)流量

2017-03-25 20:30:15

2017-12-25 11:15:06

JavaArray數(shù)組

2019-06-23 15:21:42

Google谷歌平板

2025-04-22 02:00:00

芯片晶圓光刻機(jī)

2021-10-15 22:19:15

電腦藍(lán)屏重啟

2022-01-17 08:52:32

CPUCPU工具顯卡

2016-08-03 16:01:47

GitLinux開源

2021-11-08 07:11:49

決策樹數(shù)據(jù)分類器

2022-03-28 11:00:34

JVMJava對(duì)象

2022-04-19 11:25:31

JVMZGC垃圾收集器

2020-04-10 15:05:09

深度學(xué)習(xí)人工智能蒸餾

2018-01-26 14:35:16

程序員入門經(jīng)歷

2021-05-11 11:08:37

電腦病毒軟件

2020-09-10 06:56:12

SringMVC源碼參數(shù)

2021-11-03 06:57:42

數(shù)據(jù)線性邏輯

2021-10-25 05:54:59

SSD固態(tài)硬盤存儲(chǔ)

2022-04-21 08:20:33

CPU換蓋CPU
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

www.cao超碰| 伊人久久大香线蕉精品| 无码人妻一区二区三区线| 日韩中文字幕高清在线观看| 日韩免费看网站| 免费在线观看日韩视频| 天堂地址在线www| 成人国产精品免费观看动漫| 国产精品极品在线| 精品少妇久久久久久888优播| 国产成人精品一区二区免费看京 | 黄色av网站免费| 欧美国产免费| 在线观看欧美日韩国产| 久久久久亚洲av成人网人人软件| 欧美与亚洲与日本直播| 亚洲国产精品久久久男人的天堂| 亚洲精品一区二区三区四区五区| 三级在线观看网站| 国产麻豆视频一区| 国产精品久久久久久超碰 | 国内久久视频| 久久精品视频99| 亚洲精品91在线| 久久悠悠精品综合网| 欧美高清视频在线高清观看mv色露露十八 | 香蕉国产在线视频| 国产sm精品调教视频网站| 国产精品视频99| 久久国产视频一区| 亚洲黄色视屏| 九九视频直播综合网| 天天干天天操天天拍| 欧美激情在线免费| 日韩的一区二区| 亚洲欧美综合视频| 精品中文字幕一区二区三区四区 | 小日子的在线观看免费第8集| 美女色狠狠久久| 色播五月激情综合网| 91视频 -- 69xx| 91丝袜在线| 亚洲永久精品国产| 亚洲色婷婷久久精品av蜜桃| 理论片午午伦夜理片在线播放| 中文字幕成人在线观看| 深夜福利成人| 97超碰人人在线| 国产欧美1区2区3区| 日韩精品久久久| 国产毛片av在线| 国产精品无人区| 一区二区三区四区五区精品| 国产1区2区3区在线| 国产精品嫩草久久久久| 亚洲a∨一区二区三区| 懂色av中文在线| 欧美韩国一区二区| 永久久久久久| 欧美野外wwwxxx| 婷婷一区二区三区| aⅴ在线免费观看| 香蕉视频亚洲一级| 欧美日韩电影在线| 国产精品久久久久久9999| 国产日本亚洲| 精品人伦一区二区色婷婷| 黄色免费视频网站| 免费看日本一区二区| 中文字幕日韩精品在线| 免费看特级毛片| 亚洲一级一区| 国产成人精品一区| 97人妻精品一区二区三区动漫| 国产伦精一区二区三区| 99久久伊人精品影院| 免费国产黄色片| 久久久99免费| 男女h黄动漫啪啪无遮挡软件| 青草视频在线免费直播 | 嫩草影视亚洲| 综合av色偷偷网| 高h视频免费观看| 国产欧美一级| 国产色综合天天综合网| 免费观看成年人视频| 久久久久久久综合狠狠综合| 中文精品一区二区三区| 毛片在线网址| 欧美日韩一卡二卡| 中文字幕天堂av| 日本成人小视频| 色综合男人天堂| 精品国产www| 丁香婷婷综合激情五月色| 少妇免费毛片久久久久久久久| 午夜激情在线| 欧美性大战久久久久久久| 免费黄色av网址| 精品国产一区二区三区| 欧美高清第一页| 中文字幕观看在线| 99精品视频在线观看免费| 亚洲人成网站在线观看播放| 国产乱码午夜在线视频| 91麻豆精品国产综合久久久久久 | 欧美专区第一页| 99热这里只有精品5| 国产亚洲污的网站| 亚洲理论电影在线观看| 福利一区二区免费视频| 日韩激情av在线播放| 小泽玛利亚一区二区免费| 久久久久国产精品一区二区| 国产91精品一区二区绿帽| jizz在线观看视频| 欧美日韩亚洲一区二区| 99九九精品视频| 成人av资源电影网站| 91国产精品电影| www.国产麻豆| 亚洲欧美一区二区三区久本道91| 波多野结衣天堂| 亚洲精品aaaaa| 久久免费视频在线| 亚洲国产999| 亚洲视频狠狠干| 少妇激情一区二区三区| 亚洲ab电影| 2023亚洲男人天堂| 免费a级片在线观看| 亚洲亚洲人成综合网络| 日韩欧美中文视频| 91成人看片| 成人免费网站在线| 欧美日韩xx| 欧美日韩一区二区在线观看视频| 美女爆乳18禁www久久久久久| 国产精品日本欧美一区二区三区| 国产欧美欧洲| 午夜伦理福利在线| 亚洲精品美女久久 | 欧美三级三级三级爽爽爽| 成人免费无遮挡无码黄漫视频| 先锋亚洲精品| 欧美日本亚洲| 91久久久久久白丝白浆欲热蜜臀| 亚洲天堂成人在线| 中文字幕在线观看你懂的| 中文字幕免费不卡| 怡红院亚洲色图| 99久久影视| 99九九视频| 2001个疯子在线观看| 亚洲国产高清福利视频| 狠狠躁夜夜躁人人爽天天高潮| av亚洲精华国产精华| 国产又黄又大又粗视频| 免费黄色成人| 成人国产精品色哟哟| 久久bbxx| 精品国产凹凸成av人网站| 国产一区二区三区影院| 久久久国产精品不卡| 午夜视频在线网站| 狠狠色综合网| 欧美国产一区二区在线| 久久精品国产福利| 欧美福利小视频| 丝袜+亚洲+另类+欧美+变态| 欧美综合欧美视频| 国产97免费视频| 99久久亚洲一区二区三区青草 | 久久99精品久久久久久国产越南| 潘金莲一级淫片aaaaa免费看| 99re8这里有精品热视频免费| 2019最新中文字幕| 国产福利免费在线观看| 日韩久久久久久| 免费av网站在线| 中文字幕视频一区| 亚洲啪av永久无码精品放毛片| 国产精品婷婷| 中文字幕超清在线免费观看| 国产乱人伦精品一区| 国产精品 欧美在线| 91麻豆一二三四在线| 日韩高清有码在线| 国产精品视频一二区| 欧美日韩一区二区在线 | 亚洲一区久久久| 一区二区三区四区日本视频| 久久天天躁狠狠躁夜夜爽蜜月| 天天综合在线视频| 666欧美在线视频| www.中文字幕在线观看| 中文字幕一区三区| 国产男男chinese网站| 国产在线精品一区二区三区不卡| 777精品久无码人妻蜜桃| 欧美gayvideo| 你懂的视频在线一区二区| 欧美h版在线观看| 国产精品久久久久影院日本| missav|免费高清av在线看| 日日骚av一区| 酒色婷婷桃色成人免费av网| 精品国产123| 国产女人18毛片水真多| 在线免费观看不卡av| 久久婷婷国产麻豆91| 亚洲视频资源在线| 微拍福利一区二区| 99久久精品免费看| 在线播放国产视频| 九一九一国产精品| 手机在线免费观看毛片| 国产欧美日本| 91专区在线观看| 黄色精品一区| 日韩人妻一区二区三区蜜桃视频| 日韩在线视频精品| 性欧美.com| 欧美日韩国产传媒| 欧美极品视频一区二区三区| 精品国产18久久久久久洗澡| 91手机在线观看| 狂野欧美xxxx韩国少妇| 国产成人自拍一区| 欧美性生活久久| 伊人手机在线视频| 欧美日韩一区二区三区四区在线观看 | 日韩av中文字幕在线免费观看| 中文字幕乱码人妻无码久久| 懂色av影视一区二区三区| 欧美日韩免费做爰视频| 综合在线观看色| 二区三区四区视频| 国产情人综合久久777777| 给我看免费高清在线观看| 最新超碰在线| 日韩中文在线中文网在线观看| 日本大片在线观看| 亚洲裸体xxxx| 杨幂毛片午夜性生毛片| 91精品国产一区二区在线观看| 青青草国产精品一区二区| av影视在线看| 久久久久久国产精品三级玉女聊斋| 久久久久国产精品午夜一区| 97视频在线免费| 最新成人av网站| 东北少妇不带套对白| 激情婷婷欧美| 日日橹狠狠爱欧美超碰| 国产九九精品| 女性隐私黄www网站视频| 视频一区二区中文字幕| 国产又黄又猛又粗| 看片网址国产福利av中文字幕| 日韩主播视频在线| 国产91在线视频观看| 一二三区精品| 久久久999精品| 内衣办公室在线| 中文字幕无线精品亚洲乱码一区| 天堂资源在线中文| 欧美激情第1页| 最新欧美色图| 国产综合在线观看视频| 免费一级欧美在线大片| 国内成+人亚洲| 不卡视频在线| 男人天堂新网址| 亚洲欧美卡通另类91av| 中文字幕 日韩 欧美| 国产黄色成人av| av网站免费在线看| 亚洲欧美日韩国产综合| 黄色激情视频在线观看| 91成人免费在线| 亚洲va欧美va| 亚洲天堂一区二区三区| 丝袜在线观看| 国产精品久久久久久av福利| 日韩在线亚洲| 日韩精品最新在线观看| 欧美日韩第一区| 成人性视频欧美一区二区三区| 国产乱码精品一区二区三区av| 色噜噜在线观看| 亚洲欧美日韩国产综合| 成人免费毛片男人用品| 欧美一级夜夜爽| 国产天堂在线| 欧美肥臀大乳一区二区免费视频| 电影亚洲精品噜噜在线观看| 亚洲在线第一页| 清纯唯美亚洲综合一区| 国产v片免费观看| 国产在线看一区| 中文字幕网站在线观看| 亚洲一区二区在线播放相泽 | 日韩欧美在线中文字幕| 国产理论视频在线观看| 亚洲欧美在线一区二区| 好久没做在线观看| 亚洲va国产va天堂va久久| 国产日产一区| 少妇高潮喷水在线观看| 国产一区二区三区日韩| 欧美熟妇激情一区二区三区| 亚洲一级二级三级在线免费观看| 中文字幕一区二区三区免费看 | 成人免费视频观看视频| 日韩成人免费| 粉嫩虎白女毛片人体| 成人国产电影网| 一级黄色录像视频| 欧美精品在线观看播放| 福利片在线观看| 日本国产精品视频| 狼人精品一区二区三区在线| 日韩精品一区二区三区电影| 久久99热99| 成人无码精品1区2区3区免费看| 欧美午夜激情在线| 亚洲欧美色视频| 91国产一区在线| 麻豆精品少妇| 国产免费黄色小视频| 不卡一二三区首页| 免费在线看黄网址| 日韩免费成人网| 丰满诱人av在线播放| 99中文字幕| 亚洲婷婷免费| 人妻换人妻a片爽麻豆| 亚洲国产视频在线| www久久久久久| 欧美日本黄视频| 影音先锋欧美激情| 免费视频爱爱太爽了| 国产v日产∨综合v精品视频| 国产小视频在线看| 亚洲福利视频在线| 欧美调教sm| 欧美午夜精品久久久久免费视| 日韩精品每日更新| 中文字幕在线观看二区| 5858s免费视频成人| caopeng在线| 成人午夜电影免费在线观看| 99精品欧美| 免费毛片视频网站| 欧美日韩一区三区| 国产成人l区| 国产精品xxxx| 国产精品一卡| 纪美影视在线观看电视版使用方法| 欧美挠脚心视频网站| av免费看在线| 精品国产电影| 丝袜美腿亚洲综合| 狂野欧美性猛交| 欧美一级艳片视频免费观看| 欧美aaa免费| 久久综合入口| 久久精品国产精品青草| 欧美成人精品欧美一级私黄| 日韩av在线免费看| 成人在线免费av| 久久久99精品视频| 国产亚洲精品aa午夜观看| 亚洲字幕av一区二区三区四区| 欧美不卡视频一区发布| 日韩av黄色在线| 日本中文字幕二区| 午夜视频一区二区| 2021av在线| 国产区日韩欧美| 青青草97国产精品免费观看 | 欧美日韩精品一本二本三本 | 亚洲综合免费观看高清完整版在线 | 亚洲精品成a人ⅴ香蕉片| 欧美亚洲色图视频| 国产欧美日韩视频在线观看| 精品国产av鲁一鲁一区| 日本精品性网站在线观看| 91精品国产91久久综合| 亚洲国产果冻传媒av在线观看| 欧美日韩视频在线第一区| 成人女同在线观看| 亚洲一区二区三区四区中文| www.亚洲激情.com| h片在线免费看| 国产精品久久久久av免费| 亚洲人成免费| 久久福利免费视频| 亚洲色图18p|