主頁 > 移動端開發 > Flutter架構概覽

Flutter架構概覽

2021-11-03 08:36:03 移動端開發

文章目錄

  • 前言
  • 架構層
  • 回應式用戶界面
  • Widgets
    • 組成
    • 構建widgets
    • 狀態管理
  • 渲染和布局
    • Flutter的渲染模型
    • 從用戶操作到GPU
    • 構建:從Widget到Element
    • 布局和渲染
    • Platform embedding

前言

本文總結Flutter架構概覽,包含其設計層面的核心原則以及概念,

Flutter是一個跨平臺的UI工具集,它允許在各種作業系統上復用相同的代碼,同時應用程式直接與底層平臺互動,避免了不同平臺視圖的差異,同時也讓開發者能夠在不同平臺上都能交付擁有原生體驗的高性能應用,

開發階段,FLutter應用會在一個VM(程式虛擬機)中運行,從而可以保留狀態且無需重新編譯的情況下,熱多載相關的更新,對于發行版(release),Flutter程式會直接編譯錯機器碼,或者針對Web平臺的JavaScript,

概覽分為以下幾個部分:

  1. 分層模型:Flutter的構成要素
  2. 回應式用戶界面:Flutter用戶界面開發的核心概念
  3. widgets介紹:構建Flutter用戶界面的基石
  4. 渲染程序:Flutter如何將界面布局轉化為像素
  5. 平臺嵌入層的概念:讓Flutter應用可以再移動端以及桌面端作業系統執行的代碼

架構層

Flutter被設計為一個可擴展的分層系統,它可以被看做是各個獨立的組件系列合集,上層的組件各自依賴下層的組件,組件無法越權訪問底層的內容,并且框架層的各個部分都是可選且可替代,

對于底層作業系統而言,Flutter應用程式的包裝方式與其他原生應用相同,在每一個平臺上,都回去包含一個特定的嵌入層,從而提供一個程式入口,程式由此可以與底層作業系統進行協調,訪問諸如Surface渲染,輔助功能和輸入等等服務,并且管理時間回圈佇列,該嵌入層采用了適合當前平臺語言撰寫,例如Android使用的是Java/C++,IOS和MacOSSierra使用的是OC和OC++,Windows和Linux使用的是C++,Flutter代碼可以通過嵌入層,以模塊方式集成到現有的應用中,也可以作為應用的主體,Flutter本身包含了各個常見平臺的嵌入層,同時也存在一些其他的嵌入層,

Flutter引擎毫無疑問是Flutter的核心,它主要是C++撰寫,并提供了Flutter應用所需要的原語,當需要繪制新的一幀的內容時,引擎將負責對需要合成的場景進行柵格化,它提供了Flutter核心API的底層實作,包括圖形(通過Skia)、文本布局、檔案以及網路IO、輔助功能支持、插件架構和Dart運行環境以及編譯環境的工具鏈,

引擎將C++ 代碼包裝成Dart代碼,通過dart:ui暴露給Flutter框架層,該庫暴露了最底層的原語,包括用于驅動圖形輸入、圖形、和文本渲染的子系統的類,

通常,開發者可以通過Flutter Framework與Flutter進行互動,該Framework提供了以Dart語音撰寫的現代回應式框架,它包括由一系列層組成的一組豐富的平臺,布局和基礎庫,從下層到上層,依次有:

  • 基礎的 foundational 類及一些基層之上的構建塊服務,如 animation、 painting 和 gestures,它們可以提供上層常用的抽象,
  • 渲染層 用于提供操作布局的抽象,有了渲染層,你可以構建一棵可渲染物件的樹,在你動態更新這些物件時,渲染樹也會自動根據你的變更來更新布局,
  • widget 層 是一種組合的抽象,每一個渲染層中的渲染物件,都在 widgets 層中有一個對應的類,此外,widgets 層讓你可以自由組合你需要復用的各種類,回應式編程模型就在該層級中被引入,
  • MaterialCupertino 庫提供了全面的 widgets 層的原語組合,這套組合分別實作了 Material 和 iOS 設計規范,

