2014-03-26 106 views
1

我已經閱讀了關於StackOverflow的幾十個問題,但無法獲得任何可靠的解決方案。WebView - 窗口泄漏

我有一個活動,我正在顯示來自服務器的視頻。這些視頻是HTML5,因此,我使用的是HTML5WebView(僅支持播放HTML5視頻的一些方法,實際上是從互聯網上的某個庫中獲取的)。

在4.4.2的Nexus5上播放的視頻很好,但在2.3.6和2.3.5上播放時,會出現內存泄漏,應用程序崩潰。我不知道如何解決這個問題,所以如果有人能夠幫助,那會很好。

我也讀過這可能是一個錯誤,在較低的版本,但如果有一個解決方法,請做建議。

這裏是我的代碼...

custom_screen.xml

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" > 

    <FrameLayout 
     android:id="@+id/fullscreen_custom_content" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:visibility="gone" /> 

    <LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:orientation="vertical" > 

     <FrameLayout 
      android:id="@+id/main_content" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" > 
     </FrameLayout> 
    </LinearLayout> 

</FrameLayout> 

WebViewActivity.java

public class WebViewActivity extends Activity { 

    HTML5WebView mWebView; 
    public String url = "http://m.oxblue.com"; 
    public String fileName = "OxBlue-Photo.png"; 

    ProgressDialog pd; 

    @SuppressLint("NewApi") 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** WebViewActivity - onCreate *****"); 

     mWebView = new HTML5WebView(this); 

     if (savedInstanceState != null) { 
      mWebView.restoreState(savedInstanceState); 
     } else { 

      if (Const.DEBUGGING) 
       Log.d(Const.DEBUG, "GLobal URL = " + url); 

      mWebView.setWebViewClient(new WebViewClient() { 

       @Override 
       public boolean shouldOverrideUrlLoading(WebView view, String url) { 

        if (Const.DEBUGGING) 
         Log.d(Const.DEBUG, "URL = " + url); 

        view.loadUrl(url); 

        return true; 
       } 

       @Override 
       public void onPageStarted(WebView view, String url, 
         Bitmap favicon) { 
        super.onPageStarted(view, url, favicon); 

        pd = new ProgressDialog(WebViewActivity.this); 
        pd.setMessage("Loading..."); 
        pd.show(); 
       } 

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

      }); 
      mWebView.setDownloadListener(new DownloadListener() { 

       @Override 
       public void onDownloadStart(String url, String userAgent, 
         String contentDisposition, String mimetype, 
         long contentLength) { 

        Log.d(Const.DEBUG, "Dowloading..."); 
        Log.d(Const.DEBUG, "URL = " + url); 

        DownloadFile downloadFile = new DownloadFile(
          WebViewActivity.this); 
        downloadFile.execute(url); 

       } 
      }); 

      mWebView.loadUrl(url); 
     } 

     setContentView(mWebView.getLayout()); 

    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     mWebView.saveState(outState); 
    } 

    @Override 
    public void onConfigurationChanged(Configuration newConfig) { 
     super.onConfigurationChanged(newConfig); 
    } 

    @Override 
    public void onStop() { 
     super.onStop(); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** WebViewActivity - onStop *****"); 

     if (mWebView != null) 
      mWebView.stopLoading(); 
     this.finish(); 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** WebViewActivity - onDestroy *****"); 

    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** WebViewActivity - onPause *****"); 
    } 

    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** WebViewActivity - onKeyDown *****"); 

     if (keyCode == KeyEvent.KEYCODE_BACK) { 

      this.finish(); 

      if (mWebView.inCustomView()) { 
       mWebView.hideCustomView(); 
       return true; 
      } 
     } 
     return super.onKeyDown(keyCode, event); 
    } 

    public class DownloadFile extends AsyncTask<String, Void, Boolean> { 
    //AsyncTask to download file... not related to the problem.. so removed 
    } 

} 

HTML5WebView.java

public class HTML5WebView extends WebView { 

    Activity a; 

    private MyWebChromeClient mWebChromeClient; 
    private View mCustomView; 
    private FrameLayout mCustomViewContainer; 
    private WebChromeClient.CustomViewCallback mCustomViewCallback; 

    private FrameLayout mContentView; 
    private FrameLayout mBrowserFrameLayout; 
    private FrameLayout mLayout; 

    static final String LOGTAG = "HTML5WebView"; 

    @SuppressLint("SetJavaScriptEnabled") 
    private void init(Context context) { 
     Context mContext; 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - init *****"); 

     mContext = context; 
     a = (Activity) mContext; 

     mLayout = new FrameLayout(context); 

     mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(a).inflate(
       R.layout.custom_screen, null); 
     mContentView = (FrameLayout) mBrowserFrameLayout 
       .findViewById(R.id.main_content); 
     mCustomViewContainer = (FrameLayout) mBrowserFrameLayout 
       .findViewById(R.id.fullscreen_custom_content); 

     mLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS); 

