0

I'm writing an Android App which is opening a locally stored html file check.html. This html file is showing some nice text and then http-forwarding to my real wanted webpage. Now it can happen, that the server is down, or even the network is unplugged, but I want the app to keep trying to reach it all the time.

In case that just the server is down, Android has some latency when asking for the webpage before returning to onReceivedError(), so I don't have a problem, but if the network is unplugged, it returns immediately.

After 1 to 2 minutes my app crashes with:

08-22 14:17:47.382: D/dalvikvm(8337): GC_CONCURRENT freed 178K, 3% free 12530K/12807K, paused 0ms+7ms
08-22 14:17:57.572: D/dalvikvm(8337): GC_CONCURRENT freed 235K, 3% free 14253K/14599K, paused 0ms+7ms
08-22 14:18:07.882: D/dalvikvm(8337): GC_CONCURRENT freed 241K, 3% free 15969K/16327K, paused 0ms+10ms
08-22 14:18:17.717: D/dalvikvm(8337): GC_CONCURRENT freed 237K, 2% free 17717K/18055K, paused 0ms+13ms
08-22 14:18:28.757: D/dalvikvm(8337): GC_CONCURRENT freed 249K, 2% free 19468K/19783K, paused 3ms+3ms
08-22 14:18:39.105: D/dalvikvm(8337): GC_CONCURRENT freed 241K, 2% free 21185K/21511K, paused 4ms+10ms
08-22 14:18:49.008: D/dalvikvm(8337): GC_CONCURRENT freed 238K, 2% free 22900K/23239K, paused 0ms+10ms
08-22 14:18:51.157: E/dalvikvm(8337): JNI ERROR (app bug): local reference table overflow (max=512)
08-22 14:18:51.157: W/dalvikvm(8337): JNI local reference table (0x1729e78) dump:
08-22 14:18:51.157: W/dalvikvm(8337): Last 10 entries (of 512):
08-22 14:18:51.157: W/dalvikvm(8337): 511: 0x410dec88 android.content.res.AssetManager
08-22 14:18:51.157: W/dalvikvm(8337): 510: 0x4215db30 byte[] (32768 elements)
08-22 14:18:51.157: W/dalvikvm(8337): 509: 0x42155b18 byte[] (32768 elements)
08-22 14:18:51.158: E/dalvikvm(8337): Failed adding to JNI local ref table (has 512 entries)

Here is the code:

mWebView = (WebView) findViewById(R.id.webView);
mWebView.setWebChromeClient(new KB_WebChromeClient(this));
mWebView.setWebViewClient(new KB_WebViewClient());
mWebView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mWebView.clearCache(true);
mWebView.clearHistory();
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.addJavascriptInterface(new WebAppInterface(this, this), "Android");

mWebView.loadUrl("file:///android_asset/check.html");

The WebViewClient has the following code:

public class KB_WebViewClient extends WebViewClient {

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        view.clearCache(true);
    }

    @Override
    public void onReceivedError (WebView view, int errorCode, String description, String failingUrl)
    {
        view.loadUrl("file:///android_asset/check.html");
    }
}

Basically, the check.html page is loaded, onPageFinished is called and then the html forwards to the other page, which is not working, so onReceivedError is called, which will start loading the check.html again aso.

On my research I found SO: Android Expand JNI Local Reference Table, where the answer is

You need to delete local references to classes as well as to objects.

What does it mean and where is my problem? Does webview.loadUrl create anything complex and the Garbage Collector is not fast enough to clean up? I don't create the WebView all the time, so what is messing around?

Thanks for help in advance!

edit: I implemented a counter which counts how often the webview.loadUrl is called. It counts 280 to 290 before the app crashes. I tried to remove the check.html and just load my wanted webpage in the network, then the amount of tries goes up to 570 before crashing. It seams, that every webpage load is counted, even when it's done from the HTML page. I was having a look at the heap of the system and what I saw is that every time when one loop is done, there are 1 to 3 more byte[] arrays with 32kB size allocated and not freed. Even the Garbage Collection doesn't free it. I will try to get rid of the Webview every x tries. Maybe that workaround could work...

edit: Under the link they show that there is a bug:

It seems that there is a leakage in the external/webkit/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp.

if (!m_buffer) {
m_buffer = env->NewByteArray(out->capacity());
m_buffer = (jbyteArray) env->NewGlobalRef(m_buffer);
}

A local reference is created by NewByteArray but it's not deleted.