Flutter 框架相對較小,因為一些開發者可能會使用到的更高層級的功能已經被拆分到不同的軟體包中,使用 Dart 和 Flutter 的核心庫實作,其中包括平臺插件,例如 camera 和 webview;與平臺無關的功能,例如 characters、 http 和 animations,還有一些軟體包來自于更為寬泛的生態系統中,例如 應用內支付、 Apple 認證 和 Lottie 影片,

該概覽的其余部分將從 UI 開發的回應式范例開始,瀏覽各個構建層,而后,我們會講述 widgets 如何被組織,并轉換成應用程式的渲染物件,同時我們也會講述 Flutter 如何在平臺層面與其他代碼進行互動,最終,我們會對目前 Flutter 對于 Web 平臺的支持與其他平臺的異同做一個總結,

回應式用戶界面

Flutter 是一個回應式的且偽宣告式的 UI 框架,開發者負責提供應用狀態與界面狀態之間的映射,框架則在運行時將應用狀態的更改更新到界面上,在大部分傳統的 UI 框架中,界面的初始狀態通常會被一次性定義,然后,在運行時根據用戶代碼分別回應事件進行更新,

Flutter 與其他回應式框架類似,采用了顯式剝離基礎狀態和用戶界面的方式,來解決這一問題,你可以通過 React 風格的 API,創建 UI 的描述,讓框架負責通過配置優雅地創建和更新用戶界面,

在 Flutter 里,widgets(類似于 React 中的組件)是用來配置物件樹的不可變類,這些 widgets 會管理單獨的布局物件樹,接著參與管理合成的布局物件樹, Flutter 的核心就是一套高效的遍歷樹的變動的機制,它會將物件樹轉換為更底層的物件樹,并在樹與樹之間傳遞更改,

build() 是將狀態轉化為 UI 的方法,widget 通過重寫該方法來宣告 UI 的構造,build() 方法在框架需要時都可以被呼叫(每個渲染幀可能會呼叫一次),從設計角度來看,它應當能夠快速執行且沒有額外影響的,這樣的實作設計依賴于語言的運行時特征(特別是物件的快速實體化和清除),幸運的是,Dart 非常適合這份作業,

Widgets

應用額如前所述,FLutter強調以widgets作為組成單位,Widgets是構建Flutter應用界面的基礎塊,每個widget都是一部分不可變的UI宣告,

