2012-10-09 13 views
5

Moja aplikacja Android 4.0.4 składa się z WebView, przez który użytkownik może przeglądać wiele stron przechowywanych lokalnie w katalogu zasobów. Podczas jazdy na rowerze po stronach następujący błąd ostatecznie zostaje wyzwolony i awarii aplikacji:Android WebView wyciek pamięci referencyjnej o wartości zasobów

  • JNI ERROR (app bug): miejscowy przepełnienie tabeli odniesienia (max = 512)
  • Failed dodanie do JNI tabeli miejscowy Ref (ma 512 wpisów)
  • VM przerywanie
  • sygnał krytyczny 11 (SIGSEGV) w 0xdeadd00dd (kod = 1)

problem wydaje się być związane z tych zgłaszanych tutaj:

Użyłem instrukcje wtyczek Memory Analyzer Narzędzie podane w poniższy link, aby zbadać szczegóły:

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

Rezultaty są pokazane poniżej (nie można przesłać ekranu es jeszcze, więc tekst będzie musiał zrobić):

mata Fragment

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% 
... 

Log Fragment

... 
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 
... 

Patrząc na wpisy mat, wydaje się, że główny wątek zawiera odniesienia do wszystkich stron HTML, plików CSS i Javascript, obrazów i plików audio i nie zwalnia ich. W końcu, gdy wywoływany jest nowy adres URL, AssetManager próbuje zapisać wpisy wykraczające poza granice (512 wpisów) lokalnej tabeli ref. JNI, wyczerpuje dostępne miejsce i powoduje wyciek pamięci.

Próbowałem różnych rzeczy, aby zapobiec zachowaniu referencji, bez powodzenia.Obejmuje to:

  • inicjowania WebView tak, że nie buforują strony:

    mWebView.getSettings().setAppCacheEnabled(false); 
    mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); 
    mWebView.setDrawingCacheEnabled(false); 
    
  • próbuje usunąć wszystkie istniejące dane Webview przed załadowaniem Następna strona:

    mWebView.stopLoading(); 
    mWebView.clearCache(true); 
    mWebView.destroyDrawingCache(); 
    mWebView.clearHistory(); 
    mWebView.freeMemory(); 
    mWebView.getInstance().deleteAllData(); 
    
  • tworzenie WebView dynamicznie, a nie poprzez res/layout XML i przypisywanie kontekstu aplikacji zamiast kontekstu działania:

    mWebView = new WebView(getApplicationContext()); 
    
  • niszcząc obecną WebView przed zmianą stron i dynamicznie tworzenia nowej jeden na następnej stronie, ale nie ma to wpływu innego niż spowolnienie aplikacji; lokalnej tabeli JNI zachowuje dawne nazwy:

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

Ideałem byłoby, aby usunąć stare wpisy w tabeli JNI przed załadowaniem następną stronę, ale nie znalazłem sposób to zrobić to - po wywołaniu adresu URL odniesienia do katalogu zasobów są przechowywane pomimo tego, co próbowałem. Może coś przeoczyłem lub trwa korekta w późniejszym wydaniu Androida? Jeśli to się nie uda, nawet niszczenie i tworzenie nowych WebView dla każdej strony negatywnie wpływa na wydajność i komplikuje projektowanie, jeśli strony mogłyby być tworzone we własnym wątku i przechowywać własne odwołania do zasobów zamiast głównego wątku, może to by działało (pod warunkiem, że wątki mogą zostać zatrzymane podczas zmiany strony).

Aktualizacja

Próbowałem tworzenia moje WebViews w oddzielnych wątków i mam błąd "Wszystkie metody Webview musi być wywoływana na wątków UI. Przyszłe wersje WebView może nie obsługiwać wykorzystywania na innych wątkach. ". Zakładam, że "wątek UI" odnosi się do głównego wątku. Ten wpis wydaje się wspierać że:

A WebView in a thread can't be created

+0

@VicTom Ja również spotkałem 'JNI błąd (aplikacja bug): przepełnienie lokalnego tabeli odniesienia (max = 512)' bug. –

