成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Android Flutter實(shí)踐內(nèi)存初探

dabai / 980人閱讀

摘要:摘要實(shí)踐內(nèi)存初探閑魚(yú)技術(shù)匠修我們想使用來(lái)統(tǒng)一移動(dòng)開(kāi)發(fā)并做了一些實(shí)踐。將內(nèi)存管理分為新生代和老年代。在標(biāo)記階段,所有線程參與并發(fā)的完成對(duì)回收對(duì)象的標(biāo)記,降低標(biāo)記階段耗時(shí)。的首幀渲染耗時(shí)較高,在版本有明顯感受,大概會(huì)黑屏秒,版本會(huì)好很多。

摘要: Android Flutter實(shí)踐內(nèi)存初探 閑魚(yú)技術(shù)-匠修我們想使用Flutter來(lái)統(tǒng)一移動(dòng)App開(kāi)發(fā)并做了一些實(shí)踐。移動(dòng)設(shè)備上的資源有限,通常內(nèi)存使用都是一個(gè)我們?nèi)粘i_(kāi)發(fā)中十分關(guān)注的問(wèn)題。那么,F(xiàn)lutter是如何使用內(nèi)存,又會(huì)對(duì)Native App的內(nèi)存帶來(lái)哪些影響呢?本文將簡(jiǎn)單介紹Flutter內(nèi)存機(jī)制,結(jié)合測(cè)試和我們的開(kāi)發(fā)實(shí)踐,對(duì)日常關(guān)心的Bitmap內(nèi)存使用,View繪制內(nèi)存使用方面做一些探索。

閑魚(yú)技術(shù)-匠修
我們想使用Flutter來(lái)統(tǒng)一移動(dòng)App開(kāi)發(fā)并做了一些實(shí)踐。移動(dòng)設(shè)備上的資源有限,通常內(nèi)存使用都是一個(gè)我們?nèi)粘i_(kāi)發(fā)中十分關(guān)注的問(wèn)題。那么,F(xiàn)lutter是如何使用內(nèi)存,又會(huì)對(duì)Native App的內(nèi)存帶來(lái)哪些影響呢?本文將簡(jiǎn)單介紹Flutter內(nèi)存機(jī)制,結(jié)合測(cè)試和我們的開(kāi)發(fā)實(shí)踐,對(duì)日常關(guān)心的Bitmap內(nèi)存使用,View繪制內(nèi)存使用方面做一些探索。

Dart RunTime簡(jiǎn)介

Flutter Framework使用Dart語(yǔ)言開(kāi)發(fā),所以App進(jìn)程中需要一個(gè)Dart運(yùn)行環(huán)境(VM),和Android Art一樣,F(xiàn)lutter也對(duì)Dart源碼做了AOT編譯,直接將Dart源碼編譯成了本地字節(jié)碼,沒(méi)有了解釋執(zhí)行的過(guò)程,提升執(zhí)行性能。這里重點(diǎn)關(guān)注Dart VM內(nèi)存分配(Allocate)和回收(GC)相關(guān)的部分。

和Java顯著不同的是Dart的"線程"(Isolate)是不共享內(nèi)存的,各自的堆(Heap)和棧(Stack)都是隔離的,彼此之間通過(guò)消息通道來(lái)通信。Dart天然不存在數(shù)據(jù)競(jìng)爭(zhēng)和變量狀態(tài)同步的問(wèn)題,整個(gè)Flutter Framework Widget的渲染過(guò)程都運(yùn)行在一個(gè)isolate中。

Dart VM將內(nèi)存管理分為新生代(New Generation)和老年代(Old Generation)。

新生代(New Generation): 通常初次分配的對(duì)象都位于新生代中,該區(qū)域主要是存放內(nèi)存較小并且生命周期較短的對(duì)象,比如局部變量。新生代會(huì)頻繁執(zhí)行內(nèi)存回收(GC),回收采用“復(fù)制-清除”算法,將內(nèi)存分為兩塊(圖中的from 和 to),運(yùn)行時(shí)每次只使用其中的一塊(圖中的from),另一塊備用(圖中的to)。當(dāng)發(fā)生GC時(shí),將當(dāng)前使用的內(nèi)存塊中存活的對(duì)象拷貝到備用內(nèi)存塊中,然后清除當(dāng)前使用內(nèi)存塊,最后,交換兩塊內(nèi)存的角色。

