WebView - 窗口泄漏

时间:2014-03-26 08:18:49

标签: android html5 video android-webview

我已经在StackOverflow上阅读了几十个问题,但无法获得任何可靠的解决方案。

我有一项活动,我正在从服务器显示视频。视频是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 com.android.internal.policy.impl.PhoneWindow$DecorView@405da260 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 com.android.internal.policy.impl.PhoneWindow$DecorView@405da260 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)

我在HTML5WebView中的removeAllViews()中尝试了onDetachedFromWindow(),但没有任何改变。

1 个答案:

答案 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)