     WebSettings s = getSettings(); 
     s.setBuiltInZoomControls(true); 
     s.setSupportZoom(true); 
     s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); 
     s.setUseWideViewPort(true); 
     s.setLoadWithOverviewMode(true); 
     s.setUseWideViewPort(true); 
     s.setSaveFormData(true); 
     s.setJavaScriptEnabled(true); 

     mWebChromeClient = new MyWebChromeClient(); 
     setWebChromeClient(mWebChromeClient); 
     setWebViewClient(new WebViewClient()); 
     setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); 
     s.setDomStorageEnabled(true); 

     mContentView.addView(this); 
    } 

    public HTML5WebView(Context context) { 
     super(context); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, 
        "***** HTML5WebView - Single Parameter Constructor *****"); 

     init(context); 
    } 

    public HTML5WebView(Context context, AttributeSet attrs) { 
     super(context, attrs); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, 
        "***** HTML5WebView - 2 Parameter Constructor *****"); 

     init(context); 
    } 

    public HTML5WebView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, 
        "***** HTML5WebView - 3 Parameter Constructor *****"); 

     init(context); 
    } 

    private boolean is_gone = false; 

    @Override 
    protected void onFocusChanged(boolean focused, int direction, 
      Rect previouslyFocusedRect) { 
     super.onFocusChanged(focused, direction, previouslyFocusedRect); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - onFocusChanged *****"); 

     if (!focused) { 
      try { 
       WebView.class.getMethod("onPause").invoke(this); 
      } catch (Exception e) { 
      } 
      this.pauseTimers(); 
      this.is_gone = true; 
     } else { 
      try { 
       WebView.class.getMethod("onResume").invoke(this); 
      } catch (Exception e) { 
      } 
      this.resumeTimers(); 
      this.is_gone = false; 
     } 

    } 

    public void onWindowVisibilityChanged(int visibility) { 
     super.onWindowVisibilityChanged(visibility); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - onWindowVisibility *****"); 

     if (visibility == View.GONE) { 
      try { 
       WebView.class.getMethod("onPause").invoke(this); 
      } catch (Exception e) { 
      } 
      this.pauseTimers(); 
      this.is_gone = true; 
     } else if (visibility == View.VISIBLE) { 
      try { 
       WebView.class.getMethod("onResume").invoke(this); 
      } catch (Exception e) { 
      } 
      this.resumeTimers(); 
      this.is_gone = false; 
     } 
    } 

    public void onDetachedFromWindow() { 
     super.onDetachedFromWindow(); 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, 
        "***** HTML5WebView - onDetachedFromWindow *****"); 

     mContentView.removeAllViews(); 
     mBrowserFrameLayout.removeAllViews(); 
     mLayout.removeAllViews(); 

     if (this.is_gone) { 
      try { 
       this.destroy(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, 
        "***** HTML5WebView - onDetachedFromWindow - Ending *****"); 

    } 

    public FrameLayout getLayout() { 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - getLayout Method *****"); 

     return mLayout; 
    } 

    public boolean inCustomView() { 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - inCustomView Method *****"); 

     return (mCustomView != null); 
    } 

    public void hideCustomView() { 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - hideCustomView *****"); 

     mWebChromeClient.onHideCustomView(); 
    } 

    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 

     if (Const.DEBUGGING) 
      Log.d(Const.DEBUG, "***** HTML5WebView - onKeyDown *****"); 

     if (keyCode == KeyEvent.KEYCODE_BACK) { 
      if ((mCustomView == null) && canGoBack()) { 
       goBack(); 
       return true; 
      } 
     } 
     return super.onKeyDown(keyCode, event); 
    } 

    private class MyWebChromeClient extends WebChromeClient { 

     private View mVideoProgressView; 

     @Override 
     public void onShowCustomView(View view, 
       WebChromeClient.CustomViewCallback callback) { 

      if (Const.DEBUGGING) 
       Log.d(Const.DEBUG, 
         "***** HTML5WebView - MyWebChromeClient - onShowCustomView *****"); 

      HTML5WebView.this.setVisibility(View.GONE); 

      if (mCustomView != null) { 
       callback.onCustomViewHidden(); 
       return; 
      } 

      mCustomViewContainer.addView(view); 
      mCustomView = view; 
      mCustomViewCallback = callback; 
      mCustomViewContainer.setVisibility(View.VISIBLE); 
     } 

     @Override 
     public void onHideCustomView() { 

      if (Const.DEBUGGING) 
       Log.d(Const.DEBUG, 
         "***** HTML5WebView - MyWebChromeClient - onHideCustomView *****"); 

      if (mCustomView == null) 
       return; 

      mCustomView.setVisibility(View.GONE); 
      mCustomViewContainer.removeView(mCustomView); 
      mCustomView = null; 
      mCustomViewContainer.setVisibility(View.GONE); 

      if (mCustomViewCallback != null) 
       mCustomViewCallback.onCustomViewHidden(); 

      mBrowserFrameLayout.setVisibility(View.GONE); 
      HTML5WebView.this.setVisibility(View.VISIBLE); 

      a.finish(); 
     } 

     @Override 
     public View getVideoLoadingProgressView() { 

      if (Const.DEBUGGING) 
       Log.d(Const.DEBUG, 
         "***** HTML5WebView - MyWebChromeClient - getVideoLoadingProgressView *****"); 

      if (mVideoProgressView == null) { 
       LayoutInflater inflater = LayoutInflater.from(a); 
       mVideoProgressView = inflater.inflate(
         R.layout.video_loading_progress, null); 
      } 

      return mVideoProgressView; 
     } 

     @Override 
     public void onGeolocationPermissionsShowPrompt(String origin, 
       GeolocationPermissions.Callback callback) { 

      if (Const.DEBUGGING) 
       Log.d(Const.DEBUG, 
         "***** HTML5WebView - MyWebChromeClient - onGeolocationPermissionsShowPrompt *****"); 

      callback.invoke(origin, true, false); 
     } 
    } 

    static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(
      ViewGroup.LayoutParams.MATCH_PARENT, 
      ViewGroup.LayoutParams.MATCH_PARENT); 
} 