老年代(Old Generation): 在新生代的GC中“幸存”下來(lái)的對(duì)象,它們會(huì)被轉(zhuǎn)移到老年代中。老年代存放生命力周期較長(zhǎng),內(nèi)存較大的對(duì)象。老年代通常比新生代要大很多。老年代的GC回收采用“標(biāo)記-清除”算法,分成標(biāo)記和清除兩個(gè)階段。在標(biāo)記階段,所有線程參與并發(fā)的完成對(duì)回收對(duì)象的標(biāo)記,降低標(biāo)記階段耗時(shí)。在清理階段,由GC線程負(fù)責(zé)清理回收對(duì)象,和應(yīng)用線程同時(shí)執(zhí)行,不影響應(yīng)用運(yùn)行。

可以看到,Dart VM借鑒了很多JVM的思路,Dart中產(chǎn)生內(nèi)存泄露的方式也和Java類(lèi)似,Java中很多排查內(nèi)存泄露的思路和防止內(nèi)存泄露的編程方法應(yīng)該也可以借鑒過(guò)來(lái)。

Image內(nèi)存初探
對(duì)圖片的合理使用和優(yōu)化是UI編程的重要部分,F(xiàn)lutter提供了Image Widget,我們可以方便的使用:

//使用本地圖片
new Image.asset("images/xxxx.jpg");

//使用網(wǎng)絡(luò)圖片
new Image.network("https://xxxxxx");

我們知道Android將內(nèi)存分為Java虛擬機(jī)內(nèi)存和Native內(nèi)存,各大廠商都對(duì)Java虛擬機(jī)內(nèi)存有一個(gè)上限限制,到達(dá)上限就會(huì)觸發(fā)OOM異常,而對(duì)Native內(nèi)存的使用沒(méi)有太嚴(yán)格的限制,現(xiàn)在的手機(jī)內(nèi)存都很大,一般有較大的Native內(nèi)存富余。那么Android中ImageView使用的是Java虛擬機(jī)內(nèi)存還是Native內(nèi)存呢?

我們可以來(lái)做一個(gè)測(cè)試:在一個(gè)界面上,每點(diǎn)擊一次,就在上面堆加一張圖片。為了防止后面的圖片完全覆蓋前面的圖片而出現(xiàn)優(yōu)化的情況,每次都縮小幾個(gè)像素,這樣就不會(huì)出現(xiàn)完全覆蓋。

打開(kāi)Android Profiler,一張一張?zhí)砑訄D片,觀察內(nèi)存數(shù)據(jù)。分別測(cè)試了Android的6.0,7.0和8.0系統(tǒng),結(jié)果如下:

Android 6.0(Google Nextus5)

Android 7.0(Meizu pro5)

Android 8.0(Google pixel)

在測(cè)試中,隨著圖片一張張?jiān)黾?,Android 6.0 和 7.0都是Java部分的內(nèi)存在增長(zhǎng),而Android 8.0則是Native部分的內(nèi)存在增長(zhǎng)。由此有結(jié)論,Android原生的ImageView在6.0和7.0版本中使用的Java虛擬機(jī)內(nèi)存,而在Android 8.0中則使用的Native內(nèi)存。

而Flutter Image Widget使用的是哪部分內(nèi)存呢?我們用Flutter界面來(lái)做相同的測(cè)試。Flutter Engine的Debug版本和Release版本存在很大的性能差異,所以我們測(cè)試最好使用Release版本,但是,Release版本的Apk又不能使用Android profiler來(lái)觀察內(nèi)存,所以我們需要在Debug版本的Apk中打包一個(gè)Release版本的Flutter Engine, 可以修改flutter tool中的flutter.gradle來(lái)實(shí)現(xiàn):

//不做判斷,強(qiáng)制改為打包release版本的engine
private static String buildModeFor(buildType) {
    // if (buildType.name == "profile") {
    //     return "profile"
    // } else if (buildType.debuggable) {
    //     return "debug"
    // }
    return "release"
}