Odpowiedz

2

I mają pochodzić z obejścia, aby uniknąć wycieku pamięci, ale kosztem podwojenie ilości przechowywanych danych. Zasadniczo cała zawartość katalogu zasobów musi zostać skopiowana do pamięci wewnętrznej lub zewnętrznej, a strony HTML muszą zostać załadowane do WebView z tych katalogów pamięci masowej.

Internal Storage

  • katalog docelowy: mContext.getFilesDir();
  • np. /data/data/com.package.Nazwa/pliki
  • HTML/CSS/kod Javascript jest ukryta przed użytkownikiem
  • kod
  • zostaną usunięte, jeśli aplikacja jest odinstalowane
  • pliki multimedialne muszą być umieszczone w katalogu głównym, a nie w podkatalogu jeśli < dźwięku > lub <wideo> tagi działają normalnie z MediaPlayer; jeśli jest umieszczony w podkatalogu, pliki mogą być uruchamiane tylko przez JavaScript za pomocą przekierowania adresu URL [np. location.href = audioNodes.src; ] zamiast grać [audioNodes.play(); ] i przechwytywanie tego adresu URL w WebViewClient.shouldOverrideUrlLoading i przypisywanie go do zmiennej MediaPlayer (podkatalogi nie zachowują się w sposób konwencjonalny).

pamięci zewnętrzne

  • katalog docelowy: nowy plik (mContext.getExternalFilesDir (STORAGE_SERVICE) .getAbsolutePath());
  • np. /mnt/sdcard/Android/data/com.package.name/files/storage
  • HTML/CSS/kod JavaScript mogą być postrzegane przez użytkownika, odsłaniając wewnętrzne funkcjonowanie kodzie
  • wymaga karty SD być obecny (jeśli telefon korzysta z zewnętrznej karty) i nie zabezpieczony przed zapisem
  • kod
  • zostaną usunięte, jeśli aplikacja jest odinstalowane
  • bez szczególnego postępowania z poszkodowanym do plików multimedialnych w podkatalogów, działa bezpośrednio z MediaPlayer (zachowuje się konwencjonalnie)

Poniżej znajduje się przykładowa metoda r przenoszenie zawartości określonego katalogu/podkatalogu z folderu zasobów do magazynu (w tym przypadku wewnętrznym). Jeśli napotka podkatalog, zostanie wywołane rekurencyjnie, aby drążyć dane. Wywołanie seed musi minąć "" jako sourceDirName.

UWAGA: Jak napisano, działa to tylko dla głębokości podkatalogu 1, a pliki są odróżniane od katalogów przez umieszczenie "." znak między nazwą a rozszerzeniem - może być konieczne użycie innego testu, jeśli ten nie działa. Nie można użyć testu takiego jak , jeśli (nowy plik (sourceFileNames [i]). IsDirectory()) na składniku aktywów, ponieważ nie są to prawdziwe pliki; z dokumentacji AssetManager: "pliki ... zostały dołączone do aplikacji jako prosty strumień bajtów. '

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

"Jak napisano, działa tylko dla głębokości podkatalogu 1", czy możesz wyjaśnić, dlaczego tylko 1 poziom? – iTake

+0

Sprawdź to, aby skopiować wszystkie podfoldery: http://stackoverflow.com/a/7486288/898776 – iTake

+0

iTake, powód, dla którego przykład obniża się o 1 poziom, polega na tym, że ustawiam katalog główny ("targetDir") na " mContext.getFilesDir(); " dla każdej rekurencji, która działa w mojej konkretnej sytuacji. Jeśli chcesz pogłębić głębiej, musisz przekazać bieżący katalog główny jako dodatkowy argument. – VicTorn

2

tylko spróbować tego

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); 

przed załadunkiem HTML WebView i użyłem loadDataWithBaseURL zamiast loadUrl.

loadUrl może być używany tylko wtedy, gdy ładujesz serwer WWW w widoku webowym, w innym przypadku prowadzi to do wycieku pamięci.

jego pracował dla mnie

+0

nie działa dla mnie –