logcat的痕跡......

03-26 13:28:14.862: D/OxBlue(16557): ***** WebViewActivity - onPause ***** 
03-26 13:28:15.073: D/OxBlue(16557): ***** HTML5WebView - onWindowVisibility ***** 
03-26 13:28:15.133: D/OxBlue(16557): ***** WebViewActivity - onStop ***** 
03-26 13:28:15.133: D/webkit-timers(16557): [JWebCoreJavaBridge::pause] >> do pause 
03-26 13:28:15.143: D/OxBlue(16557): ***** WebViewActivity - onDestroy ***** 
03-26 13:28:15.163: D/OxBlue(16557): ***** HTML5WebView - onDetachedFromWindow ***** 
03-26 13:28:15.163: D/OxBlue(16557): ***** HTML5WebView - onFocusChanged ***** 
03-26 13:28:15.163: D/OxBlue(16557): ***** HTML5WebView - onDetachedFromWindow ***** 
03-26 13:28:15.163: D/webviewglue(16557): nativeDestroy view: 0x2f0b68 
03-26 13:28:15.163: D/OxBlue(16557): ***** HTML5WebView - onDetachedFromWindow - Ending ***** 
03-26 13:28:15.163: D/OxBlue(16557): ***** HTML5WebView - onDetachedFromWindow - Ending ***** 
03-26 13:28:15.183: E/WindowManager(16557): Activity com.xx.xxx.webview.WebViewActivity has leaked window [email protected] that was originally added here 
03-26 13:28:15.183: E/WindowManager(16557): android.view.WindowLeaked: Activity com.xx.xxx.webview.WebViewActivity has leaked window [email protected] that was originally added here 
03-26 13:28:15.183: E/WindowManager(16557):  at android.view.ViewRoot.<init>(ViewRoot.java:277) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.view.Window$LocalWindowManager.addView(Window.java:433) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.app.Dialog.show(Dialog.java:265) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.app.AlertDialog$Builder.show(AlertDialog.java:802) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.widget.VideoView$4.onError(VideoView.java:386) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:1476) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.os.Handler.dispatchMessage(Handler.java:99) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.os.Looper.loop(Looper.java:150) 
03-26 13:28:15.183: E/WindowManager(16557):  at android.app.ActivityThread.main(ActivityThread.java:4277) 
03-26 13:28:15.183: E/WindowManager(16557):  at java.lang.reflect.Method.invokeNative(Native Method) 
03-26 13:28:15.183: E/WindowManager(16557):  at java.lang.reflect.Method.invoke(Method.java:507) 
03-26 13:28:15.183: E/WindowManager(16557):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
03-26 13:28:15.183: E/WindowManager(16557):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
03-26 13:28:15.183: E/WindowManager(16557):  at dalvik.system.NativeStart.main(Native Method) 
03-26 13:28:15.373: W/dalvikvm(16557): JNI: DeleteGlobalRef(0xde5deb2f) failed to find entry (valid=0) 
03-26 13:28:15.373: W/dalvikvm(16557): JNI: DeleteGlobalRef(0xde5deb0f) failed to find entry (valid=0) 

我試過removeAllViews()onDetachedFromWindow()裏面的HTML5WebView,但沒有任何改變。

+0

這不是一個存儲器leak.It的像在錯誤 「android.view.WindowLeaked」 指定的窗口泄漏。 –

+0

好的..會改變問題標題..謝謝 –

回答

0

它看起來像是一個漏水AlertDialog,它試圖表明,由於遇到錯誤加載視頻

at android.app.AlertDialog$Builder.show(AlertDialog.java:802) 
at android.widget.VideoView$4.onError(VideoView.java:386) 

你可以訪問你的代碼中VideoView?如果是這樣,您可以嘗試使用setOnErrorListener [1]在出錯時運行空回調。我認爲這是試圖彈出對話框的默認行爲。

[1] http://developer.android.com/reference/android/widget/VideoView.html#setOnErrorListener(android.media.MediaPlayer.OnErrorListener)

+0

如果我沒有從代碼訪問VideoView會怎麼樣?有什麼方法可以獲得訪問權限嗎?我真的不確定,如何獲得訪問權限。 –

+0

您是否看到應用中存在視頻錯誤的提醒對話框? – ksasq

+0

不,我在應用中看不到任何提醒對話框。 –