相同地,我們向Flutter界面中添加圖片并用Android Profiler來(lái)觀察內(nèi)存,測(cè)試使用的dart代碼:

class StackImageState extends State {
  var images = [];
  var index = 0;

  @override
  Widget build(BuildContext context) {
    var widgets = [];

    for (int i = 0; i <= index; i++) {
      var pos = i - (i ~/ 103) * 103;
      widgets.add(new Container(
          child: new Image.asset("images/${pos}.jpg", fit: BoxFit.cover),
          padding: new EdgeInsets.only(top: i * 2.0)));
    }

    widgets.add(new Center(
        child: new GestureDetector(
            child: new Container(
                child: new Text("添加圖片(${index})",
                    style: new TextStyle(color: Colors.red)),
                color: Colors.green,
                padding: const EdgeInsets.all(8.0)),
            onTap: () {
              setState(() {
                index++;
              });
            })));
    return new Stack(
        children: widgets, alignment: AlignmentDirectional.topCenter);
  }
}

得到的結(jié)果是:

Android 6.0

Android 8.0

可以看到,F(xiàn)lutter Image使用的內(nèi)存既不屬于Java虛擬機(jī)內(nèi)存也不屬于Native內(nèi)存,而是Graphics內(nèi)存(在Meizu pro5設(shè)備上也不屬于Graphics,事實(shí)上Meizu pro5設(shè)備不能歸類(lèi)Flutter Image所使用的內(nèi)存),官方對(duì)Graphics內(nèi)存的解釋是:

那么至少Flutter Image所使用的內(nèi)存不會(huì)是Java虛擬機(jī)內(nèi)存,這對(duì)不少Android設(shè)備都是一個(gè)好消息,這意味著使用Flutter Image沒(méi)有OOM的風(fēng)險(xiǎn),能夠較好的利用Native內(nèi)存。

使用Image的時(shí)候,建立一個(gè)內(nèi)存緩存池是個(gè)好習(xí)慣,F(xiàn)lutter Framework提供了一個(gè)ImageCache來(lái)緩存加載的圖片,但它不同于Android Lru Cache,不能精確的使用內(nèi)存大小來(lái)設(shè)定緩存池容量,而是只能粗略的指定最大緩存圖片張數(shù)。

FlutterView內(nèi)存初探
Flutter設(shè)計(jì)之初是想統(tǒng)一Android和IOS的界面編程,所以理想的基于Flutter的apk只需要提供一個(gè)MainActivity做入口即可,后面所有的頁(yè)面跳轉(zhuǎn)都在FlutterView中管理。但是,如果是一個(gè)已有規(guī)模的app接入Flutter開(kāi)發(fā),我們不可能將已有的Activity頁(yè)面都用Flutter重新實(shí)現(xiàn)一遍,這時(shí)候就需要考慮本地頁(yè)面和Flutter頁(yè)面之間的跳轉(zhuǎn)交互了。iOS可以方便的管理頁(yè)面棧,但是Android就很復(fù)雜(Android有任務(wù)棧機(jī)制,低內(nèi)存Activity回收機(jī)制等),所以通常我們還是使用Activity作為頁(yè)面容器來(lái)展示flutter頁(yè)面。這時(shí)有兩種選擇,可以每次啟動(dòng)一個(gè)Activity就啟動(dòng)一個(gè)新的FlutterView,也可以啟動(dòng)Activity的時(shí)候復(fù)用已有的FlutterView。

不復(fù)用FlutterView

復(fù)用FlutterView

Flutter Framework中FlutterView是綁定Activity使用的,要復(fù)用FlutterView就必須能夠把FlutterView多帶帶拎出來(lái)使用。所幸現(xiàn)在FlutterView和Activity耦合程度并不很深,最關(guān)鍵的地方是FlutterNativeView必須attach一個(gè)Activity:

//attach到當(dāng)前Activity
mNativeView.attachViewAndActivity(this, activity);