I think that this could be related to my problem. I'm running Android 4.0.3 and I can't update, as it seems they fixed that problem....well well, I'll try to find a workaround.

Community
  • 1
  • 1
Micky
  • 499
  • 6
  • 12

2 Answers2

1

As far as I understood it right, the WebView of Android has a memory leak when a page is loaded (see the question edit for Link). One could come to the idea, that a workaround would be to destroy the WebView. It would work, but for some reason the WebView can't be destroyed (at least the GC can't clean it up) because it's linked to the Activity. The solution is to destroy the Activity after several tries. I decided to do it after every 150 tries.

WebViewClient:

@Override
public void onReceivedError (WebView view, int errorCode, String description, String failingUrl)
{
    counter++;
    if(150 == counter){
        mainActivity.KillThisThenStartNewOne();
    }
    view.loadUrl("file:///android_asset/check.html");
}

Activity:

public void KillThisThenStartNewOne(){
     Intent intent = new Intent(this.getApplicationContext(), MainActivity.class);
     intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);  
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        this.getApplicationContext().startActivity(intent);
        //for restarting the Activity
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
}

Tested on Android 4.0.3 and works. The restart of the Activity takes about 0.5 to 1 second.

Micky
  • 499
  • 6
  • 12
  • I have the same problem with you. But I don't know how to solve this problem. I just use WebView to load several(many) html files. Not NDK, NOT jni. How to fix this bug? Can you give me some help? – Xianfeng.Cai Sep 06 '13 at 08:22
  • In my app, I just load local html file. not from Internet. I also had the same problem. for my case, onReceivedError never be called. I don't know if your workaround is a good choice. – Xianfeng.Cai Sep 14 '13 at 09:00
  • How I can solve this problem? My webview need to load too many local html files. – Xianfeng.Cai Sep 14 '13 at 09:01
  • The same problem: http://stackoverflow.com/questions/18631899/webview-loadurl-referencetable-overflow-max-512 – Xianfeng.Cai Sep 16 '13 at 23:59
  • At first, please don't spam here in the comments! My solution works for the case if you continously try to open a page which is offline for a time and in the online time you normally don't reload the page. That means, that there is 1 call to that page while being online and many while being offline. That's why I'm landing in the onReceivedError() and can count there. For a better handling I should also count in the online cases, but there's no need. To solve your problem, just count how often you call a page and call KillThisThenStartNewOne every x counts (in my case I do it every 150 times) – Micky Oct 04 '13 at 03:51
0
    public void onReceivedError (WebView view, int errorCode, String description, String failingUrl)
    {
        view.loadUrl("file:///android_asset/check.html");
    }

You are doing this quite fast I guess, in a sort of loop. Create a handler and posting it delayed on the (one) handler would probably solve this. Something like:

private Handler handler = new Handler();

@Override
public void onReceivedError (final WebView view, int errorCode, String description, String failingUrl) {

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
             if (view != null && view.isActivated()) {
                     view.loadUrl("file:///android_asset/check.html");
             }
        }
    }, 500);
}

I think you did 10000's of url loads in those 1-2 minutes, now you will do one check every 0.5 seconds, your app will like that a lot better and won't crash anymore.

Frank
  • 12,010
  • 8
  • 61
  • 78
  • So would you say, that my problem is the speed of trying (it's like 500ms for each try when no network is connected) and the system is too slow to take care of my garbage? I could wait for 10 seconds without issues, but my fear is that my problem is just postponed to let's say 4 hours working with the following crash... – Micky Aug 22 '13 at 09:11
  • Yes, the speed of trying. Put a log statement and check: if you get into the onReceivedError method like a 100 times in a matter of seconds, than yes this must be the problem. With your loop you might be clogging the whole system. – Frank Aug 22 '13 at 09:27
  • I put in a counter which counts the tries and shows it to me. Like i said, each try already takes about 500ms and after a count of 280 it crashes. So I bet that even if i wait for 10 seconds I will get the problem after these 280 +- x tries. – Micky Aug 23 '13 at 04:32
  • Ah, I read your reply too quickly. Okay. Pinging the server (outside your webview) and only updating the webview when the server responded, might be an option. Good luck. – Frank Aug 23 '13 at 07:20
  • @Frank I also have this serious problem. I need someone give me some help. Micky's workaround seems not work for me. The app will still have 'JNI ReferenceTable overflow (max=512) ' – Xianfeng.Cai Sep 16 '13 at 23:58