Widgets通過布局組合形成一種層次結構關系,每個Widget都是嵌套在其父級的內部,并可以通過父級接收背景關系,從根布局(托管Flutter應用的容器,通常是MaterialApp或者CupertinoApp)開始,自上而下就是這樣的結構,如下面實體;

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('My Home Page'),
        ),
        body: Center(
          child: Builder(
            builder: (BuildContext context) {
              return Column(
                children: [
                  const Text('Hello World'),
                  const SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: () {
                      print('Click!');
                    },
                    child: const Text('A button'),
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

在上面的代碼中,所有的實體化的類都是widgets,

應用會根據事件互動,通知框架替換層級中的舊的widget為新的widget,最后框架會比較新舊widgets,高效的更新用戶界面,

Flutter擁有其自己的UI控制實作,而不是由系統自帶的方法進行托管:例如,IOS的Switch控制元件和Android的選擇控制元件都有一個Dart實作,

這樣的實作有幾個優勢:

  • 提供了誣陷的擴展性,
  • Flutter可以直接合成所有的場景,而無需在Flutter與原生平臺之間來回的切換,從而避免了明顯的性能瓶頸,
  • 將應用的行為與作業系統的依賴解耦,

組成

Widget通常由更小的且用途單一的widgets組合而成,提供更強大的功能,

在設計的時候,相關的概念設計已盡可能地少量存在,而通過大量的內容進行填充,eg,Flutter在widgets層中使用了相同的概念(一個Widget)來表示螢屏上的繪制、布局(位置和大小)、用戶互動、狀態管理、主題、影片以及導航,在影片層,Animation和Tween這對概念組合,涵蓋了大部分的設計空間,在渲染層,RenderObject用來描述布局、繪制、觸摸判斷以及可訪問性,在這些場景中,最終對于包含的內容都很多:有數百個widgets和Render objects,以及數十種的影片和補間型別,

類的層次結構是有意的淺而廣,以最大限度的增加可能的組合數量,重點放在小的,可組合的widget上,確保每個widget都能橫好的完成一件事情,核心功能均被抽象,甚至像編劇和對齊這樣的基礎功能,都被實作為單獨的組件,而不是內置于核心中,(這樣的實作也與傳統的API形成了對比,類似于邊距這樣的功能通常都內置在了每個組件的公共核心內,Flutter中的widget則不同,)因此,如果你需要講一個widget居中,預期調整Align這樣的屬性,不如將他包裹在一個Center widget內,

Flutter中包含了邊距,對齊,行,列和網格系列的widgets,這些布局型別的widgets自身沒有視覺內容,而只用于控制其他的widgets的部分布局條件,Flutter也包含了以這種組合方法組成的實用性widgets,

例如,一個常用的widget Container,是由幾個widget組合而成,包含了布局、繪制、定位和大小的功能,更具體地說,Container是由LimitedBox、ConstrainedBox、Align、Padding、DecoratedBox和Transform組合而成的,你也可以通過查看原始碼看到這些組合,Flutter 有一個典型的特征,即你可以深入到任意一個 widget,查看其原始碼,因此,你可以通過同樣的方式組合其他的 widgets,也可以參考 Container 來創建其他的 widget,而不需要繼承 Container 來實作自定義的效果,

構建widgets

先前提到,可以通過重寫build()方法,回傳一個新的元素樹,來定義視覺展示,這棵樹用更為具體的術語表示了widget在UI中的部分,例如,工具列widget的build方法可能會回傳水平布局,其中可能包含了一些文字,各種各樣的按鈕,根據需要,框架會遞回請求每個widget進行構建,直到整棵樹都被具體的可渲染的物件描述為止,然后框架會將可渲染的物件縫合在一起,組成可渲染的物件樹,

Widget的build方法應該是沒有副作用的,每當一個方法要求構建市,widget都應當能回傳一個widget的元素樹,與先簽回傳的widget也沒有關聯,框架會根據渲染物件樹來確定哪些構建方法需要被呼叫,這是一響略顯繁重的作業,

每個渲染幀,Flutter都可以根據變換的狀態,呼叫build()方法重建部分UI,因此,保證build方法輕量且能夠快速回傳widget是非常關鍵的,繁重的計算作業應該通過一些異步的方法來完成,然后作為構建方法build的一部分存盤,

盡管這樣的實作看起來不夠成熟,但是這樣的自動對比方法非常有效,可以實作高性能的互動應用,同時,以這種方式設計的build方法,將重點放在widget組合的宣告上,從而簡化了代碼,而不是以一種狀態去更新另一種狀態這樣的復雜程序,

狀態管理

那么,在眾多的widget都持有狀態的情況下,系統中的狀態是如何被傳遞和管理的呢?

與其他類相同,你可以通過widget的建構式來初始化資料,如此一來build()方法可以確保子widget使用其所需要的資料進行實體化:

@override
Widget build(BuildContext context) {
   return ContentWidget(importantState);
}

然而,隨著widget樹層級的逐漸增加加深,依賴樹結構上下傳遞狀態資訊會變得十分麻煩,這時,第三張型別的widget——InheritedWidget,提供了一種從共享的祖先節點獲取資料的簡易辦法,你可以使用InheritedWidget創建包含狀態的widget,該widget會將一個共同的祖先節點包裹在widget樹中,如下:

現在,當ExamWidget或者GradeWIdget物件需要獲取StudentState的資料時,可以直接使用以下方式:

final studentState = StudentState.of(context);

呼叫of(context)會根據當前構建的背景關系(即當前的widge位置的句柄),并回傳型別為StudentState的在樹中距離最近的祖先節點,InheritedWidget同時也包含了updateShouldNotify()方法,Flutter會呼叫它來判斷依賴了某個狀態的widget是否需要更新重建,

InheritedWidget在Flutter中被大量用于共享狀態,例如應用的視覺主題,包含了應用于整個應用的顏色和字體樣式等屬性,MaterialApp的build()方法會在構建市在樹中插入一個主題,更生層次的widget便可以使用.of()方法來查找相關的主題資料,例如:

Container(
  color: Theme.of(context).secondaryHeaderColor,
  child: Text(
    'Text with a background color',
    style: Theme.of(context).textTheme.headline6,
  ),
);

類似的,以該方法實作的還有提供了路由頁面的Navigator,提供了螢屏資訊指標,包括方向,尺寸和高度的MediaQuery等等,

隨著應用程式的不斷迭代,更高級的狀態管理方法變得更加有吸引力,它們可以減少有狀態的widget的創建,許多Flutter應用使用了provider用于狀態管理,它對InheritedWidget進行了進一步的包裝,FLutter的分層架構也允許使用其他的實作來替換狀態管理只UI的方案,例如flutter_hooks,

渲染和布局

本節介紹Flutter的渲染機制,包括將widget層級結構轉換成螢屏上繪制的實際像素的一系列步驟,

Flutter的渲染模型

你可能思考過:既然Flutter是一個跨平臺的框架,那么它又是如何提供與原生平臺框架相當的性能的呢?

讓我們從Android原生應用的角度開始思考,當你在撰寫繪制的內容的時候,你需要呼叫Android框架的Java代碼,Android的系統庫提供了可以將自身繪制到Canvas物件的組件,接下來Android就可以使用由C/C++撰寫的Skia圖形引擎,呼叫CPU和GPU完成在設備上的繪制,

跨平臺框架都會在Android和IOS的UI底層庫上創建一層抽象,該抽象層嘗試抹平各個系統之間的差異,這時,應用程式的代碼通常使用JavaScript等解釋型語言來進行撰寫,這些代碼會與基于Java的Android和基于OC的IOS進行互動,最終展示UI界面,所有流程都增加了顯著的開銷,在UI和應用邏輯有凡在的互動時更為如此,

相比之下,Flutter通過染過系統UI組件庫,使用自己的widget內容集,消減了抽象層的開銷,用于繪制Flutter影像內容的Dart代碼被編譯成機器碼,并使用Skia進行渲染,Flutter同時也嵌入了自己的Skia副本作文引擎的一部分,讓開發者能再設備未更新到最新系統時,也能跟進升級自己的應用,保證穩定性并提升性能,

從用戶操作到GPU

對于Flutter渲染機制而言,首要原則就是簡單快捷,Flutter為資料流向系統提供了直通的通道,如以下的流程圖所示:

Render pipeline sequencing diagram

接下來讓我們更加深入了解其中的一些階段,

構建:從Widget到Element

首先觀察以下的代碼片段,它代表了一個簡單的widget結構:

Container(
  color: Colors.blue,
  child: Row(
    children: [
      Image.network('https://www.example.com/1.png'),
      const Text('A'),
    ],
  ),
);

當Flutter需要繪制這段代碼時,框架會呼叫build()方法,回傳一顆基于當前應用狀態來繪制UI的widget子樹,在這個程序中,build()方法可能會在必要時,根據狀態引入新的widget,在上面的例子中,Container的color和child就是電信的例子,我們可以查看Container的原始碼,會發現當color屬性不為空時,ColoredBox會被加入用于顏色布局,

if (color != null)
  current = ColoredBox(color: color!, child: current);

與之對應的,Image和Text在構建程序中也會引入RawImage和RichText,如此一來,最終生成的widget結構比代碼表示的層級更深,在該場景中如下圖:

Render pipeline sequencing diagram

這就是為什么你在使用Dart DevTools的Flutter inspector除錯widget樹結構時,會發下實際的結構比你原本代碼中的結構更深,

在構建階段,Flutter會將代碼中描述的widgets轉化成對應的Element樹,每一個Widget都有一個對應的Element,每一個Element代表了梳妝層次結構中特定位置的widget實體,目前有兩種Element的基本型別:

  • ComponentElement,其他Element的宿主,
  • RenderObjectElement,參與布局或繪制階段的Element,

Render pipeline sequencing diagram

RenderObjectElement是底層RenderObject與對應的widget之間的橋梁,我們晚點會介紹,

任何widget都可以通過其BuildContext參考到Element,它是該widget在樹中的位置的句柄,類似于Theme.of(context)方法呼叫中的context,它作為build()方法的引數被傳遞,

由于widgets以及它上下節點的關系都是不可變的,因此,對widget樹做任何操作(例如將Text(‘A’)修改成Text(‘B’))都會回傳一個新的widget物件集合,但是這并不是意味著底層呈現的內容必須要重新構建,Element樹每一幀之間都是持久化的,因此起著至關重要的性能作用,Flutter依靠該優勢,實作類一種好似widget樹被完全拋棄,而快取了底層表示的機制,Flutter可以根據發生變化的widget,來重建需要重新配置的Element樹的部分,

布局和渲染

很少有應用只繪制單個widget,因此,有效的排布widget的結構以及在渲染完成前決定每個Element的大小和位置,是所有UI框架的重點之一,

在渲染樹中,每個節點的基類都是RenderObject,該基類為布局和繪制定義了一個抽象的模型,這是再平凡不過的事情:它并不總是一個固定大小,甚至不尊徐笛卡爾坐標系規律,每一個RenderObjectElement都了解其父節點的資訊,對于其子節點,除了如何訪問和獲得他們的布局約束,并沒有更多的資訊,這樣設計讓RenderObject擁有高效的抽象能力,能夠處理各種各樣的使用場景,

在構建階段,Flutter會為Element樹中的每個RenderObjectElement創建或更新其對于的一個從RenderObject繼承的物件,RenderObject實際上是原語:渲染文字的RenderParagraph、渲染圖片的RenderImage以及在繪制子節點內容前應用變換的RenderTransform是更為上層的實作,

Differences between the widgets hierarchy and the element and render trees

大部分的Flutter widget是由一個繼承了RenderBox的子類物件渲染的,他們呈現出的RenderObject會在二維迪卡空間中擁有固定的大小,RenderBox提供了盒子模型限制,為每個widget關聯了渲染的最小和最大的寬度和高度,

在進行布局的時候,Flutter會議DFS(深度優先遍歷)方式遍歷渲染書,并將限制以自上而下的方式從父節點傳遞給子節點,子節點若要確定自己的大小,則必須遵循父節點傳遞的限制,子節點的回應方式是在父節點簡歷的約束內將大效益自下而上的方式傳遞給父節點,

Constraints go down, sizes go up

在遍歷完成一次樹之后,每個物件都通過父級約束而擁有了明確的大小,隨時可以通過呼叫paint()進行渲染,

盒子限制模型十分強大,它的物件布局的時間復雜度是O(n):

  • 父節點可以通過設定最大和最小的尺寸限制,決定其子節點物件的大小,例如:在一個手機應用中,最高層級的渲染物件將會限制其子節點的大小為螢屏的尺寸,(子節點可以選擇如何占用空間,例如,它們可能在設定的限制中以居中的方式布局,)
  • 父節點可以決定子節點的寬度,而讓子節點靈活地自適應布局高度(或決定高度而自適應寬度),現實中有一種例子就是流式布局的文本,它們常常會填充橫向限制,再根據文字內容的多少決定高度,

這樣的盒子約束模型,同樣也適用于子節點物件需要知道有多少可用空間渲染其內容的場景,通過使用 LayoutBuilder widget,子節點可以得到從上層傳遞下來的約束,并合理利用該約束物件,使用方法如下:

Widget build(BuildContext context) {
  return LayoutBuilder(
    builder: (context, constraints) {
      if (constraints.maxWidth < 600) {
        return const OneColumnLayout();
      } else {
        return const TwoColumnLayout();
      }
    },
  );
}

所有的RenderObject的根節點是RenderView,代表了渲染樹的總體輸出,當平臺需要渲染新的一幀內容時(例如一個vsync型號或者一個紋理的更新完成),會呼叫一次compositeFrame()方法,它是RenderView的一部分,該方法會創建一個SceneBuilder來觸發當前畫面的更新,當畫面更新完畢,RenderView會將合成的畫面傳遞給dart:ui中的Window.render()方法,控制GPU進行渲染,

Platform embedding

我們都知道,Flutter 的界面構建、布局、合成和繪制全都由 Flutter 自己完成,而不是轉換為對應平臺系統的原生組件,獲取紋理和聯動應用底層的生命周期的方法,不可避免地會根據平臺特性而改變, Flutter 引擎本身是與平臺無關的,它提供了一個穩定的 ABI(應用二進制介面),包含一個 平臺嵌入層,可以通過其方法設定并使用 Flutter,

平臺嵌入層是用于呈現所有 Flutter 內容的原生系統應用,它充當著宿主作業系統和 Flutter 之間的粘合劑的角色,當你啟動一個 Flutter 應用時,嵌入層會提供一個入口,初始化 Flutter 引擎,獲取 UI 和柵格化執行緒,創建 Flutter 可以寫入的紋理,嵌入層同時負責管理應用的生命周期,包括輸入的操作(例如滑鼠、鍵盤和觸控)、視窗大小的變化、執行緒管理和平臺訊息的傳遞, Flutter 擁有 Android、iOS、Windows、macOS 和 Linux 的平臺嵌入層,當然,開發者可以創建自定義的嵌入層,正如這個 可用的例子 以 VNC 風格的幀緩沖區支持了遠程 Flutter,還有 [支持樹莓派運行的例子]https://github.com/ardera/flutter-pi),

每一個平臺都有各自的一套 API 和限制,以下是一些關于平臺簡短的說明:

  • 在 iOS 和 macOS 上, Flutter 分別通過 UIViewControllerNSViewController 載入到嵌入層,這些嵌入層會創建一個 FlutterEngine,作為 Dart VM 和您的 Flutter 運行時的宿主,還有一個 FlutterViewController,關聯對應的 FlutterEngine,傳遞 UIKit 或者 Cocoa 的輸入事件到 Flutter,并將 FlutterEngine 渲染的幀內容通過 Metal 或 OpenGL 進行展示,

  • 在 Android 上,Flutter 默認作為一個 Activity 加載到嵌入層中,此時視圖是通過一個 FlutterView 進行控制的,基于 Flutter 內容的合成和 z 排列 (z-ordering) 的要求,將 Flutter 的內容以視圖模式或紋理模式進行呈現,

  • 在 Windows 上,Flutter 的宿主是一個傳統的 Win32 應用,內容是通過一個將 OpenGL API 呼叫轉換成 DirectX 11 的等價呼叫的庫 ANGLE 進行渲染的,目前正在嘗試將 UWP 應用作為 Windows 的一種嵌入層,并將 ANGLE 替換為通過 DirectX 12 直接呼叫 GPU 的方式,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/345792.html

標籤:其他

上一篇:前端——從html到flutter的前端之路

下一篇:Android MVVM框架搭建(二)OKHttp + Retrofit + RxJava

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more