2012-10-09 45 views
5

我的Android 4.0.4應用程序由一個WebView組成,用戶可以通過該WebView查看本地存儲在資產目錄中的多個頁面。當通過頁面循環,下面的錯誤,最終被觸發和應用程序崩潰:Android WebView資產引用內存泄漏

  • JNI錯誤(應用程序錯誤):本地參照表溢出(最大值= 512)
  • 無法添加到JNI本地參考表(具有512個條目)
  • VM中止
  • 致命信號11(SIGSEGV)在0xdeadd00dd(代碼= 1)

這個問題似乎涉及到此處報道的那些:

我用在下面的鏈接中給出的內存分析工具插件指令,以檢查細節:

http://therockncoder.blogspot.ca/2012/09/fixing-android-memory-leak.html

結果顯示如下(不能發佈屏幕截圖上課的是,這樣的文本將不得不這樣做):

MAT摘錄

Class Name                                                                      | Shallow Heap | Retained Heap | Percentage 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
                                                                           |    |    |   
java.lang.Thread @ 0x40daa320 Thread-39775 Thread                                                            |   80 | 15,310,552 |  76.74% 
|- byte[32768] @ 0x40d5a8d0 <!DOCTYPE html>.<html xml:lang="">.<head>. <title>Android Test-HTML5-480PX-Page 0</title>. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />. <meta name="viewport" content="width=360, height=480">. <!--. <meta name="viewport" co...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x40e25bb8 on-play-state: running;.  -webkit-animation-timing-function: step-start;. }. @-webkit-keyframes ag16780-anim45051. {.  0.000% { background-position:0px 0px,77px 0px,77px 0px,77px 0px,77px 0px,77px 0px; }.  20.000% { background-position...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x40e60520 //<![CDATA[.var pageParams = new Object();.pageParams['readMode'] = 1;...function applyReadMode().{. var audioNodes = document.getElementsByClassName('BGAudio');. for (var i=0; i<audioNodes.length; i++). {. .if (pageParams['readMode'] == 0). ...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x40e86f48 .PNG........IHDR....... ......U......gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....bKGD..............pHYs...#...#.x.?v...ZIDATH..U]l\W..f.9.......8..G.....Z$ZUi..*EQQ...RE.Hi.D".0BTj..J.x..O.J..C.)..IU.R......HB.8..&..\.x..w..{fx...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x40f09f00 html.{. padding   : 0px 0px 0px 0px;. margin   : 0px 0px 0px 0px;. height   : 100%;. width    : 100%;. background  : #ffffff;.}.body.{. padding   : 0px 0px 0px 0px;. margin   : 0px 0px...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x40f11f18 .=Xt.....H...hE:t.;.......=.s....f.(.....v.5'<.8}=.=kXF..&&...K...j.........<...A..........}.......c.c..7.e{b.....O.p..h....e.1....8.zd{..........}3.Z.W..v.|}y.u...3M.....h2IDAT...........Z.;u..M.....'!.(.S.....|j.]..h.l7.... .....I...u.&J5.";9.d04.S.........|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41009af0 //<![CDATA[.var pageParams = new Object();.pageParams['readMode'] = 1;...function applyReadMode().{. var audioNodes = document.getElementsByClassName('BGAudio');. for (var i=0; i<audioNodes.length; i++). {. .if (pageParams['readMode'] == 0). ...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41011b08 html.{. padding   : 0px 0px 0px 0px;. margin   : 0px 0px 0px 0px;. height   : 100%;. width    : 100%;. background  : #ffffff;.}.body.{. padding   : 0px 0px 0px 0px;. margin   : 0px 0px...|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41019b20 ...t.%#2#b......0..0.6.....A.M....%.F,.*...(.>.q$_.0... a..sF...."Ypn"....#[email protected]).F....4.....Q.1.Wd..3.|.Y%........:.w.F~ ]..0i>a......4n.E7.O..+.7S...D...|.IDAT..'...<.....E.n...!.1.....Tx211..E....4 .*f....>..)..)...gS.j.. WX....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41021b38 .PNG........IHDR...|.................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....PLTE.................................................................................................................................................|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41029b50 .PNG........IHDR.......Q.......0.....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....bKGD..............pHYs...#...#.x.?v..:.IDATx...gx...........-.....4...p...S....%$!$.$$....z....8T.:[email protected]|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41031b68 .PNG........IHDR...V.........0..X....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....bKGD..............pHYs...#...#.x.?v....IDATh..Zyp.U....f.;3....j0..p...E]...M...*%..,.J..Pb..,....Z.AE.E.....n.."[email protected]>...........W......|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410889d0 .PNG........IHDR.............a.~e....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....PLTE...Q_HRgL......=I93?/f~Zg~[[email protected])0$'2&(3&/<('1$......""!:;:"#"......)3&...............&&&'''.......... ...........$.677888.$....................|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410909e8 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...wPLTE........~..............v...........~.........{yn........................................................F.. ..4..............!...........+..l....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x41098a00 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...wPLTE...uuj....................i.......................................................................S..'..$..............*..............[..........|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410a0a18 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...tPLTE...llh...........................|.u.....................................................]..*..+..............-..............T..............M....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410a8a30 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...wPLTE...ooi.......................}....................................................................[..)..%..............+..............Y..........|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410b0a48 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...zPLTE...wvk.......................{....................................................................\..(..$..............,..............\..........|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410b8a60 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...zPLTE...rqg.....d...................................z........................................................R..%.. ..............(..............]....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410c0a78 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...wPLTE...rrh........r............................................................................................Q..%.. ..............(................|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410c8a90 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...wPLTE........}..............O...........|.........xwl........................................................G..!.....8.............."...........'....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410d0aa8 .PNG........IHDR.....................gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...zPLTE........~..............m...........~.........{yn........................................................F.. ..4..............!...........+..l....|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410d8ac0 .PNG........IHDR.............e.. ....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...yPLTE...mmmhfZ........h.................)........c..kppp..h..u.......................................................................x..o..a..........|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410e0ad8 .PNG........IHDR.............e.. ....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<....PLTE...hhh.......................-...........e..~........|.............................................................................s.............|  32,784 |  32,784 |  0.16% 
|- byte[32768] @ 0x410e8af0 .PNG........IHDR.............e.. ....gAMA......a.....sRGB........ cHRM..z&..............u0...`..:....p..Q<...|PLTE.........,,+........c..~...........]..............y....................n.........................................................................|  32,784 |  32,784 |  0.16% 
'- Total: 25 of 471 entries; 446 more                                                               |    |    |   
android.widget.HorizontalScrollView @ 0x40e1d8b8                                                            |   576 |  978,456 |  4.90% 
class android.content.res.Resources @ 0x40ab1570 System Class                                                         |   48 |  266,432 |  1.34% 
... 

日誌摘要

... 
10-08 22:10:28.970: D/MediaPlayer(9823): pause() out 
10-08 22:10:29.090: E/dalvikvm(9823): JNI ERROR (app bug): local reference table overflow (max=512) 
10-08 22:10:29.090: W/dalvikvm(9823): JNI local reference table (0x2498270) dump: 
10-08 22:10:29.090: W/dalvikvm(9823): Last 10 entries (of 512): 
10-08 22:10:29.090: W/dalvikvm(9823):  511: 0x40d696d0 android.content.res.AssetManager 
10-08 22:10:29.090: W/dalvikvm(9823):  510: 0x4201eab0 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  509: 0x42016a98 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  508: 0x4200ea80 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  507: 0x42006a68 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  506: 0x41ffea50 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  505: 0x41ff6a38 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  504: 0x41feea20 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  503: 0x41fe6a08 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823):  502: 0x41fde9f0 byte[] (32768 elements) 
10-08 22:10:29.090: W/dalvikvm(9823): Summary: 
10-08 22:10:29.090: W/dalvikvm(9823):   1 of java.lang.Class 
10-08 22:10:29.090: W/dalvikvm(9823):  510 of byte[] (32768 elements) (510 unique instances) 
10-08 22:10:29.090: W/dalvikvm(9823):   1 of android.content.res.AssetManager 
10-08 22:10:29.090: E/dalvikvm(9823): Failed adding to JNI local ref table (has 512 entries) 
10-08 22:10:29.090: I/dalvikvm(9823): "Thread-39898" prio=5 tid=12 RUNNABLE 
10-08 22:10:29.090: I/dalvikvm(9823): | group="main" sCount=0 dsCount=0 obj=0x40d6b9d8 self=0x222a030 
10-08 22:10:29.090: I/dalvikvm(9823): | sysTid=9871 nice=0 sched=0/0 cgrp=default handle=29456640 
10-08 22:10:29.090: I/dalvikvm(9823): | schedstat=(520906000 142824000 757) utm=22 stm=30 core=2 
10-08 22:10:29.090: I/dalvikvm(9823): at android.content.res.AssetManager.readAsset(Native Method) 
10-08 22:10:29.090: I/dalvikvm(9823): at android.content.res.AssetManager.access$700(AssetManager.java:35) 
10-08 22:10:29.090: I/dalvikvm(9823): at android.content.res.AssetManager$AssetInputStream.read(AssetManager.java:648) 
10-08 22:10:29.090: I/dalvikvm(9823): at dalvik.system.NativeStart.run(Native Method) 
10-08 22:10:29.090: E/dalvikvm(9823): VM aborting 
10-08 22:10:29.090: A/libc(9823): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1) 
10-08 22:10:29.140: I/MediaPlayer(9823): setLPAflag() in 
... 

通過查看MAT條目,看來主線程持有對所有HTML頁面,CSS和Javascript文件,圖像和音頻文件的引用,並且不會釋放它們。最後,當調用新URL時,AssetManager嘗試寫入超出JNI本地引用表的邊界(512個條目)的條目,耗盡可用空間並導致內存泄漏。

我試過各種各樣的事情,以防止保留引用,沒有運氣。這包括:

  • 初始化的WebView,這樣它不緩存的網頁:

    mWebView.getSettings().setAppCacheEnabled(false); 
    mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); 
    mWebView.setDrawingCacheEnabled(false); 
    
  • 試圖加載下一個頁面之前刪除所有現有的WebView數據:

    mWebView.stopLoading(); 
    mWebView.clearCache(true); 
    mWebView.destroyDrawingCache(); 
    mWebView.clearHistory(); 
    mWebView.freeMemory(); 
    mWebView.getInstance().deleteAllData(); 
    
  • 動態創建WebView而不是通過res/layout XML,併爲其分配應用程序上下文而不是活動上下文:

    mWebView = new WebView(getApplicationContext()); 
    
  • 破壞當前的WebView改變頁面和動態創建下一個頁面一個新的人之前,但是這比減慢應用程序的其他沒有影響; JNI的本地表仍保留舊的引用:

    mWebViewContainer.removeViewAt(0); 
    mWebView.destroyDrawingCache(); 
    mWebView.destroy(); 
    mWebView = null; 
    System.gc(); 
    

理想的事情將是消除在JNI表中的舊條目加載下一個頁面之前,但我發現沒有辦法的事那 - 一旦URL被調用,資產目錄引用就會被保留,儘管我已經嘗試了。也許我錯過了一些東西,或者在稍後的Android版本中有待解決?否則,即使爲每個頁面銷燬和創建新的WebView都會對性能產生負面影響,並且會使設計複雜化,如果頁面可以在自己的Thread中創建並保存自己的資產引用而不是主線程,那可能會起作用只要這些線程可以在頁面更改期間停止)。

更新

我想在單獨的線程創建我的網頁視圖,並得到了錯誤「所有的WebView方法必須在UI線程中調用。WebView中的未來版本可能不支持在其他線程使用。「。我假設'UI線程'指的是主線程。此公告似乎支持:

A WebView in a thread can't be created

+0

@VicTom我也中遇到的工作 'JNI ERROR(APP錯誤):本地參照表溢出(最大值= 512)' 的錯誤。 –

回答

2

我想出了避免內存泄漏的方法,但在加倍存儲的數據量的成本。基本上,資產目錄的所有內容都必須複製到內部或外部存儲,而HTML頁面必須從這些存儲目錄加載到WebView中。

內部存儲

  • 目標目錄:mContext.getFilesDir();
  • 例如。 /data/data/com.package。名/文件
  • HTML/CSS/Javascript代碼是從用戶隱藏
  • 如果應用被卸載
  • 媒體文件必須放在根目錄下,而不是在子目錄如果<音頻>或<視頻
  • 代碼將被刪除>標籤要與MediaPlayer正常工作;如果放置在子目錄中,則只能通過使用URL重定向的JavaScript來觸發文件[例如。 location.href = audioNodes.src; ]而不是播放[audioNodes.play(); ]並將該URL捕獲到WebViewClient.shouldOverrideUrlLoading中並將其分配給MediaPlayer變量(子目錄看起來不像傳統方式)。

外部存儲

  • 目標目錄:新文件(mContext.getExternalFilesDir(STORAGE_SERVICE).getAbsolutePath());
  • 例如。 /mnt/sdcard/Android/data/com.package.name/files/storage
  • HTML/CSS/Javascript代碼可以被用戶看到,透露出你的代碼
  • 的內部運作需要SD卡是存在(如果手機使用外接卡),並沒有寫保護
  • 如果應用被卸載
  • 需要的子目錄中的媒體文件無需特殊處理,將MediaPlayer的直接合作
  • 代碼將被刪除(常規行爲)

下面是一個示例方法fo r將指定的目錄/子目錄的內容從資產文件夾傳輸到存儲器(在本例中爲內部)。如果遇到子目錄,將遞歸地調用以深入研究。種子呼叫必須通過「」作爲sourceDirName。

注意:正如所寫的,只能用於1的子目錄深度,並且通過包含「。」來區分文件和目錄。名稱和擴展名之間的字符 - 如果這個名稱不適用於您,您可能需要使用其他測試。如果在資產成員上使用新文件(sourceFileNames [i])。isDirectory()),則不可能使用這樣的測試,因爲它們不是真實文件;來自AssetManager文檔:'文件...已作爲簡單的字節流與應用程序捆綁在一起。

private void copyAssetsToStorage(String sourceDirName) 
{ 
    // /data/data/com.package.name/files 
    try 
    { 
     String[] sourceFileNames = mContext.getAssets().list(sourceDirName); 
     File  targetDir  = mContext.getFilesDir(); 

     if (sourceDirName != "") 
     { 
      sourceDirName += "/"; 
      targetDir  = new File(targetDir, sourceDirName); 
      targetDir.mkdir(); 
     } 

     targetDir.setReadable(true, false); 
     if (sourceFileNames != null) 
     { 
      byte[]   buffer; 
      int    length; 
      InputStream  inStream; 
      File    outFile; 
      FileOutputStream outStream; 
      for (int i = 0; i < sourceFileNames.length; i++) 
      { 
       if (sourceFileNames[i].contains(".") == false) 
       { 
        // Recursive call to drill down 
        copyAssetsToStorage(sourceFileNames[i]); 
       } 
       else 
       { 
        inStream = mContext.getAssets().open(
           sourceDirName + sourceFileNames[i]); 
        outFile = new File(targetDir, sourceFileNames[i]); 
        outStream = new FileOutputStream(outFile, false); 
        buffer = new byte[8192]; 
        while ((length = inStream.read(buffer)) > 0) 
        { 
         outStream.write(buffer, 0, length); 
        } 
        inStream.close(); 
        inStream = null; 
        outStream.flush(); 
        outStream.close(); 
        outStream = null; 
        outFile.setReadable(true, false); 
       } 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     ex.printStackTrace(); 
    } 

} // copyAssetsToStorage 
+0

'正如所寫,只能用於子目錄深度爲1',請你解釋爲什麼只有1級? – iTake

+0

檢查了這個複製所有子文件夾:http://stackoverflow.com/a/7486288/898776 – iTake

+0

iTake,這個例子只有1級的原因是我設置根目錄(「targetDir」)爲「 mContext.getFilesDir();」每個遞歸都適用於我的特定情況。如果您想深入瞭解,則必須將當前根目錄作爲附加參數傳遞。 – VicTorn

2

只是試試這個

WebSettings settings = webvew.getSettings(); 
settings.setAppCacheEnabled(false); 
settings.setCacheMode(WebSettings.LOAD_NO_CACHE); 
settings.setDatabaseEnabled(false); 
settings.setDomStorageEnabled(false); 
settings.setGeolocationEnabled(false); 
settings.setPluginsEnabled(false); 
settings.setSaveFormData(false); 
settings.setSavePassword(false); 

加載之前的HTML網頁流量和我用來代替使用loadURL loadDataWithBaseURL。

只有在webview中加載web服務器時才能使用loadUrl,否則會導致內存泄漏。

它對我

+0

不適合我 –