我的Android应用程序包含多个活动,每个活动负责一个片段(暂时)。我的片段通常会像这样显示/附加:
mTopicFragment = (TopicFragment)getSupportFragmentManager().findFragmentByTag("topic");
if(mTopicFragment == null)
mTopicFragment = TopicFragment.newInstance(bid, page, pid);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.content, mTopicFragment, "topic")
.commit();
}
TopicFragment
包含显示一些HTML和CSS / JS内容的WebView
。浏览应用程序一段时间后,其中一个TopicFragment
WebView中的滚动变慢,最终,应用程序完全冻结。 ADB日志显示以下异常:
12-12 22:49:33.931 12582-12582/com.mde.potdroid3 W/Adreno-EGLSUB﹕ <DequeueBuffer:606>: dequeue native buffer fail: Unknown error 2147483646, buffer=0x0, handle=0x0
12-12 22:49:33.941 12582-12582/com.mde.potdroid3 W/Adreno-EGLSUB﹕ <DequeueBuffer:606>: dequeue native buffer fail: Invalid argument, buffer=0x0, handle=0x0
12-12 22:49:33.941 12582-12582/com.mde.potdroid3 W/Adreno-ES20﹕ <gl2_surface_swap:43>: GL_OUT_OF_MEMORY
12-12 22:49:33.941 12582-12582/com.mde.potdroid3 W/Adreno-EGL﹕ <qeglDrvAPI_eglSwapBuffers:3597>: EGL_BAD_SURFACE
12-12 22:49:33.941 12582-12582/com.mde.potdroid3 W/HardwareRenderer﹕ EGL error: EGL_BAD_SURFACE
12-12 22:49:33.951 12582-12582/com.mde.potdroid3 W/HardwareRenderer﹕ Mountain View, we've had a problem here. Switching back to software rendering.
12-12 22:20:04.461 10081-10081/com.mde.potdroid3 E/Surface﹕ dequeueBuffer failed (Unknown error 2147483646)
12-12 22:20:04.461 10081-10081/com.mde.potdroid3 E/ViewRootImpl﹕ Could not lock surface
java.lang.IllegalArgumentException
at android.view.Surface.nativeLockCanvas(Native Method)
at android.view.Surface.lockCanvas(Surface.java:243)
at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2435)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2409)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2253)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1883)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:544)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5081)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
在互联网上,我只能找到有关此例外的信息,其中人们拥有自定义视图。这里发生了什么?它可以与我的应用程序的内存消耗有关吗?看起来好像每次调用上面的代码时,实例化一个新的TopicFragment
,显示并推送到后面的堆栈。我怎么能进一步调试这种行为?
还有一个信息:当我在开发人员设置中启用叠加时,应用程序似乎使用了大量的CPU。可能是这样,当我离开它们并且出于某种原因继续在后台运行时,我的碎片没有被正确分离?
这就是我使用WebView的方式:
mWebView = (WebView)getView().findViewById(R.id.topic_webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
mWebView.getSettings().setAllowFileAccess(true);
mWebView.addJavascriptInterface(mJsInterface, "api");
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.loadData("", "text/html", "utf-8");
mWebView.setBackgroundColor(0x00000000);
不提到的内存泄漏here。
答案 0 :(得分:2)
我想我修好了。显然,有一个错误会阻止WebView的正确内存管理。当我在他们的视图中使用WebView
启动许多活动时,WebView存在于内存中,并且当内存不足时活动未被正确杀死。我使用TopicFragment
中的以下代码解决了该问题,该代码显示了WebView
:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
View v = super.onCreateView(inflater, container, saved);
mActivity = (BaseActivity) getSupportActivity();
// this is the framelayout which will contain our WebView
mWebContainer = (FrameLayout) v.findViewById(R.id.web_container);
return v;
}
public void onResume() {
super.onResume();
// create new WebView and set all its options.
mWebView = new WebView(mActivity);
mWebView....
// add it to the container
mWebContainer.addView(mWebView);
// if data is available, display it immediately
if(mTopic != null) {
mWebView.loadDataWithBaseURL("file:///android_asset/", mTopic.getHtmlCache(),
"text/html", "UTF-8", null);
}
}
@Override
public void onPause() {
super.onPause();
// destroy the webview
mWebView.destroy();
mWebView = null;
// remove the view from the container.
mWebContainer.removeAllViews();
}
这样,就会在WebView
和onResume
中创建并删除onPause
。这是一些开销并不完美,但它解决了内存问题,在性能等方面几乎没有明显的意义。
答案 1 :(得分:0)
如果你的应用程序中有许多WebViews在不同的活动/片段中,你可以尝试使用一个池,在那里你可以在Activies onCreate()方法中检索一个WebView实例,并将WebView返回给Activies onDestory中的池( )。
WebViewPool大小可以由用户设备内存类设置。 这可能不是每个使用Web视图的应用程序的最佳解决方案,但它是您可以考虑的解决方案。您可能需要进行一些测试等。
但请记住,池中的对象不是垃圾回收,因此如果您决定使用池,请务必小心。
答案 2 :(得分:0)
我终于找到了这个问题。关闭WebView的硬件加速可以解决它。
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
//...
if (Utils.isKitkat()) {
disableHardwareAcc();
}
//...
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
protected void disableHardwareAcc() {
mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}