初始化FlutterView時(shí)必須傳入一個(gè)Activity,當(dāng)其他Activity復(fù)用FlutterView時(shí)再調(diào)用該Attach方法即可。這里有個(gè)問(wèn)題,就是FlutterView中必須保存一個(gè)Activity引用,這個(gè)一個(gè)內(nèi)存泄露隱患,我們可以在FluterView detach時(shí)候?qū)ainActivity傳入,因?yàn)橥ǔU麄€(gè)App交互過(guò)程中MainActivity都是一直存在的,可以避免其他Activity泄露。

為了更好的權(quán)衡兩種方法的利弊,我們先用空頁(yè)面來(lái)測(cè)試一下當(dāng)頁(yè)面增加時(shí)內(nèi)存的變化:

不復(fù)用FlutterView時(shí),頁(yè)面增加時(shí)內(nèi)存變化

復(fù)用FlutterView時(shí),頁(yè)面增加時(shí)內(nèi)存變化

不復(fù)用FlutterView時(shí)平均打開(kāi)一個(gè)頁(yè)面(空頁(yè)面),Java內(nèi)存增長(zhǎng)0.02M,Native內(nèi)存增長(zhǎng)0.73M。復(fù)用FlutterView時(shí)平均打開(kāi)一個(gè)頁(yè)面(空頁(yè)面),Java內(nèi)存增長(zhǎng)0.019M,Native內(nèi)存增長(zhǎng)0.65M??梢?jiàn)復(fù)用FlutterView在內(nèi)存使用上是有優(yōu)勢(shì)的,但主要復(fù)用的還是Native部分的內(nèi)存。復(fù)用FlutterView必然帶來(lái)額外的一些復(fù)雜邏輯,有時(shí)候?yàn)榱诉壿嫼?jiǎn)單,后期維護(hù)上的方便,犧牲一些相對(duì)不太珍貴的Native內(nèi)存也是值得的。

復(fù)用單個(gè)FlutterView有時(shí)會(huì)有些“意外”,比如當(dāng)Activity切換時(shí),就不得不將當(dāng)前FlutterView detach掉給后面新建的Activity使用,當(dāng)前界面就會(huì)空白閃動(dòng),有個(gè)想法是可以將當(dāng)前界面截屏下來(lái)遮擋住后面的界面變化,這種方式有時(shí)會(huì)帶來(lái)額外的適配問(wèn)題。

FlutterView復(fù)用與否不是絕對(duì)的,有時(shí)候可以使用一些綜合性折中方案,比如,我們可以建立一個(gè)FlutterViewProvider,里面維護(hù)N個(gè)可復(fù)用的FlutterView,如圖:

這樣的好處是,可以存在一定程度上的復(fù)用,又可以避免只有一個(gè)FlutterView出現(xiàn)的一些尷尬問(wèn)題。

FlutterView的首幀渲染耗時(shí)較高,在Debug版本有明顯感受,大概會(huì)黑屏2秒,release版本會(huì)好很多。但我們觀察Cpu曲線,發(fā)現(xiàn)還是一個(gè)較為耗時(shí)的過(guò)程。有一種體驗(yàn)優(yōu)化的思路是,我們可以預(yù)先讓將要使用的FlutterView加載好首幀,這樣,在真正使用的時(shí)候就很快了,可以先建立一個(gè)只有1個(gè)像素的窗口,在這個(gè)窗口里面完成FlutterView首幀渲染,代碼如下:

final WindowManager wm = mFakeActivity.getWindowManager();
final FrameLayout root = new FrameLayout(mFakeActivity);
     
//一個(gè)像素足矣
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1, 1);
root.addView(flutterView,params);
WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
wlp.width = 1;
wlp.height = 1;
wlp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
wlp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
wm.addView(root,wlp);

final FlutterView.FirstFrameListener[] listenerRef = new FlutterView.FirstFrameListener[1];
    listenerRef[0] = new FlutterView.FirstFrameListener() {
        @Override
       public void onFirstFrame() {
           //首幀渲染完后取消窗口
             wm.removeView(root);
          flutterView.removeFirstFrameListener(listenerRef[0]);
       }
       };

flutterView.addFirstFrameListener(listenerRef[0]);
String appBundlePath = FlutterMain.findAppBundlePath(mFakeActivity.getApplicationContext());
flutterView.runFromBundle(appBundlePath, null, "main", true);

原文鏈接

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71453.html

相關(guān)文章

  • 跨平臺(tái)開(kāi)發(fā)框架 Lynx 初探

    摘要:跨平臺(tái)開(kāi)發(fā)是目前開(kāi)發(fā)較熱門(mén)的方向,在這方面取得了很大的成功,同時(shí)也獲得了非常多的關(guān)注。最近發(fā)現(xiàn)了跨平臺(tái)的一個(gè)新框架,從官方簡(jiǎn)介來(lái)看這個(gè)框架還處于狀態(tài),目前還沒(méi)有完整的文檔,只有體驗(yàn)。希望能有更多的跨平臺(tái)開(kāi)發(fā)框架出現(xiàn),推動(dòng)技術(shù)的發(fā)展。 跨平臺(tái)開(kāi)發(fā)是目前開(kāi)發(fā)較熱門(mén)的方向,React Native 在這方面取得了很大的成功,同時(shí) Flutter 也獲得了非常多的關(guān)注。React Native...

    jsyzchen 評(píng)論0 收藏0
  • RN 技術(shù)探索:Hermes Engine 初探

    摘要:原方式中是經(jīng)過(guò)壓縮的腳本文件,預(yù)編譯后則是二進(jìn)制文件。兩者影響疊加導(dǎo)致整體減小,包大小得到優(yōu)化。引擎包引擎包官方文檔中對(duì)內(nèi)存區(qū)的描述您的應(yīng)用用于處理代碼和資源如字節(jié)碼已優(yōu)化或已編譯的碼庫(kù)和字體的內(nèi)存。本文首發(fā)自普惠出行產(chǎn)品技術(shù) 自從 Google 的 Flutter 發(fā)布之后,F(xiàn)acebook 對(duì) React-Native 的迭代開(kāi)始快了起來(lái),優(yōu)化 React-Native 的性能表現(xiàn)...

    Cc_2011 評(píng)論0 收藏0
  • 用前端 最舒服的躺姿 "搞定" Flutter (組件篇)

    摘要:是谷歌的移動(dòng)框架,可以快速在和上構(gòu)建高質(zhì)量的原生用戶(hù)界面。在全世界好了這些,大家早就知道了,來(lái)點(diǎn)實(shí)在的話說(shuō)隔壁師兄,閑魚(yú)是最早一批與谷歌展開(kāi)合作,并在重要的商品詳情頁(yè)中使用技術(shù)上線的。一切皆來(lái)自的組件皆來(lái)自。是狀態(tài)不可變的稱(chēng)為無(wú)狀態(tài)。 前言 要說(shuō)2018年最火的跨端技術(shù),當(dāng)屬于 Flutter 莫屬,應(yīng)該沒(méi)人質(zhì)疑吧。一個(gè)新的技術(shù)的趨勢(shì),最明顯的特征,就是它一定想把前浪拍死在沙灘上。這個(gè)...

    LMou 評(píng)論0 收藏0
  • 用前端 最舒服的躺姿 "搞定" Flutter (組件篇)

    摘要:是谷歌的移動(dòng)框架,可以快速在和上構(gòu)建高質(zhì)量的原生用戶(hù)界面。在全世界好了這些,大家早就知道了,來(lái)點(diǎn)實(shí)在的話說(shuō)隔壁師兄,閑魚(yú)是最早一批與谷歌展開(kāi)合作,并在重要的商品詳情頁(yè)中使用技術(shù)上線的。一切皆來(lái)自的組件皆來(lái)自。是狀態(tài)不可變的稱(chēng)為無(wú)狀態(tài)。 前言 要說(shuō)2018年最火的跨端技術(shù),當(dāng)屬于 Flutter 莫屬,應(yīng)該沒(méi)人質(zhì)疑吧。一個(gè)新的技術(shù)的趨勢(shì),最明顯的特征,就是它一定想把前浪拍死在沙灘上。這個(gè)...

    Donne 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<